/* SleepLib (DeVilbiss) Intellipap Loader Implementation Author: Mark Watkins License: GPL Copyright: (c)2011 Mark Watkins Notes: Intellipap requires the SmartLink attachment to access this data. */ #include #include #include "intellipap_loader.h" extern QProgressBar *qprogress; Intellipap::Intellipap(Profile *p,MachineID id) :CPAP(p,id) { m_class=intellipap_class_name; } Intellipap::~Intellipap() { } IntellipapLoader::IntellipapLoader() { m_buffer=NULL; } IntellipapLoader::~IntellipapLoader() { } int IntellipapLoader::Open(QString & path,Profile *profile) { // Check for SL directory // Check for DV5MFirm.bin? QString newpath; path=path.replace("\\","/"); QString dirtag="SL"; if (path.endsWith("/"+dirtag)) { return 0; //newpath=path; } else { newpath=path+"/"+dirtag; } QString filename; ////////////////////////// // Parse the Settings File ////////////////////////// filename=newpath+"/SET1"; QFile f(filename); if (!f.exists()) return 0; f.open(QFile::ReadOnly); QTextStream tstream(&f); QHash lookup; lookup["Sn"]=STR_PROP_Serial; lookup["Mn"]=STR_PROP_ModelNumber; 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"; // Delay Pressure lookup["Dt"]="RampTime"; // Delay Time //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"; // Inhale Rounding (0-5) lookup["Re"]="SmartFlexERnd"; // Exhale Rounding (0-5) //lookup["Bu"]="Bu"; //WF //lookup["Ie"]="Ie"; //20 //lookup["Se"]="Se"; //05 //lookup["Si"]="Si"; //05 //lookup["Mi"]="Mi"; //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["Ks=s"]="Ks_s"; lookup["Ks=i"]="Ks_i"; QHash set1; QHash::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(STR_PROP_Serial)) { mach=CreateMachine(set1[STR_PROP_Serial],profile); } if (!mach) { qDebug() << "Couldn't get Intellipap machine record"; return 0; } // Refresh properties data.. for (QHash::iterator i=set1.begin();i!=set1.end();i++) { mach->properties[i.key()]=i.value(); } mach->properties[STR_PROP_Model]=STR_MACH_Intellipap+" "+mach->properties[STR_PROP_ModelNumber]; f.close(); ////////////////////////// // Parse the Session Index ////////////////////////// unsigned char buf[27]; filename=newpath+"/U"; f.setFileName(filename); if (!f.exists()) return 0; QVector SessionStart; QVector SessionEnd; QHash Sessions; quint32 ts1, ts2;//, length; //unsigned char cs; f.open(QFile::ReadOnly); int cnt=0; QDateTime epoch(QDate(2002,1,1),QTime(0,0,0),Qt::UTC); // Intellipap Epoch int ep=epoch.toTime_t(); do { cnt=f.read((char *)buf,9); ts1=(buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; ts2=(buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; ts1+=ep; ts2+=ep; SessionStart.append(ts1); SessionEnd.append(ts2); //cs=buf[8]; } while (cnt>0); qDebug() << "U file logs" << SessionStart.size() << "sessions."; f.close(); ////////////////////////// // Parse the Session Data ////////////////////////// filename=newpath+"/L"; f.setFileName(filename); if (!f.exists()) return 0; 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; } Session *sess; SessionID sid; for (int i=0;iSessionExists(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(INTELLIPAP_Unknown1,EVL_Event); sess->AddEventList(INTELLIPAP_Unknown2,EVL_Event); sess->AddEventList(CPAP_LeakTotal,EVL_Event); sess->AddEventList(CPAP_MaxLeak,EVL_Event); sess->AddEventList(CPAP_TidalVolume,EVL_Event); sess->AddEventList(CPAP_MinuteVent,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=(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); // current pressure sess->eventlist[CPAP_EPAP][0]->AddEvent(time,m_buffer[pos+0x13]/10.0); // epap / low sess->eventlist[CPAP_IPAP][0]->AddEvent(time,m_buffer[pos+0x14]/10.0); // ipap / high sess->eventlist[CPAP_LeakTotal][0]->AddEvent(time,m_buffer[pos+0x7]); // "Average Leak" sess->eventlist[CPAP_MaxLeak][0]->AddEvent(time,m_buffer[pos+0x6]); // "Max Leak" int rr=m_buffer[pos+0xa]; sess->eventlist[CPAP_RespRate][0]->AddEvent(time,rr); // Respiratory Rate sess->eventlist[INTELLIPAP_Unknown1][0]->AddEvent(time,m_buffer[pos+0xf]); // sess->eventlist[INTELLIPAP_Unknown1][0]->AddEvent(time,m_buffer[pos+0xc]); sess->eventlist[CPAP_Snore][0]->AddEvent(time,m_buffer[pos+0x4]); //4/5?? // 0x0f == Leak Event // 0x04 == Snore? if (m_buffer[pos+0xf]>0) { // Leak Event if (!sess->eventlist.contains(CPAP_LeakFlag)) { sess->AddEventList(CPAP_LeakFlag,EVL_Event); } sess->eventlist[CPAP_LeakFlag][0]->AddEvent(time,m_buffer[pos+0xf]); } if (m_buffer[pos+0x5]>4) { // This matches Exhale Puff.. not sure why 4 if (!sess->eventlist.contains(CPAP_ExP)) { sess->AddEventList(CPAP_ExP,EVL_Event); } for (int q=0;qeventlist[CPAP_ExP][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); } for (int q=0;qeventlist[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); } for (int q=0;qeventlist[CPAP_Hypopnea][0]->AddEvent(time,m_buffer[pos+0x11]); } if (m_buffer[pos+0x12]>0) { // NRI // is this == to RERA?? CA?? if (!sess->eventlist.contains(CPAP_NRI)) { sess->AddEventList(CPAP_NRI,EVL_Event); } for (int q=0;qeventlist[CPAP_NRI][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); EventDataType mv=tv*rr; // MinuteVent=TidalVolume * Respiratory Rate sess->eventlist[CPAP_MinuteVent][0]->AddEvent(time,mv/1000.0); break; } } pos+=26; } for (int i=0;ieventlist.size()==0) { // delete sess; // continue; //} quint64 first=qint64(sid)*1000L; quint64 last=qint64(SessionEnd[i])*1000L; sess->settings[CPAP_PresReliefType]=(PRTypes)PR_SMARTFLEX; int i=set1["SmartFlex"].toInt(); sess->settings[CPAP_PresReliefSet]=i; 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); if (max==min) { sess->settings[CPAP_Mode]=(int)MODE_CPAP; sess->settings[CPAP_Pressure]=min; } else { sess->settings[CPAP_Mode]=(int)MODE_APAP; sess->settings[CPAP_PressureMin]=min; sess->settings[CPAP_PressureMax]=max; } sess->eventlist.erase(sess->eventlist.find(CPAP_IPAP)); sess->eventlist.erase(sess->eventlist.find(CPAP_EPAP)); sess->m_min.erase(sess->m_min.find(CPAP_EPAP)); sess->m_max.erase(sess->m_max.find(CPAP_EPAP)); if (pressettings[CPAP_RampPressure]=pres; } //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[STR_PROP_DataVersion]=QString().sprintf("%i",intellipap_data_version); mach->Save(); delete [] m_buffer; if (qprogress) qprogress->setValue(100); f.close(); return 1; } Machine *IntellipapLoader::CreateMachine(QString serial,Profile *profile) { if (!profile) return NULL; qDebug() << "Create Machine " << serial; QList ml=profile->GetMachines(MT_CPAP); bool found=false; QList::iterator i; Machine *m=NULL; for (i=ml.begin(); i!=ml.end(); i++) { if (((*i)->GetClass()==intellipap_class_name) && ((*i)->properties[STR_PROP_Serial]==serial)) { MachList[serial]=*i; //static_cast(*i); found=true; m=*i; break; } } if (!found) { m=new Intellipap(profile,0); } m->properties[STR_PROP_Brand]="DeVilbiss"; m->properties[STR_PROP_Series]=STR_MACH_Intellipap; if (found) return m; MachList[serial]=m; profile->AddMachine(m); m->properties[STR_PROP_Serial]=serial; m->properties[STR_PROP_DataVersion]=QString::number(intellipap_data_version); QString path="{"+STR_GEN_DataFolder+"}/"+m->GetClass()+"_"+serial+"/"; m->properties[STR_PROP_Path]=path; m->properties[STR_PROP_BackupPath]=path+"Backup/"; return m; } bool intellipap_initialized=false; void IntellipapLoader::Register() { if (intellipap_initialized) return; qDebug() << "Registering IntellipapLoader"; RegisterLoader(new IntellipapLoader()); //InitModelMap(); intellipap_initialized=true; }