DeVilbiss Intellipap Loader

This commit is contained in:
Mark Watkins 2011-11-21 20:20:11 +10:00
parent abca267416
commit 7182c84786
10 changed files with 288 additions and 18 deletions

View File

@ -22,7 +22,7 @@ Day::~Day()
MachineType Day::machine_type() MachineType Day::machine_type()
{ {
return machine->GetType(); return machine->GetType();
}; }
void Day::AddSession(Session *s) void Day::AddSession(Session *s)
{ {

View File

@ -11,9 +11,12 @@ It does not seem to record multiple days, graph data is overwritten each time..
*/ */
#include <QDir> #include <QDir>
#include <QProgressBar>
#include "intellipap_loader.h" #include "intellipap_loader.h"
extern QProgressBar *qprogress;
Intellipap::Intellipap(Profile *p,MachineID id) Intellipap::Intellipap(Profile *p,MachineID id)
:CPAP(p,id) :CPAP(p,id)
{ {
@ -41,6 +44,7 @@ IntellipapLoader::~IntellipapLoader()
int IntellipapLoader::Open(QString & path,Profile *profile) int IntellipapLoader::Open(QString & path,Profile *profile)
{ {
// Check for SL directory // Check for SL directory
// Check for DV5MFirm.bin?
QString newpath; QString newpath;
QString dirtag="SL"; QString dirtag="SL";
@ -51,41 +55,272 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
newpath=path+QDir::separator()+dirtag; newpath=path+QDir::separator()+dirtag;
} }
unsigned char buf[27]; QString filename;
QString filename=newpath+QDir::separator()+"U";
//////////////////////////
// Parse the Settings File
//////////////////////////
filename=newpath+QDir::separator()+"SET1";
QFile f(filename); QFile f(filename);
if (!f.exists()) return 0; if (!f.exists()) return 0;
f.open(QFile::ReadOnly);
QTextStream tstream(&f);
QHash<quint32,quint32> Sessions; QHash<QString,QString> lookup;
lookup["Sn"]="Serial";
lookup["Mn"]="Model";
lookup["Mo"]="PAPMode"; // 0=cpap, 1=auto
//lookup["Pn"]="Pn";
lookup["Pu"]="MaxPressure";
lookup["Pl"]="MinPressure";
//lookup["Ds"]="Ds";
//lookup["Pc"]="Pc";
lookup["Pd"]="RampPressure";
lookup["Dt"]="RampTime";
//lookup["Ld"]="Ld";
//lookup["Lh"]="Lh";
//lookup["FC"]="FC";
//lookup["FE"]="FE";
//lookup["FL"]="FL";
lookup["A%"]="ApneaThreshold";
lookup["Ad"]="ApneaDuration";
lookup["H%"]="HypopneaThreshold";
lookup["Hd"]="HypopneaDuration";
//lookup["Pi"]="Pi"; //080
//lookup["Pe"]="Pe"; //WF
//lookup["Ri"]="SmartFlexIRnd"; //1
//lookup["Re"]="SmartFlexERnd"; //2
//lookup["Bu"]="Bu"; //WF
//lookup["Ie"]="Ie"; //20
//lookup["Se"]="Se"; //05
//lookup["Si"]="Si"; //05
//lookup["Mi"]="Mi"; //0
//lookup["Uh"]="Uh"; //0000.0
//lookup["Up"]="Up"; //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["Ks=s"]="Ks_s";
lookup["Ks=i"]="Ks_i";
quint32 ts1, ts2, length; QHash<QString,QString> set1;
unsigned char cs; QHash<QString,QString>::iterator hi;
while (1) {
QString line=tstream.readLine();
if ((line.length()<=2) ||
(line.isNull())) break;
QString key=line.section("\t",0,0).trimmed();
hi=lookup.find(key);
if (hi!=lookup.end()) {
key=hi.value();
}
QString value=line.section("\t",1).trimmed();
set1[key]=value;
qDebug() << key << "=" << value;
}
Machine *mach=NULL;
if (set1.contains("Serial")) {
mach=CreateMachine(set1["Serial"],profile);
}
if (!mach) {
qDebug() << "Couldn't get Intellipap machine record";
return 0;
}
// Refresh properties data..
for (QHash<QString,QString>::iterator i=set1.begin();i!=set1.end();i++) {
mach->properties[i.key()]=i.value();
}
f.close();
//////////////////////////
// Parse the Session Index
//////////////////////////
unsigned char buf[27];
filename=newpath+QDir::separator()+"U";
f.setFileName(filename);
if (!f.exists()) return 0;
QVector<quint32> SessionStart;
QVector<quint32> SessionEnd;
QHash<SessionID,Session *> Sessions;
quint32 ts1, ts2;//, length;
//unsigned char cs;
f.open(QFile::ReadOnly); f.open(QFile::ReadOnly);
int cnt=0; int cnt=0;
QDateTime epoch(QDate(2000,1,1),QTime(0,0,0),Qt::UTC); QDateTime epoch(QDate(2002,1,1),QTime(0,0,0),Qt::UTC); // Intellipap Epoch
int ep=epoch.toTime_t(); int ep=epoch.toTime_t();
do { do {
cnt=f.read((char *)buf,9); cnt=f.read((char *)buf,9);
ts1=(buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; ts1=(buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
ts2=(buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; ts2=(buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
Sessions[ts1]=ts2; ts1+=ep;
QDateTime d1=QDateTime::fromTime_t(ts1+ep); ts2+=ep;
QDateTime d2=QDateTime::fromTime_t(ts2+ep); SessionStart.append(ts1);
SessionEnd.append(ts2);
length=ts2-ts1; //cs=buf[8];
cs=buf[8];
qDebug() << d1 << d2 << "Length: (" << length << ")";
} while (cnt>0); } while (cnt>0);
qDebug() << "U file logs" << SessionStart.size() << "sessions.";
f.close(); f.close();
//////////////////////////
// Parse the Session Data
//////////////////////////
filename=newpath+QDir::separator()+"L"; filename=newpath+QDir::separator()+"L";
f.setFileName(filename); f.setFileName(filename);
if (!f.exists()) return 0; if (!f.exists()) return 0;
f.open(QFile::ReadOnly); f.open(QFile::ReadOnly);
long size=f.size();
int recs=size/26;
m_buffer=new unsigned char [size];
if (size!=f.read((char *)m_buffer,size)) {
qDebug() << "Couldn't read 'L' data"<< filename;
return 0;
}
// Check for DV5MFirm.bin Session *sess;
SessionID sid;
for (int i=0;i<SessionStart.size();i++) {
sid=SessionStart[i];
if (mach->SessionExists(sid)) {
// knock out the already imported sessions..
SessionStart[i]=0;
SessionEnd[i]=0;
} else if (!Sessions.contains(sid)) {
sess=Sessions[sid]=new Session(mach,sid);
sess->SetChanged(true);
sess->AddEventList(CPAP_IPAP,EVL_Event);
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(CPAP_Leak,EVL_Event);
sess->AddEventList(CPAP_MaxLeak,EVL_Event);
//sess->AddEventList(CPAP_AHI,EVL_Event);
sess->AddEventList(CPAP_TidalVolume,EVL_Event);
sess->AddEventList(CPAP_RespRate,EVL_Event);
sess->AddEventList(CPAP_Snore,EVL_Event);
} else {
// If there is a double up, null out the earlier session
// otherwise there will be a crash on shutdown.
for (int z=0;z<SessionStart.size();z++) {
if (SessionStart[z]==(quint32)sid) {
SessionStart[z]=0;
SessionEnd[z]=0;
break;
}
}
QDateTime d=QDateTime::fromTime_t(sid);
qDebug() << sid << "has double ups" << d;
/*Session *sess=Sessions[sid];
Sessions.erase(Sessions.find(sid));
delete sess;
SessionStart[i]=0;
SessionEnd[i]=0; */
}
}
long pos=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;
for (int j=0;j<SessionStart.size();j++) {
sid=SessionStart[j];
if (!sid) continue;
if ((ts1>=(quint32)sid) && (ts1<SessionEnd[j])){
Session *sess=Sessions[sid];
qint64 time=quint64(ts1)*1000L;
sess->eventlist[CPAP_Pressure][0]->AddEvent(time,m_buffer[pos+0xd]/10.0); // 0x0d
sess->eventlist[CPAP_EPAP][0]->AddEvent(time,m_buffer[pos+0x13]/10.0);
sess->eventlist[CPAP_IPAP][0]->AddEvent(time,m_buffer[pos+0x14]/10.0);
sess->eventlist[CPAP_Leak][0]->AddEvent(time,m_buffer[pos+0x7]); //correct
sess->eventlist[CPAP_MaxLeak][0]->AddEvent(time,m_buffer[pos+0x6]); //correct
sess->eventlist[CPAP_RespRate][0]->AddEvent(time,m_buffer[pos+0xa]); // 0x0a is correct
sess->eventlist[CPAP_Te][0]->AddEvent(time,m_buffer[pos+0xf]);
sess->eventlist[CPAP_Ti][0]->AddEvent(time,m_buffer[pos+0xc]);
sess->eventlist[CPAP_Snore][0]->AddEvent(time,m_buffer[pos+0x5]); //4/5??
if (m_buffer[pos+0x5]>0) {
if (!sess->eventlist.contains(CPAP_VSnore)) {
sess->AddEventList(CPAP_VSnore,EVL_Event);
}
sess->eventlist[CPAP_VSnore][0]->AddEvent(time,m_buffer[pos+0x5]);
}
if (m_buffer[pos+0x10]>0) {
if (!sess->eventlist.contains(CPAP_Obstructive)) {
sess->AddEventList(CPAP_Obstructive,EVL_Event);
}
sess->eventlist[CPAP_Obstructive][0]->AddEvent(time,m_buffer[pos+0x10]);
}
if (m_buffer[pos+0x11]>0) {
if (!sess->eventlist.contains(CPAP_Hypopnea)) {
sess->AddEventList(CPAP_Hypopnea,EVL_Event);
}
sess->eventlist[CPAP_Hypopnea][0]->AddEvent(time,m_buffer[pos+0x11]);
}
if (m_buffer[pos+0x12]>0) {
if (!sess->eventlist.contains(CPAP_Apnea)) {
sess->AddEventList(CPAP_Apnea,EVL_Event);
}
sess->eventlist[CPAP_Apnea][0]->AddEvent(time,m_buffer[pos+0x12]);
}
quint16 tv=(m_buffer[pos+0x8] << 8) | m_buffer[pos+0x9]; // correct
sess->eventlist[CPAP_TidalVolume][0]->AddEvent(time,tv);
break;
}
}
pos+=26;
}
for (int i=0;i<SessionStart.size();i++) {
SessionID sid=SessionStart[i];
if (sid) {
sess=Sessions[sid];
//if (sess->eventlist.size()==0) {
// delete sess;
// continue;
//}
quint64 first=quint64(sid)*1000L;
quint64 last=quint64(SessionEnd[i])*1000L;
quint64 len=last-first;
//if (len>0) {
//if (!sess->first()) {
sess->set_first(first);
sess->set_last(last);
// }
sess->UpdateSummaries();
mach->AddSession(sess,profile);
/*} else {
delete sess;
}*/
}
}
mach->properties["DataVersion"]=QString().sprintf("%i",intellipap_data_version);
mach->Save();
delete [] m_buffer;
if (qprogress) qprogress->setValue(100);
f.close();
return 1; return 1;
} }

View File

@ -87,6 +87,7 @@ const QString CPAP_RespRate="RespRate";
const QString CPAP_TidalVolume="TidalVolume"; const QString CPAP_TidalVolume="TidalVolume";
const QString CPAP_PTB="PTB"; const QString CPAP_PTB="PTB";
const QString CPAP_Leak="Leak"; const QString CPAP_Leak="Leak";
const QString CPAP_MaxLeak="MaxLeak";
const QString CPAP_FLG="FLG"; const QString CPAP_FLG="FLG";
const QString CPAP_IE="IE"; const QString CPAP_IE="IE";
const QString CPAP_Te="Te"; const QString CPAP_Te="Te";

View File

@ -414,6 +414,7 @@ void Session::UpdateSummaries()
//sum(id); // avg calculates this and cnt. //sum(id); // avg calculates this and cnt.
min(id); min(id);
max(id); max(id);
count(id);
if ((id==CPAP_FlowRate) || (id==CPAP_MaskPressure)) continue; if ((id==CPAP_FlowRate) || (id==CPAP_MaskPressure)) continue;
cph(id); cph(id);

View File

@ -178,6 +178,7 @@ Daily::Daily(QWidget *parent,gGraphView * shared, MainWindow *mw)
PRD->AddLayer(AddCPAP(new gLineChart(CPAP_IPAP,Qt::red,square))); PRD->AddLayer(AddCPAP(new gLineChart(CPAP_IPAP,Qt::red,square)));
LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_Leak,Qt::darkYellow,square))); LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_Leak,Qt::darkYellow,square)));
LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_MaxLeak,Qt::darkRed,square)));
SNORE->AddLayer(AddCPAP(new gLineChart(CPAP_Snore,Qt::darkGray,true))); SNORE->AddLayer(AddCPAP(new gLineChart(CPAP_Snore,Qt::darkGray,true)));
PTB->AddLayer(AddCPAP(new gLineChart(CPAP_PTB,Qt::gray,square))); PTB->AddLayer(AddCPAP(new gLineChart(CPAP_PTB,Qt::gray,square)));

View File

@ -45,6 +45,7 @@ One id code per item
<channel id="0x1112" class="data" name="RespEvent" details="Respiratory Events" label="Resp Events" unit="" color="black"/> <channel id="0x1112" class="data" name="RespEvent" details="Respiratory Events" label="Resp Events" unit="" color="black"/>
<channel id="0x1113" class="data" name="FLG" details="Flow Limit Graph" label="Flow Limit" unit="" color="dark grey"/> <channel id="0x1113" class="data" name="FLG" details="Flow Limit Graph" label="Flow Limit" unit="" color="dark grey"/>
<channel id="0x1114" class="data" name="TgMV" details="Target Minute Ventilation" label="Trgt Min Vent." unit="" color="dark cyan"/> <channel id="0x1114" class="data" name="TgMV" details="Target Minute Ventilation" label="Trgt Min Vent." unit="" color="dark cyan"/>
<channel id="0x1115" class="data" name="MaxLeak" details="Maximum Leak" label="MaxLeaks" unit="L/min" color="dark red"/>
<channel id="0x1150" class="data" name="PRS1_00" details="Unknown 00" label="U00" unit="" color="black"/> <channel id="0x1150" class="data" name="PRS1_00" details="Unknown 00" label="U00" unit="" color="black"/>
<channel id="0x1151" class="data" name="PRS1_01" details="Unknown 01" label="U01" unit="" color="black"/> <channel id="0x1151" class="data" name="PRS1_01" details="Unknown 01" label="U01" unit="" color="black"/>

View File

@ -19,7 +19,9 @@ p,a,td,body { font-size: 14px }
<p>I don't recommend using this built in "web browser" to do any major surfing in, it will work, but it's mainly meant as a help browser. <p>I don't recommend using this built in "web browser" to do any major surfing in, it will work, but it's mainly meant as a help browser.
(It doesn't support SSL encryption.)</p> (It doesn't support SSL encryption.)</p>
<p>Here are the <a href='qrc:/docs/release_notes.html'>release notes</a> for this version, in case you missed them.<br/> <p>
Here is a link to the <a href="http://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=Main_Page">SleepyHead Wiki</a><br/>
Here are the <a href='qrc:/docs/release_notes.html'>release notes</a> for this version, in case you missed them.<br/>
Plus a few <a href='qrc:/docs/usage.html'>usage notes</a>, and some important information for Mac users.<br/> Plus a few <a href='qrc:/docs/usage.html'>usage notes</a>, and some important information for Mac users.<br/>
SleepyHead's <a href='http://www.sourceforge.net/projects/sleepyhead'>Project Website</a> on SourceForge</p> SleepyHead's <a href='http://www.sourceforge.net/projects/sleepyhead'>Project Website</a> on SourceForge</p>
<p>The authors' boring and neglected <a href='http://jedimark64.blogspot.com'>Personal Blog</a></p> <p>The authors' boring and neglected <a href='http://jedimark64.blogspot.com'>Personal Blog</a></p>

View File

@ -103,8 +103,8 @@ MainWindow::MainWindow(QWidget *parent) :
if (!PROFILE.Exists("EnableMultithreading")) PROFILE["EnableMultithreading"]=QThread::idealThreadCount()>1; if (!PROFILE.Exists("EnableMultithreading")) PROFILE["EnableMultithreading"]=QThread::idealThreadCount()>1;
if (!PROFILE.Exists("MemoryHog")) PROFILE["MemoryHog"]=false; if (!PROFILE.Exists("MemoryHog")) PROFILE["MemoryHog"]=false;
if (!PROFILE.Exists("EnableGraphSnapshots")) PROFILE["EnableGraphSnapshots"]=false; if (!PROFILE.Exists("EnableGraphSnapshots")) PROFILE["EnableGraphSnapshots"]=true;
if (!PROFILE.Exists("SquareWavePlots")) PROFILE["SquareWavePlots"]=true; if (!PROFILE.Exists("SquareWavePlots")) PROFILE["SquareWavePlots"]=false;
if (!PROFILE.Exists("EnableOximetry")) PROFILE["EnableOximetry"]=false; if (!PROFILE.Exists("EnableOximetry")) PROFILE["EnableOximetry"]=false;
if (!PROFILE.Exists("LinkGroups")) PROFILE["LinkGroups"]=false; if (!PROFILE.Exists("LinkGroups")) PROFILE["LinkGroups"]=false;
if (!PROFILE.Exists("AlwaysShowOverlayBars")) PROFILE["AlwaysShowOverlayBars"]=0; if (!PROFILE.Exists("AlwaysShowOverlayBars")) PROFILE["AlwaysShowOverlayBars"]=0;
@ -568,3 +568,15 @@ void MainWindow::on_actionExp_ort_triggered()
if (ex.exec()==ExportCSV::Accepted) { if (ex.exec()==ExportCSV::Accepted) {
} }
} }
void MainWindow::on_actionOnline_Users_Guide_triggered()
{
ui->webView->load(QUrl("http://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=SleepyHead_Users_Guide"));
ui->tabWidget->setCurrentIndex(0);
}
void MainWindow::on_action_Frequently_Asked_Questions_triggered()
{
ui->webView->load(QUrl("http://sourceforge.net/apps/mediawiki/sleepyhead/index.php?title=Frequently_Asked_Questions"));
ui->tabWidget->setCurrentIndex(0);
}

View File

@ -106,6 +106,10 @@ private slots:
void on_actionExp_ort_triggered(); void on_actionExp_ort_triggered();
void on_actionOnline_Users_Guide_triggered();
void on_action_Frequently_Asked_Questions_triggered();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
Daily * daily; Daily * daily;

View File

@ -601,6 +601,9 @@
<property name="title"> <property name="title">
<string>&amp;Help</string> <string>&amp;Help</string>
</property> </property>
<addaction name="actionOnline_Users_Guide"/>
<addaction name="action_Frequently_Asked_Questions"/>
<addaction name="separator"/>
<addaction name="actionDebug"/> <addaction name="actionDebug"/>
<addaction name="actionCheck_for_Updates"/> <addaction name="actionCheck_for_Updates"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -747,6 +750,16 @@
<string>Exp&amp;ort</string> <string>Exp&amp;ort</string>
</property> </property>
</action> </action>
<action name="actionOnline_Users_Guide">
<property name="text">
<string>Online Users &amp;Guide</string>
</property>
</action>
<action name="action_Frequently_Asked_Questions">
<property name="text">
<string>&amp;Frequently Asked Questions</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>