ResMed S9 Loader Rework to get at the STR.edf sessions where no other .edf data present

This commit is contained in:
Mark Watkins 2011-12-31 09:02:45 +10:00
parent 1d52bf2e7c
commit 98912a3e9b
24 changed files with 674 additions and 150 deletions

View File

@ -75,7 +75,7 @@ void gFlagsGroup::paint(gGraph &w, int left, int top, int width, int height)
for (int i=0;i<lvisible.size();i++) {
// Alternating box color
if (i & 1) barcol=&col1; else barcol=&col2;
quads->add(left,linetop,left,linetop+m_barh,left+width-1,linetop+m_barh,left+width-1,linetop,*barcol);
quads->add(left, linetop, left, linetop+m_barh, left+width-1, linetop+m_barh, left+width-1, linetop, *barcol);
// Paint the actual flags
lvisible[i]->paint(w,left,linetop,width,m_barh);
@ -84,7 +84,7 @@ void gFlagsGroup::paint(gGraph &w, int left, int top, int width, int height)
GLShortBuffer *outlines=w.lines();
QColor blk=Qt::black;
outlines->add(left-1, top, left-1, top+height,left-1, top+height, left+width,top+height, blk);
outlines->add(left-1, top, left-1, top+height, left-1, top+height, left+width,top+height, blk);
outlines->add(left+width,top+height, left+width, top, left+width, top, left-1, top, blk);
//lines->add(left-1, top, left-1, top+height);
@ -137,7 +137,7 @@ void gFlagsLine::paint(gGraph & w,int left, int top, int width, int height)
float bartop=top+2;
float bottom=top+height-2;
bool verts_exceeded=false;
qint64 X,Y;
qint64 X,X2,L;
m_flag_color=schema::channel[m_code].defaultColor();
for (QVector<Session *>::iterator s=m_day->begin();s!=m_day->end(); s++) {
if (!(*s)->enabled()) continue;
@ -148,17 +148,22 @@ void gFlagsLine::paint(gGraph & w,int left, int top, int width, int height)
for (quint32 i=0;i<el.count();i++) {
X=el.time(i);
Y=X-(el.data(i)*1000);
if (Y < minx) continue;
L=el.data(i)*1000;
X2=X-L;
if (X2 < minx) continue;
if (X > maxx) break;
x1=(X - minx) * xmult + left;
if (m_flt==FT_Bar) {
lines->add(x1,bartop,x1,bottom,m_flag_color);
if (lines->full()) { verts_exceeded=true; break; }
} else if (m_flt==FT_Span) {
x2=(Y-minx)*xmult+left;
x2=(X2-minx)*xmult+left;
//w1=x2-x1;
quads->add(x1,bartop,x1,bottom,x2,bottom,x2,bartop,m_flag_color);
/*if (qAbs(x1-x2)<=1) {
x1-=1;
x2+=1;
}*/
quads->add(x2,bartop,x1,bartop, x1,bottom,x2,bottom,m_flag_color);
if (quads->full()) { verts_exceeded=true; break; }
}
}

View File

@ -212,13 +212,13 @@ void GLShortBuffer::add(GLshort x1, GLshort y1, GLshort x2, GLshort y2,GLshort x
GLShortBuffer::GLShortBuffer(int max,int type, bool stippled)
:GLBuffer(max,type,stippled)
{
buffer=new GLshort [max+8];
colors=new GLubyte[max*4+(8*4)];
buffer=(GLshort *)calloc(sizeof(GLshort),max+8);
colors=(GLubyte *)calloc(sizeof(GLubyte),max*4+(8*4));
}
GLShortBuffer::~GLShortBuffer()
{
if (colors) delete [] colors;
if (buffer) delete [] buffer;
if (colors) free(colors);
if (buffer) free(buffer);
}
void GLShortBuffer::add(GLshort x, GLshort y,QColor & color)
@ -307,6 +307,7 @@ void GLShortBuffer::draw()
{
if (m_cnt>0) {
bool antialias=m_forceantialias || (PROFILE.ExistsAndTrue("UseAntiAliasing") && m_antialias);
if (m_stippled) antialias=true;
float size=m_size;
if (antialias) {
glEnable(GL_BLEND);
@ -325,7 +326,6 @@ void GLShortBuffer::draw()
if (m_type==GL_LINES || m_type==GL_LINE_LOOP) {
if (m_stippled) {
glLineStipple(1, 0xcccc);
glEnable(GL_BLEND);
glEnable(GL_LINE_STIPPLE);
} else {
glLineStipple(1, 0xFFFF);
@ -342,6 +342,7 @@ void GLShortBuffer::draw()
glEnable(GL_SCISSOR_TEST);
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_SHORT, 0, buffer);
if (m_colcnt>0) {
@ -350,13 +351,12 @@ void GLShortBuffer::draw()
} else {
glColor4ub(m_color.red(),m_color.green(),m_color.blue(),m_color.alpha());
}
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(m_type, 0, m_cnt >> 1);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
// glDisableClientState(GL_COLOR_ARRAY);
if (m_colcnt>0) {
glDisableClientState(GL_COLOR_ARRAY);
}
glDisableClientState(GL_VERTEX_ARRAY);
//qDebug() << "I Drawed" << m_cnt << "vertices";
m_cnt=0;
@ -368,11 +368,12 @@ void GLShortBuffer::draw()
if (m_type==GL_POLYGON) {
glPolygonMode(GL_BACK,GL_FILL);
}
if (m_type==GL_LINES || m_type==GL_LINE_LOOP) {
if (m_stippled) {
glDisable(GL_BLEND);
glDisable(GL_LINE_STIPPLE);
glLineStipple(1, 0xFFFF);
}
}
if (antialias) {
if (m_type==GL_LINES || m_type==GL_LINE_LOOP) {

View File

@ -92,7 +92,7 @@ void gLineOverlayBar::paint(gGraph & w, int left, int topp, int width, int heigh
if (x2<left) x2=left;
if (x1>width+left) x1=width+left;
//double w1=x2-x1;
quads->add(x1,start_py,x2,start_py,x2,start_py+height,x1,start_py+height,m_flag_color);
quads->add(x2,start_py, x1,start_py, x1,start_py+height, x2,start_py+height,m_flag_color);
if (quads->full()) { verts_exceeded=true; break; }
} else if (m_flt==FT_Dot) {
if ((PROFILE.appearance->overlayType()==ODT_Bars) || (xx<3600000)) {

View File

@ -515,6 +515,10 @@ bool Day::channelHasData(ChannelID id)
r=true;
break;
}
if (sessions[i]->m_valuesummary.contains(id)) {
r=true;
break;
}
}
return r;
}

View File

@ -95,15 +95,15 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
//lookup["Se"]="Se"; //05
//lookup["Si"]="Si"; //05
//lookup["Mi"]="Mi"; //0
//lookup["Uh"]="Uh"; //0000.0
//lookup["Up"]="Up"; //0000.0
lookup["Uh"]="HoursMeter"; //0000.0
lookup["Up"]="ComplianceMeter"; //0000.0
//lookup["Er"]="ErrorCode"; // E00
//lookup["El"]="LastErrorCode"; // E00 00/00/0000
//lookup["Hp"]="Hp"; //1
//lookup["Hs"]="Hs"; //02
//lookup["Lu"]="LowUseThreshold"; // defaults to 0 (4 hours)
lookup["Sf"]="SmartFlex";
//lookup["Sm"]="SmartFlexMode";
lookup["Sm"]="SmartFlexMode";
lookup["Ks=s"]="Ks_s";
lookup["Ks=i"]="Ks_i";
@ -203,8 +203,8 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
sess->AddEventList(CPAP_EPAP,EVL_Event);
sess->AddEventList(CPAP_Pressure,EVL_Event);
sess->AddEventList(CPAP_Te,EVL_Event);
sess->AddEventList(CPAP_Ti,EVL_Event);
sess->AddEventList(INTELLIPAP_Unknown1,EVL_Event);
sess->AddEventList(INTELLIPAP_Unknown2,EVL_Event);
sess->AddEventList(CPAP_LeakTotal,EVL_Event);
sess->AddEventList(CPAP_MaxLeak,EVL_Event);
@ -320,6 +320,15 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
quint64 first=qint64(sid)*1000L;
quint64 last=qint64(SessionEnd[i])*1000L;
sess->settings[CPAP_PresReliefType]=(PRTypes)PR_SMARTFLEX;
sess->settings[CPAP_PresReliefSet]=set1["SmartFlex"].toInt();
int sfm=set1["SmartFlexMode"].toInt();
if (sfm==0) {
sess->settings[CPAP_PresReliefMode]=PM_FullTime;
} else {
sess->settings[CPAP_PresReliefMode]=PM_RampOnly;
}
EventDataType max=sess->Max(CPAP_IPAP);
EventDataType min=sess->Min(CPAP_EPAP);
EventDataType pres=sess->Min(CPAP_Pressure);

View File

@ -18,7 +18,7 @@
//********************************************************************************************
// Please INCREMENT the following value when making changes to this loaders implementation.
//
const int intellipap_data_version=1;
const int intellipap_data_version=2;
//
//********************************************************************************************

View File

@ -380,8 +380,8 @@ int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile)
//sess->summaryCPAP_Mode]!=MODE_ASV)
sess->settings[CPAP_Mode]=MODE_BIPAP;
if (sess->settings[PRS1_FlexMode].toInt()!=PR_NONE) {
sess->settings[PRS1_FlexMode]=PR_BIFLEX;
if (sess->settings[CPAP_PresReliefType].toInt()!=PR_NONE) {
sess->settings[CPAP_PresReliefType]=PR_BIFLEX;
}
sess->setAvg(CPAP_Pressure,(sess->avg(CPAP_EPAP)+sess->avg(CPAP_IPAP))/2.0);
@ -476,10 +476,13 @@ bool PRS1Loader::ParseSummary(Machine *mach, qint32 sequence, quint32 timestamp,
// This is incorrect..
if (data[offset+0x08] & 0x80) { // Flex Setting
if (data[offset+0x08] & 0x08) {
if (max>0) session->settings[PRS1_FlexMode]=(int)PR_AFLEX;
else session->settings[PRS1_FlexMode]=(int)PR_CFLEXPLUS;
} else session->settings[PRS1_FlexMode]=(int)PR_CFLEX;
} else session->settings[PRS1_FlexMode]=(int)PR_NONE;
if (max>0) session->settings[CPAP_PresReliefType]=(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;
session->settings[CPAP_PresReliefMode]=(int)PM_FullTime; // only has one mode
session->settings[PRS1_FlexSet]=(int)(data[offset+0x08] & 3);
session->settings[PRS1_HumidSetting]=(int)data[offset+0x09]&0x0f;

View File

@ -21,7 +21,7 @@ License: GPL
//********************************************************************************************
// Please INCREMENT the following value when making changes to this loaders implementation.
//
const int prs1_data_version=8;
const int prs1_data_version=9;
//
//********************************************************************************************

View File

@ -253,89 +253,255 @@ long event_cnt=0;
int ResmedLoader::Open(QString & path,Profile *profile)
{
QString newpath;
const QString datalog="DATALOG";
const QString idfile="Identification.";
const QString strfile="STR.";
QString dirtag="DATALOG";
if (path.endsWith(QDir::separator()+dirtag)) {
return 0;
//newpath=path;
} else {
newpath=path+QDir::separator()+dirtag;
}
if (!QDir().exists(newpath)) return 0;
const QString ext_TGT="tgt";
const QString ext_CRC="crc";
const QString ext_EDF="edf";
QString idfile=path+QDir::separator()+"Identification.tgt";
QFile f(idfile);
if (!f.exists()) return 0;
QHash<QString,QString> idmap;
if (f.open(QIODevice::ReadOnly)) {
if (!f.isReadable())
return 0;
while (!f.atEnd()) {
QString line=f.readLine().trimmed();
QString serial; // Serial number
QString key,value;
QString line;
QString newpath;
QString filename;
QHash<QString,QString> idmap; // Temporary properties hash
// Strip off end "/" if any
if (path.endsWith("/"))
path=path.section("/",0,-2);
// Strip off DATALOG from path, and set newpath to the path contianing DATALOG
if (path.endsWith(datalog)) {
newpath=path+"/";
path=path.section("/",0,-2);
} else {
newpath=path+"/"+datalog+"/";
}
// Add separator back
path+="/";
// Check DATALOG folder exists and is readable
if (!QDir().exists(newpath))
return 0;
///////////////////////////////////////////////////////////////////////////////////
// Parse Identification.tgt file (containing serial number and machine information)
///////////////////////////////////////////////////////////////////////////////////
filename=path+idfile+ext_TGT;
QFile f(filename);
// Abort if this file is dodgy..
if (!f.exists() || !f.open(QIODevice::ReadOnly))
return 0;
// Parse # entries into idmap.
while (!f.atEnd()) {
line=f.readLine().trimmed();
if (!line.isEmpty()) {
key=line.section(" ",0,0);
value=line.section(" ",1);
key=key.section("#",1);
if (key=="SRN") {
key=STR_PROP_Serial;
serial=value;
}
idmap[key]=value;
}
}
}
QString strfile=path+QDir::separator()+"STR.edf";
EDFParser stredf(strfile);
f.close();
// Abort if no serial number
if (serial.isEmpty()) {
qDebug() << "S9 Data card has no valid serial number in Indentification.tgt";
return 0;
}
// Early check for STR.edf file, so we can early exit before creating faulty machine record.
QString strpath=path+strfile+ext_EDF; // STR.edf file
f.setFileName(strpath);
if (!f.exists()) {
qDebug() << "Missing STR.edf file";
return 0;
}
///////////////////////////////////////////////////////////////////////////////////
// Create machine object (unless it's already registered)
///////////////////////////////////////////////////////////////////////////////////
Machine *m=CreateMachine(serial,profile);
///////////////////////////////////////////////////////////////////////////////////
// Parse the idmap into machine objects properties, (overwriting any old values)
///////////////////////////////////////////////////////////////////////////////////
for (QHash<QString,QString>::iterator i=idmap.begin();i!=idmap.end();i++) {
m->properties[i.key()]=i.value();
if (i.key()=="PCD") { // Lookup Product Code for real model string
bool ok;
int j=i.value().toInt();
if (ok) m->properties[STR_PROP_Model]=RMS9ModelMap[j];
}
}
///////////////////////////////////////////////////////////////////////////////////
// Open and Parse STR.edf file
///////////////////////////////////////////////////////////////////////////////////
EDFParser stredf(strpath);
if (!stredf.Parse()) {
qDebug() << "Faulty file" << strfile;
return 0;
}
if (stredf.serialnumber!=serial) {
qDebug() << "Identification.tgt Serial number doesn't match STR.edf!";
}
// Creating early as we need the object
QDir dir(newpath);
///////////////////////////////////////////////////////////////////////////////////
// Create the backup folder for storing a copy of everything in..
///////////////////////////////////////////////////////////////////////////////////
QString backup_path=PROFILE.Get(m->properties[STR_PROP_Path])+"Backup/";
if (!dir.exists(backup_path)) {
if (!dir.mkpath(backup_path+datalog)) {
qDebug() << "Could not create S9 backup directory :-/";
}
}
// Copy Identification files to backup folder
QFile::copy(path+idfile+ext_TGT,backup_path+idfile+ext_TGT);
QFile::copy(path+idfile+ext_CRC,backup_path+idfile+ext_CRC);
// Copy STR files to backup folder
QFile::copy(strpath,backup_path+strfile+ext_EDF);
QFile::copy(path+strfile+ext_CRC,backup_path+strfile+ext_CRC);
///////////////////////////////////////////////////////////////////////////////////
// Process the actual STR.edf data
///////////////////////////////////////////////////////////////////////////////////
qint64 duration=stredf.GetNumDataRecords()*stredf.GetDuration();
int days=duration/86400000L;
//QDateTime dt1=QDateTime::fromTime_t(stredf.startdate/1000L);
//QDateTime dt2=QDateTime::fromTime_t(stredf.enddate/1000L);
//QDate dd1=dt1.date();
//QDate dd2=dt2.date();
for (int s=0;s<stredf.GetNumSignals();s++) {
//EDFSignal & es=*stredf.edfsignals[s];
long recs=stredf.edfsignals[s]->nr*stredf.GetNumDataRecords();
QDateTime dt1=QDateTime::fromTime_t(stredf.startdate/1000L);
QDateTime dt2=QDateTime::fromTime_t(stredf.enddate/1000L);
QDate dd1=dt1.date();
QDate dd2=dt2.date();
qDebug() << "STREDF:" << stredf.edfsignals[s]->label << recs;
for (int s=0;s<stredf.GetNumSignals();s++) {
EDFSignal & es=*stredf.edfsignals[s];
long recs=es.nr*stredf.GetNumDataRecords();
//qDebug() << "STREDF:" << es.label << recs;
}
// Process STR.edf and find first and last time for each day
QDir dir(newpath);
QVector<qint8> dayused;
dayused.resize(days);
QList<SessionID> strfirst;
QList<SessionID> strlast;
QList<int> strday;
QList<bool> dayfoo;
if ((!dir.exists() || !dir.isReadable()))
return 0;
QHash<qint16,QList<time_t> > daystarttimes;
QHash<qint16,QList<time_t> > dayendtimes;
qint16 on,off;
qint16 o1[10],o2[10];
time_t st,et;
time_t time=stredf.startdate/1000L; // == 12pm on first day
for (int i=0;i<days;i++) {
EDFSignal *maskon=stredf.lookup["Mask On"];
EDFSignal *maskoff=stredf.lookup["Mask Off"];
int j=i*10;
// Counts for on and off don't line up, and I'm not sure why
// The extra 'off' always seems to start with a 1 at the beginning
// A signal it's carried over from the day before perhaps? (noon boundary)
int ckon=0,ckoff=0;
for (int k=0;k<10;k++) {
on=maskon->data[j+k];
off=maskoff->data[j+k];
o1[k]=on;
o2[k]=off;
if (on >= 0) ckon++;
if (off >= 0) ckoff++;
}
// set to true if day starts with machine running
int offset=ckoff-ckon;
dayfoo.push_back(offset>0);
st=0,et=0;
time_t l,f;
// Find the Min & Max times for this day
for (int k=0;k<ckon;k++) {
on=o1[k];
off=o2[k+offset];
f=time+on*60;
l=time+off*60;
daystarttimes[i].push_back(f);
dayendtimes[i].push_back(l);
if (!st || (st > f)) st=f;
if (!et || (et < l)) et=l;
}
strfirst.push_back(st);
strlast.push_back(et);
strday.push_back(i);
dayused[i]=ckon;
time+=86400;
}
// reset time to first day
time=stredf.startdate/1000;
///////////////////////////////////////////////////////////////////////////////////
// Open DATALOG file and build list of session files
///////////////////////////////////////////////////////////////////////////////////
qDebug() << "ResmedLoader::Open newpath=" << newpath;
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Name);
QFileInfoList flist=dir.entryInfoList();
QMap<SessionID,QVector<QString> > sessfiles;
QString ext,rest,datestr;//,s,codestr;
SessionID sessionid;
QDateTime date;
QString filename;
int size=flist.size();
sessfiles.clear();
// For each file in filelist...
for (int i=0;i<size;i++) {
QFileInfo fi=flist.at(i);
filename=fi.fileName();
ext=filename.section(".",1).toLower();
if (ext!="edf") continue;
// Forget about it if it can't be read.
if (!fi.isReadable())
continue;
// Check the file extnsion
ext=filename.section(".",1).toLower();
if (ext!=ext_EDF) continue;
// Split the filename into components
rest=filename.section(".",0,0);
datestr=filename.section("_",0,1);
// Take the filename's date, and convert it to epoch to form the sessionID.
date=QDateTime::fromString(datestr,"yyyyMMdd_HHmmss");
if (!date.isValid())
continue; // Skip file if dates invalid
sessionid=date.toTime_t();
////////////////////////////////////////////////////////////////////////////////////////////
// Resmed bugs up on the session filenames.. 1 or 2 seconds either way
// Moral of the story, when writing firmware and saving in batches, use the same datetimes.
////////////////////////////////////////////////////////////////////////////////////////////
if (sessfiles.find(sessionid)==sessfiles.end()) {
if (sessfiles.find(sessionid+2)!=sessfiles.end()) sessionid+=2;
else if (sessfiles.find(sessionid+1)!=sessfiles.end()) sessionid++;
@ -343,76 +509,265 @@ int ResmedLoader::Open(QString & path,Profile *profile)
else if (sessfiles.find(sessionid-2)!=sessfiles.end()) sessionid-=2;
}
sessfiles[sessionid].push_back(fi.canonicalFilePath());
if (qprogress) qprogress->setValue((float(i+1)/float(size)*33.0));
// Push current filename to sanitized by-session list
sessfiles[sessionid].push_back(rest);
// Update the progress bar
if (qprogress) qprogress->setValue((float(i+1)/float(size)*10.0));
QApplication::processEvents();
}
Machine *m=NULL;
QString fn;
Session *sess;
int cnt=0;
size=sessfiles.size();
QHash<SessionID,int> sessday;
/////////////////////////////////////////////////////////////////////////////
// Scan over file list and knock out of dayused list
/////////////////////////////////////////////////////////////////////////////
for (QMap<SessionID,QVector<QString> >::iterator si=sessfiles.begin();si!=sessfiles.end();si++) {
sessionid=si.key();
//qDebug() << "Parsing Session " << sessionid;
bool done=false;
bool first=true;
sess=NULL;
for (int i=0;i<si.value().size();++i) {
fn=si.value()[i].section("_",-1).toLower();
EDFParser edf(si.value()[i]);
//qDebug() << "Parsing File " << i << " " << edf.filesize;
// Earliest possible day number
int edn=((sessionid-time)/86400)-1;
if (edn<0) edn=0;
// Find real day number from str.edf mask on/off data.
int dn=-1;
for (int j=edn;j<strfirst.size();j++){
time_t st=strfirst.at(j);
time_t et=strlast.at(j);
if (sessionid>=st) {
if (sessionid<(et+300)) {
dn=j;
break;
}
}
}
if (dn>=0) {
dayused[dn]=0;
}
}
EDFSignal *sig;
/////////////////////////////////////////////////////////////////////////////
// For all days not in session lists, (to get at days without data records)
/////////////////////////////////////////////////////////////////////////////
for (int dn=0;dn<days;dn++) {
if (!dayused[dn]) continue; // Skip days with loadable data.
if (!daystarttimes.contains(dn)) continue;
int j=0;
int scnt=daystarttimes[dn].size();
sess=NULL;
// For each mask on/off segment.
for (j;j<scnt;j++) {
st=daystarttimes[dn].at(j);
// Skip if session already exists
if (m->SessionExists(st))
continue;
et=dayendtimes[dn].at(j);
// Create session
sess=new Session(m,st);
sess->really_set_first(qint64(st)*1000L);
sess->really_set_last(qint64(et)*1000L);
/////////////////////////////////////////////////////////////////////
// CPAP Mode
/////////////////////////////////////////////////////////////////////
int mode;
sig=stredf.lookupSignal(CPAP_Mode);
if (sig) {
mode=sig->data[dn];
} else mode=0;
/////////////////////////////////////////////////////////////////////
// EPR Settings
/////////////////////////////////////////////////////////////////////
sess->settings[CPAP_PresReliefType]=PR_EPR;
// Note: AutoSV machines don't have both fields
sig=stredf.lookupSignal(RMS9_EPR);
if (sig) {
int i=sig->data[dn];
sess->settings[CPAP_PresReliefMode]=i;
}
sig=stredf.lookupSignal(RMS9_EPRSet);
if (sig) {
sess->settings[CPAP_PresReliefSet]=sig->data[dn];
}
/////////////////////////////////////////////////////////////////////
// Set Min & Max pressures depending on CPAP mode
/////////////////////////////////////////////////////////////////////
if (mode==0) {
sess->settings[CPAP_Mode]=MODE_CPAP;
sig=stredf.lookupSignal(RMS9_SetPressure); // ?? What's meant by Set Pressure?
if (sig) {
EventDataType pressure=sig->data[dn]*sig->gain;
sess->settings[CPAP_PressureMin]=pressure;
}
} else { // VPAP or Auto
if (mode>5) {
sess->settings[CPAP_Mode]=MODE_BIPAP;
} else {
sess->settings[CPAP_Mode]=MODE_APAP;
}
sig=stredf.lookupSignal(CPAP_PressureMin);
if (sig) {
EventDataType pressure=sig->data[dn]*sig->gain;
sess->settings[CPAP_PressureMin]=pressure;
sess->setMin(CPAP_Pressure,pressure);
}
sig=stredf.lookupSignal(CPAP_PressureMax);
if (sig) {
EventDataType pressure=sig->data[dn]*sig->gain;
sess->settings[CPAP_PressureMax]=pressure;
sess->setMax(CPAP_Pressure,pressure);
}
}
sess->SetChanged(true);
m->AddSession(sess,profile);
}
// Add the remainder to the last session
EventDataType tmp,dur;
if (sess) {
EventDataType valmed=0,valmax=0,val95=0;
if (stredf.lookup.contains("Leak Med")) {
sig=stredf.lookup["Leak Med"];
valmed=sig->data[dn];
sess->setMedian(CPAP_Leak,valmed*sig->gain*60.0);
sess->m_gain[CPAP_Leak]=sig->gain*60.0;
sess->m_valuesummary[CPAP_Leak][valmed]=50;
}
if (stredf.lookup.contains("Leak 95")) {
sig=stredf.lookup["Leak 95"];
val95=sig->data[dn];
sess->set95p(CPAP_Leak,val95*sig->gain*60.0);
sess->m_valuesummary[CPAP_Leak][val95]=45;
}
if (stredf.lookup.contains("Leak Max")) {
sig=stredf.lookup["Leak Max"];
valmax=sig->data[dn];
sess->setMax(CPAP_Leak,valmax*sig->gain*60.0);
sess->m_valuesummary[CPAP_Leak][valmax]=5;
}
if (stredf.lookup.contains("Mask Pres Med")) {
sig=stredf.lookup["Mask Pres Med"];
valmed=sig->data[dn];
sess->setMedian(CPAP_Pressure,valmed*sig->gain);
sess->m_gain[CPAP_Pressure]=sig->gain;
sess->m_valuesummary[CPAP_Pressure][valmed]=50;
}
if (stredf.lookup.contains("Mask Pres 95")) {
sig=stredf.lookup["Mask Pres 95"];
val95=sig->data[dn];
sess->set95p(CPAP_Pressure,val95*sig->gain);
sess->m_valuesummary[CPAP_Pressure][val95]=45;
}
if (stredf.lookup.contains("Mask Pres Max")) {
sig=stredf.lookup["Mask Pres Max"];
valmax=sig->data[dn];
sess->setMax(CPAP_Pressure,valmax*sig->gain);
sess->m_valuesummary[CPAP_Pressure][valmax]=5;
}
if (stredf.lookup.contains("Mask Dur")) {
sig=stredf.lookup["Mask Dur"];
dur=sig->data[dn]*sig->gain;
}
if (stredf.lookup.contains("OAI")) {
sig=stredf.lookup["OAI"];
tmp=sig->data[dn]*sig->gain;
sess->setCph(CPAP_Obstructive,tmp);
sess->setCount(CPAP_Obstructive,tmp*(dur/60.0));
}
if (stredf.lookup.contains("HI")) {
sig=stredf.lookup["HI"];
tmp=sig->data[dn]*sig->gain;
sess->setCph(CPAP_Hypopnea,tmp);
sess->setCount(CPAP_Hypopnea,tmp*(dur/60.0));
}
if (stredf.lookup.contains("UAI")) {
sig=stredf.lookup["UAI"];
tmp=sig->data[dn]*sig->gain;
sess->setCph(CPAP_Apnea,tmp);
sess->setCount(CPAP_Apnea,tmp*(dur/60.0));
}
if (stredf.lookup.contains("CAI")) {
sig=stredf.lookup["CAI"];
tmp=sig->data[dn]*sig->gain;
sess->setCph(CPAP_ClearAirway,tmp);
sess->setCount(CPAP_ClearAirway,tmp*(dur/60.0));
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Scan through new file list and import sessions
/////////////////////////////////////////////////////////////////////////////
for (QMap<SessionID,QVector<QString> >::iterator si=sessfiles.begin();si!=sessfiles.end();si++) {
sessionid=si.key();
// Skip file if already imported
if (m->SessionExists(sessionid))
continue;
// Create the session
sess=new Session(m,sessionid);
// Process EDF File List
for (int i=0;i<si.value().size();++i) {
QString filename=si.value()[i]+".";
QString fullpath=newpath+filename;
// Copy the EDF file to the backup folder
QString backup_file=backup_path+datalog+"/"+filename;
QFile().copy(fullpath+ext_EDF, backup_file+ext_EDF);
QFile().copy(fullpath+ext_CRC, backup_file+ext_CRC);
fullpath+=ext_EDF;
EDFParser edf(fullpath);
// Parse the actual file
if (!edf.Parse())
continue;
if (first) { // First EDF file parsed, check if this data set is already imported
m=CreateMachine(edf.serialnumber,profile);
for (QHash<QString,QString>::iterator i=idmap.begin();i!=idmap.end();i++) {
if (i.key()=="SRN") {
if (edf.serialnumber!=i.value()) {
// Give a warning if doesn't match the machine serial number in Identification.tgt
if (edf.serialnumber!=serial) {
qDebug() << "edf Serial number doesn't match Identification.tgt";
}
if (edf.serialnumber!=stredf.serialnumber) {
qDebug() << "edf Serial number doesn't match STR.edf!";
}
} else if (i.key()=="PNA") {
//m->properties[STR_PROP_Model]=""; //i.value();
} else if (i.key()=="PCD") { // Product Code..
bool ok;
int j=i.value().toInt(&ok);
if (RMS9ModelMap.find(j)!=RMS9ModelMap.end()) {
m->properties[STR_PROP_Model]=RMS9ModelMap[j];
}
} else {
m->properties[i.key()]=i.value();
}
}
if (m->SessionExists(sessionid)) {
done=true;
break;
}
sess=new Session(m,sessionid);
first=false;
}
if (!done) {
fn=fullpath.section("_",-1).toLower();
if (fn=="eve.edf") LoadEVE(sess,edf);
else if (fn=="pld.edf") LoadPLD(sess,edf);
else if (fn=="brp.edf") LoadBRP(sess,edf);
else if (fn=="sad.edf") LoadSAD(sess,edf);
//if (first) {
//first=false;
//}
}
}
if (qprogress) qprogress->setValue(33.0+(float(++cnt)/float(size)*66.0));
if (qprogress) qprogress->setValue(10.0+(float(++cnt)/float(size)*90.0));
QApplication::processEvents();
EDFSignal *sig;
if (!sess) continue;
if (!sess->first()) {
delete sess;
@ -428,15 +783,19 @@ int ResmedLoader::Open(QString & path,Profile *profile)
mode=sig->data[dn];
} else mode=0;
sess->settings[CPAP_PresReliefType]=PR_EPR;
// AutoSV machines don't have both fields
sig=stredf.lookupSignal(RMS9_EPR);
if (sig) {
sess->settings[RMS9_EPR]=sig->data[dn];
int i=sig->data[dn];
sess->settings[CPAP_PresReliefMode]=i;
}
sig=stredf.lookupSignal(RMS9_EPRSet);
if (sig) {
sess->settings[RMS9_EPRSet]=sig->data[dn];
sess->settings[CPAP_PresReliefSet]=sig->data[dn];
}
if (mode==0) {
@ -494,7 +853,7 @@ int ResmedLoader::Open(QString & path,Profile *profile)
//Rather than take a dodgy guess, EPR settings can take a hit, and this data can simply be missed..
// Add the session to the machine & profile objects
m->AddSession(sess,profile); // Adding earlier than I really like here..
m->AddSession(sess,profile);
}
}

View File

@ -210,6 +210,9 @@ protected:
//! \brief Parse the PRD low resolution data, and save to Session * sess
//! This contains the Pressure, Leak, Respiratory Rate, Minute Ventilation, Tidal Volume, etc..
bool LoadPLD(Session *sess,EDFParser &edf);
QMap<SessionID,QVector<QString> > sessfiles;
};
#endif // RESMED_LOADER_H

View File

@ -62,15 +62,44 @@ Session *Machine::SessionExists(SessionID session)
}
}
Day *Machine::AddSession(Session *s,Profile *p)
// Find date this session belongs in
QDate Machine::pickDate(qint64 first)
{
QTime split_time=PROFILE.session->daySplitTime();
int combine_sessions=PROFILE.session->combineCloseSessions();
QDateTime d2=QDateTime::fromTime_t(first/1000);
QDate date=d2.date();
QTime time=d2.time();
int closest_session=0;
if (time<split_time) {
date=date.addDays(-1);
} else if (combine_sessions > 0) {
QMap<QDate,Day *>::iterator dit=day.find(date.addDays(-1)); // Check Day Before
if (dit != day.end()) {
QDateTime lt=QDateTime::fromTime_t(dit.value()->last()/1000L);
closest_session=lt.secsTo(d2)/60;
if (closest_session < combine_sessions) {
date=date.addDays(-1);
}
}
}
return date;
}
QDate Machine::AddSession(Session *s,Profile *p)
{
if (!s) {
qWarning() << "Empty Session in Machine::AddSession()";
return NULL;
return QDate();
}
if (!p) {
qWarning() << "Empty Profile in Machine::AddSession()";
return NULL;
return QDate();
}
if (s->session()>highest_sessionid)
highest_sessionid=s->session();
@ -92,7 +121,6 @@ Day *Machine::AddSession(Session *s,Profile *p)
QMap<QDate,Day *>::iterator dit,nextday;
bool combine_next_day=false;
int closest_session=0;
@ -121,7 +149,7 @@ Day *Machine::AddSession(Session *s,Profile *p)
if (session_length<ignore_sessions) {
//if (!closest_session || (closest_session>=60))
return NULL;
return QDate();
}
if (!firstsession) {
@ -159,7 +187,7 @@ Day *Machine::AddSession(Session *s,Profile *p)
}
day.erase(nextday);
}
return dd;
return date;
}
// This functions purpose is murder and mayhem... It deletes all of a machines data.
@ -421,10 +449,14 @@ CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_
CPAP_RERA, CPAP_PressurePulse, CPAP_FlowLimit, 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_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI;
CPAP_UserFlag1, CPAP_UserFlag2, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI,
CPAP_PresReliefSet, CPAP_PresReliefMode, CPAP_PresReliefType;
ChannelID RMS9_E01, RMS9_E02, RMS9_EPR, RMS9_EPRSet, RMS9_SetPressure;
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, PRS1_10, PRS1_12,
PRS1_FlexMode, PRS1_FlexSet, PRS1_HumidStatus, PRS1_HumidSetting, PRS1_SysLock, PRS1_SysOneResistStat,
PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;

View File

@ -95,7 +95,10 @@ public:
Session * SessionExists(SessionID session);
//! \brief Adds the session to this machine object, and the Master Profile list. (used during load)
Day *AddSession(Session *s,Profile *p);
QDate AddSession(Session *s,Profile *p);
//! \brief Find the date this session belongs in, according to profile settings
QDate pickDate(qint64 start);
//! \brief Sets the Class of machine (Used to reference the particular loader that created it)
void SetClass(QString t) { m_class=t; }

View File

@ -63,6 +63,10 @@ enum PRTypes//:short
{
PR_UNKNOWN=0,PR_NONE,PR_CFLEX,PR_CFLEXPLUS,PR_AFLEX,PR_BIFLEX,PR_EPR,PR_SMARTFLEX
};
enum PRModes//:short
{
PM_UNKNOWN=0,PM_RampOnly,PM_FullTime
};
//extern map<ChannelID,QString> DefaultMCShortNames;
@ -85,7 +89,8 @@ CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_
CPAP_RERA, CPAP_PressurePulse, CPAP_FlowLimit, 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_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI;
CPAP_UserFlag1, CPAP_UserFlag2, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI,
CPAP_PresReliefSet, CPAP_PresReliefMode, CPAP_PresReliefType;
extern ChannelID RMS9_E01, RMS9_E02, RMS9_EPR, RMS9_EPRSet, RMS9_SetPressure;
extern ChannelID INTP_SmartFlex;
@ -93,6 +98,8 @@ extern ChannelID PRS1_00, PRS1_01, PRS1_08, PRS1_0A, PRS1_0B, PRS1_0C, PRS1_0E,
PRS1_FlexMode, PRS1_FlexSet, PRS1_HumidStatus, PRS1_HumidSetting, PRS1_SysLock, PRS1_SysOneResistStat,
PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
extern ChannelID INTELLIPAP_Unknown1, INTELLIPAP_Unknown2;
extern ChannelID OXI_Pulse, OXI_SPO2, OXI_PulseChange, OXI_SPO2Drop, OXI_Plethy;
extern ChannelID Journal_Notes, Journal_Weight, Journal_BMI, Journal_ZombieMeter, Bookmark_Start, Bookmark_End, Bookmark_Notes;

View File

@ -106,6 +106,11 @@ void init()
CPAP_Te=schema::channel["Te"].id();
CPAP_Ti=schema::channel["Ti"].id();
CPAP_TgMV=schema::channel["TgMV"].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();
RMS9_E01=schema::channel["RMS9_E01"].id();
@ -135,6 +140,8 @@ void init()
PRS1_AutoOff=schema::channel["AutoOff"].id();
PRS1_MaskAlert=schema::channel["MaskAlert"].id();
PRS1_ShowAHI=schema::channel["ShowAHI"].id();
INTELLIPAP_Unknown1=schema::channel["IntUnk1"].id();
INTELLIPAP_Unknown2=schema::channel["IntUnk2"].id();
OXI_Pulse=schema::channel["Pulse"].id();
OXI_SPO2=schema::channel["SPO2"].id();
OXI_PulseChange=schema::channel["PulseChange"].id();

View File

@ -22,7 +22,7 @@ const quint16 filetype_data=1;
// This is the uber important database version for SleepyHeads internal storage
// Increment this after stuffing with Session's save & load code.
const quint16 summary_version=9;
const quint16 summary_version=10;
const quint16 events_version=8;
Session::Session(Machine * m,SessionID session)
@ -139,6 +139,8 @@ bool Session::StoreSummary(QString filename)
out << m_avg;
out << m_wavg;
out << m_90p;
out << m_95p;
out << m_med;
out << m_min;
out << m_max;
out << m_cph;
@ -299,6 +301,10 @@ bool Session::LoadSummary(QString filename)
in >> m_avg;
in >> m_wavg;
in >> m_90p;
if (version >= 10) {
in >> m_95p;
in >> m_med;
}
in >> m_min;
in >> m_max;
in >> m_cph;
@ -613,22 +619,24 @@ void Session::UpdateSummaries()
Min(id);
Max(id);
count(id);
last(id);
first(id);
if ((id==CPAP_FlowRate) || (id==CPAP_MaskPressure)) continue;
cph(id);
sph(id);
avg(id);
wavg(id);
p90(id);
last(id);
first(id);
// p90(id);
// p95(id);
// median(id);
}
}
if (channelExists(CPAP_Obstructive)) {
/*if (channelExists(CPAP_Obstructive)) {
setCph(CPAP_AHI,cph(CPAP_Obstructive)+cph(CPAP_Hypopnea)+cph(CPAP_ClearAirway));
setSph(CPAP_AHI,sph(CPAP_Obstructive)+sph(CPAP_Hypopnea)+sph(CPAP_ClearAirway));
}
}*/
}
@ -994,6 +1002,41 @@ EventDataType Session::p90(ChannelID id) // 90th Percentile
m_90p[id]=val;
return val;
}
EventDataType Session::p95(ChannelID id)
{
QHash<ChannelID,EventDataType>::iterator i=m_95p.find(id);
if (i!=m_95p.end())
return i.value();
if (!eventlist.contains(id)) {
m_95p[id]=0;
return 0;
}
EventDataType val=percentile(id,0.95);
m_95p[id]=val;
return val;
}
EventDataType Session::median(ChannelID id)
{
QHash<ChannelID,EventDataType>::iterator i=m_med.find(id);
if (i!=m_med.end())
return i.value();
if (!eventlist.contains(id)) {
m_med[id]=0;
return 0;
}
EventDataType val=percentile(id,0.5);
m_med[id]=val;
return val;
}
bool sortfunction (EventStoreType i,EventStoreType j) { return (i<j); }
EventDataType Session::percentile(ChannelID id,EventDataType percent)

View File

@ -141,6 +141,8 @@ public:
QHash<ChannelID,EventDataType> m_avg;
QHash<ChannelID,EventDataType> m_wavg;
QHash<ChannelID,EventDataType> m_90p;
QHash<ChannelID,EventDataType> m_95p;
QHash<ChannelID,EventDataType> m_med;
QHash<ChannelID,EventDataType> m_min;
QHash<ChannelID,EventDataType> m_max;
QHash<ChannelID,EventDataType> m_cph; // Counts per hour (eg AHI)
@ -161,7 +163,9 @@ public:
void setMax(ChannelID id,EventDataType val) { m_max[id]=val; }
void setAvg(ChannelID id,EventDataType val) { m_avg[id]=val; }
void setWavg(ChannelID id,EventDataType val) { m_wavg[id]=val; }
void setMedian(ChannelID id,EventDataType val) { m_med[id]=val; }
void set90p(ChannelID id,EventDataType val) { m_90p[id]=val; }
void set95p(ChannelID id,EventDataType val) { m_95p[id]=val; }
void setCph(ChannelID id,EventDataType val) { m_cph[id]=val; }
void setSph(ChannelID id,EventDataType val) { m_sph[id]=val; }
void setFirst(ChannelID id,qint64 val) { m_firstchan[id]=val; }
@ -200,6 +204,12 @@ public:
//! \brief Returns (and caches) the 90th Percentile of all events of type id
EventDataType p90(ChannelID id);
//! \brief Returns (and caches) the 95th Percentile of all events of type id
EventDataType p95(ChannelID id);
//! \brief Returns (and caches) the Median (50th Perc) of all events of type id
EventDataType median(ChannelID id);
//! \brief Returns (and caches) the Count-Per-Hour of all events of type id
EventDataType cph(ChannelID id);

View File

@ -715,16 +715,18 @@ void Daily::Load(QDate date)
} else {
GraphView->setCubeImage(images["nodata"]);
GraphView->setEmptyText(tr("No Data"));
emptyToggleArea->setText("No data available for this day");
emptyToggleArea->setText("No graph data available for this day");
}
if (cpap) {
if (GraphView->isEmpty()) {
if (cpap->machine->GetClass()!=STR_MACH_ResMed) {
GraphView->setCubeImage(images["brick"]);
GraphView->setEmptyText(tr("Brick Machine :("));
GraphView->setEmptyText(tr("No Graphs :("));
isBrick=true;
}
}
mode=(CPAPMode)(int)cpap->settings_max(CPAP_Mode);

View File

@ -74,6 +74,8 @@ Important: One id code per item, DO NOT CHANGE ID NUMBERS!!!
<channel id="0x1165" class="setting" scope="!session" name="SmartFlex" details="SmartFlex" label="SmartFlex" type="integer"/>
<channel id="0x1166" class="data" name="PRS1_0F" details="Unknown 0F" label="U0F" unit="?" color="dark green"/>
<channel id="0x1167" class="data" name="IntUnk1" details="Unknown 1" label="Unknown 1" unit="?" color="dark green"/>
<channel id="0x1168" class="data" name="IntUnk2" details="Unknown 2" label="Unknown 2" unit="?" color="dark green"/>
<channel id="0x1200" class="setting" scope="!session" name="PAPMode" details="PAP Mode" label="PAP Mode" type="integer">
<option id="0" value="CPAP"/>
@ -81,6 +83,23 @@ Important: One id code per item, DO NOT CHANGE ID NUMBERS!!!
<option id="2" value="Bi-Level"/>
<option id="3" value="ASV"/>
</channel>
<channel id="0x1201" class="setting" scope="!session" name="PresRelType" details="Pressure Relief Type" label="Pres. Relief" type="integer">
<Option id="0" value=""/>
<Option id="1" value="None"/>
<Option id="2" value="C-Flex"/>
<Option id="3" value="C-Flex+"/>
<Option id="4" value="A-Flex"/>
<Option id="5" value="Bi-Flex"/>
<Option id="6" value="EPR"/>
<Option id="7" value="SmartFlex"/>
</channel>
<channel id="0x1202" class="setting" scope="!session" name="PresRelMode" details="Pressure Relief Mode" label="Pres. Rel. Mode" type="integer">
<Option id="0" value=""/>
<Option id="1" value="Ramp"/>
<Option id="2" value="Full Time"/>
</channel>
<channel id="0x1203" class="setting" scope="!session" name="PresResSet" details="Pressure Relief Setting" label="Pres. Rel. Set"/>
</group>
<group name="OXI">
<channel id="0x1800" class="data" name="Pulse" details="Pulse Rate" label="Pulse Rate" unit="bpm" color="red"/>

View File

@ -13,6 +13,10 @@
<property name="windowTitle">
<string>Export as CSV</string>
</property>
<property name="windowIcon">
<iconset resource="Resources.qrc">
<normaloff>:/icons/save.png</normaloff>:/icons/save.png</iconset>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="3" column="0">
<widget class="QLabel" name="label_6">
@ -269,7 +273,9 @@
</item>
</layout>
</widget>
<resources/>
<resources>
<include location="Resources.qrc"/>
</resources>
<connections>
<connection>
<sender>cancelButton</sender>

View File

@ -13,6 +13,10 @@
<property name="windowTitle">
<string>Edit User Profile</string>
</property>
<property name="windowIcon">
<iconset resource="Resources.qrc">
<normaloff>:/icons/bob-v3.0.png</normaloff>:/icons/bob-v3.0.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">

View File

@ -238,6 +238,7 @@ Overview::Overview(QWidget *parent,gGraphView * shared) :
lk=new SummaryChart(tr("Avg Leak"),GT_LINE);
lk->addSlice(CPAP_Leak,QColor("dark grey"),ST_PERC,false,0.95);
lk->addSlice(CPAP_Leak,QColor("dark blue"),ST_WAVG,false);
lk->addSlice(CPAP_Leak,QColor("grey"),ST_MAX,false);
//lk->addSlice(CPAP_Leak,QColor("dark yellow"));
//pr->addSlice(CPAP_IPAP,QColor("red"));
LK->AddLayer(lk);

View File

@ -1311,7 +1311,7 @@ void Oximetry::on_saveButton_clicked()
if (m->SessionExists(session->session())) {
m->sessionlist.erase(m->sessionlist.find(session->session()));
}
QString path=PROFILE.Get(m->properties[STR_PROP_Path])+QString().sprintf("%08x",session->session());
QString path=PROFILE.Get(m->properties[STR_PROP_Path])+QString().sprintf("%08lx",session->session());
QString f1=path+".000";
QString f2=path+".001";
QFile::remove(f1);

View File

@ -22,6 +22,10 @@
<property name="windowTitle">
<string>Preferences</string>
</property>
<property name="windowIcon">
<iconset resource="Resources.qrc">
<normaloff>:/icons/preferences.png</normaloff>:/icons/preferences.png</iconset>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
@ -2066,7 +2070,9 @@ this application to be unstable with this feature enabled.</string>
</item>
</layout>
</widget>
<resources/>
<resources>
<include location="Resources.qrc"/>
</resources>
<connections>
<connection>
<sender>okButton</sender>

View File

@ -15,7 +15,7 @@
</property>
<property name="windowIcon">
<iconset resource="Resources.qrc">
<normaloff>:/docs/sheep.png</normaloff>:/docs/sheep.png</iconset>
<normaloff>:/icons/bob-v3.0.png</normaloff>:/icons/bob-v3.0.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>