/******************************************************************** SleepLib Session Implementation This stuff contains the base calculation smarts Copyright (c)2011 Mark Watkins License: GPL *********************************************************************/ #include "session.h" #include "math.h" #include #include #include #include #include using namespace std; Session::Session(Machine * m,SessionID session) { if (!session) { session=m->CreateSessionID(); } s_machine=m; s_session=session; s_changed=false; s_events_loaded=false; s_waves_loaded=false; _first_session=true; s_first=s_last=0; s_wavefile=""; s_eventfile=""; } Session::~Session() { TrashEvents(); TrashWaveforms(); } double Session::min_event_field(MachineCode mc,int field) { if (events.find(mc)==events.end()) return 0; bool first=true; double min=0; vector::iterator i; for (i=events[mc].begin(); i!=events[mc].end(); i++) { assert(field<(*i)->e_fields); if (first) { first=false; min=(*(*i))[field]; } else { if (min>(*(*i))[field]) min=(*(*i))[field]; } } return min; } double Session::max_event_field(MachineCode mc,int field) { if (events.find(mc)==events.end()) return 0; bool first=true; double max=0; vector::iterator i; for (i=events[mc].begin(); i!=events[mc].end(); i++) { assert(field<(*i)->e_fields); if (first) { first=false; max=(*(*i))[field]; } else { if (max<(*(*i))[field]) max=(*(*i))[field]; } } return max; } double Session::sum_event_field(MachineCode mc,int field) { if (events.find(mc)==events.end()) return 0; double sum=0; vector::iterator i; for (i=events[mc].begin(); i!=events[mc].end(); i++) { assert(field<(*i)->e_fields); sum+=(*(*i))[field]; } return sum; } double Session::avg_event_field(MachineCode mc,int field) { if (events.find(mc)==events.end()) return 0; double sum=0; int cnt=0; vector::iterator i; for (i=events[mc].begin(); i!=events[mc].end(); i++) { assert(field<(*i)->e_fields); sum+=(*(*i))[field]; cnt++; } return sum/cnt; } bool sortfunction (double i,double j) { return (i array; vector::iterator e; for (e=events[mc].begin(); e!=events[mc].end(); e++) { Event & ev = *(*e); array.push_back(ev[field]); } std::sort(array.begin(),array.end(),sortfunction); int size=array.size(); double i=size*percent; double t; double q=modf(i,&t); int j=t; if (j>=size-1) return array[j]; double a=array[j-1]; double b=array[j]; if (a==b) return a; //double c=(b-a); //double d=c*q; return array[j]+q; } double Session::weighted_avg_event_field(MachineCode mc,int field) { if (events.find(mc)==events.end()) return 0; int cnt=0; bool first=true; qint64 last; int lastval=0,val; const int max_slots=2600; qint64 vtime[max_slots]={0}; double mult; if ((mc==CPAP_Pressure) || (mc==CPAP_EAP) || (mc==CPAP_IAP)) { mult=10.0; } else mult=10.0; vector::iterator i; for (i=events[mc].begin(); i!=events[mc].end(); i++) { Event & e =(*(*i)); val=e[field]*mult; assert(fieldmax_slots) { qWarning("max_slots to small in Session::weighted_avg_event_fied()"); return 0; } vtime[lastval]+=d; } cnt++; last=e.e_time; lastval=val; } qint64 total; for (int i=0; i 0) { s0=(vtime[i]/3600.0); s1+=i*s0; s2+=s0; } } return (s1/total)/mult; } void Session::AddEvent(Event * e) { events[e->code()].push_back(e); if (s_first) { if (e->time()time(); if (e->time()>s_last) s_last=e->time(); } else { s_first=s_last=e->time(); //_first_session=false; } //qDebug((e->time().toString("yyyy-MM-dd HH:mm:ss")+" %04i %02i").toLatin1(),e->code(),e->fields()); } void Session::AddWaveform(Waveform *w) { waveforms[w->code()].push_back(w); if (!s_last) { if (w->start()start(); if (w->end()>s_last) s_last=w->end(); } else { s_first=w->start(); s_last=w->end(); } // Could parse the flow data for Breaths per minute info here.. } void Session::TrashEvents() // Trash this sessions Events and release memory. { map >::iterator i; vector:: iterator j; for (i=events.begin(); i!=events.end(); i++) { for (j=i->second.begin(); j!=i->second.end(); j++) { delete *j; } } events.clear(); } void Session::TrashWaveforms() // Trash this sessions Waveforms and release memory. { map >::iterator i; vector:: iterator j; for (i=waveforms.begin(); i!=waveforms.end(); i++) { for (j=i->second.begin(); j!=i->second.end(); j++) { delete *j; } } waveforms.clear(); } //const int max_pack_size=128; bool Session::Store(QString path) // Storing Session Data in our format // {DataDir}/{MachineID}/{SessionID}.{ext} { QDir dir(path); if (!dir.exists(path)) dir.mkpath(path); QString base; base.sprintf("%08lx",s_session); base=path+"/"+base; //qDebug(("Storing Session: "+base).toLatin1()); bool a,b,c; a=StoreSummary(base+".000"); // if actually has events if (events.size()>0) b=StoreEvents(base+".001"); if (waveforms.size()>0) c=StoreWaveforms(base+".002"); if (a) { s_changed=false; } return a; } const quint32 magic=0xC73216AB; bool Session::StoreSummary(QString filename) { BinaryFile f; f.Open(filename,BF_WRITE); f.Pack((quint32)magic); // Magic Number f.Pack((quint32)s_machine->id()); // Machine ID f.Pack((quint32)s_session); // Session ID f.Pack((quint16)0); // File Type 0 == Summary File f.Pack((quint16)0); // File Version quint32 starttime=s_first/1000L; quint32 duration=(s_last-s_first)/1000L; f.Pack(starttime); // Session Start Time f.Pack(duration); // Duration of sesion in seconds. f.Pack((quint16)summary.size()); map mctype; // First output the Machine Code and type for each summary record map::iterator i; for (i=summary.begin(); i!=summary.end(); i++) { MachineCode mc=i->first; QVariant::Type type=i->second.type(); // Urkk.. this is a mess. if (type==QVariant::Bool) { mctype[mc]=MC_bool; } else if (type==QVariant::Int) { mctype[mc]=MC_int; } else if (type==QVariant::LongLong) { mctype[mc]=MC_long; } else if (type==QVariant::Double) { mctype[mc]=MC_double; } else if (type==QVariant::String) { mctype[mc]=MC_string; } else if (type==QVariant::DateTime) { mctype[mc]=MC_datetime; } else { QString t=i->second.typeToName(type); qWarning() << "Error in Session->StoreSummary: Can't pack variant type " << t; exit(1); } f.Pack((qint16)mc); f.Pack((qint8)mctype[mc]); } // Then dump out the actual data, according to format. for (i=summary.begin(); i!=summary.end(); i++) { MachineCode mc=i->first; if (mctype[mc]==MC_bool) { f.Pack((qint8)i->second.toBool()); } else if (mctype[mc]==MC_int) { f.Pack((qint32)i->second.toInt()); } else if (mctype[mc]==MC_long) { f.Pack((qint64)i->second.toLongLong()); } else if (mctype[mc]==MC_double) { f.Pack((double)i->second.toDouble()); } else if (mctype[mc]==MC_string) { f.Pack(i->second.toString()); } else if (mctype[mc]==MC_datetime) { f.Pack(i->second.toDateTime()); } } f.Close(); return true; } bool Session::LoadSummary(QString filename) { if (filename.isEmpty()) return false; //qDebug(("Loading Summary "+filename).toLatin1()); BinaryFile f; if (!f.Open(filename,BF_READ)) { qDebug() << "Couldn't open file" << filename; return false; } quint64 t64; quint32 t32; quint16 t16; quint8 t8; qint16 sumsize; map mctype; vector mcorder; if (!f.Unpack(t32)) throw UnpackError(); // Magic Number if (t32!=magic) throw UnpackError(); if (!f.Unpack(t32)) throw UnpackError(); // MachineID if (!f.Unpack(t32)) throw UnpackError(); // Sessionid; s_session=t32; if (!f.Unpack(t16)) throw UnpackError(); // File Type if (t16!=0) throw UnpackError(); //wrong file type if (!f.Unpack(t16)) throw UnpackError(); // File Version // dont care yet if (!f.Unpack(t32)) throw UnpackError(); // Start time s_first=qint64(t32)*1000L; if (!f.Unpack(t32)) throw UnpackError(); // Duration // (16bit==Limited to 18 hours) s_last=s_first+qint64(t32)*1000L; if (!f.Unpack(sumsize)) throw UnpackError(); // Summary size (number of Machine Code lists) for (int i=0; iid()); // Machine ID f.Pack((quint32)s_session); // This session's ID f.Pack((quint16)1); // File type 1 == Event f.Pack((quint16)0); // File Version quint32 starttime=s_first/1000L; quint32 duration=(s_last-s_first)/1000L; f.Pack(starttime); f.Pack(duration); f.Pack((qint16)events.size()); // Number of event categories map >::iterator i; vector::iterator j; for (i=events.begin(); i!=events.end(); i++) { f.Pack((qint16)i->first); // MachineID f.Pack((qint16)i->second.size()); // count of events in this category j=i->second.begin(); f.Pack((qint8)(*j)->fields()); // number of data fields in this event type } bool first; float tf; qint64 last=0,eventtime,delta; for (i=events.begin(); i!=events.end(); i++) { first=true; for (j=i->second.begin(); j!=i->second.end(); j++) { eventtime=(*j)->time(); if (first) { f.Pack((*j)->time()); first=false; } else { delta=eventtime-last; if (delta>0xffffffff) { qDebug("StoreEvent: Delta too big.. needed to use bigger value"); exit(1); } f.Pack((quint32)delta); } for (int k=0; k<(*j)->fields(); k++) { tf=(*(*j))[k]; f.Pack((float)tf); } last=eventtime; } } f.Close(); return true; } bool Session::LoadEvents(QString filename) { if (filename.isEmpty()) return false; BinaryFile f; if (!f.Open(filename,BF_READ)) { qDebug() << "Couldn't open events file" << filename; return false; } quint32 t32; quint16 t16; quint8 t8; //qint16 i16; // qint16 sumsize; if (!f.Unpack(t32)) throw UnpackError(); // Magic Number if (t32!=magic) throw UnpackError(); if (!f.Unpack(t32)) throw UnpackError(); // MachineID if (!f.Unpack(t32)) throw UnpackError(); // Sessionid; s_session=t32; if (!f.Unpack(t16)) throw UnpackError(); // File Type if (t16!=1) throw UnpackError(); //wrong file type if (!f.Unpack(t16)) throw UnpackError(); // File Version // dont give a crap yet.. if (!f.Unpack(t32)) throw UnpackError(); // Start time s_first=qint64(t32)*1000L; if (!f.Unpack(t32)) throw UnpackError(); // Duration // (16bit==Limited to 18 hours) s_last=s_first+qint64(t32)*1000L; qint16 evsize; if (!f.Unpack(evsize)) throw UnpackError(); // Summary size (number of Machine Code lists) map mcsize; map mcfields; vector mcorder; MachineCode mc; for (int i=0; iid()); // Machine ID f.Pack((quint32)s_session); // This session's ID f.Pack((quint16)2); // File type 2 == Waveform f.Pack((quint16)0); // File Version quint32 starttime=s_first/1000L; quint32 duration=(s_last-s_first)/1000L; f.Pack(starttime); f.Pack(duration); f.Pack((qint16)waveforms.size()); // Number of different waveforms map >::iterator i; vector::iterator j; for (i=waveforms.begin(); i!=waveforms.end(); i++) { f.Pack((qint16)i->first); // Machine Code t16=i->second.size(); f.Pack(t16); // Number of (hopefully non-linear) waveform chunks for (j=i->second.begin(); j!=i->second.end(); j++) { Waveform &w=*(*j); // 64bit number.. f.Pack(w.start()); // Start time of first waveform chunk //qint32 samples; //double seconds; f.Pack((qint32)w.samples()); // Total number of samples f.Pack((qint64)w.duration()); // Total number of seconds f.Pack((qint16)w.min()); f.Pack((qint16)w.max()); f.Pack((qint8)sizeof(SampleFormat)); // Bytes per sample f.Pack((qint8)0); // signed.. all samples for now are signed 16bit. //t8=0; // 0=signed, 1=unsigned, 2=float // followed by sample data. if (IsPlatformLittleEndian()) { f.Write((const char *)w.GetBuffer(),w.samples()*sizeof(SampleFormat)); } else { for (int k=0; k<(*j)->samples(); k++) f.Pack((qint16)w[k]); } } } return true; } bool Session::LoadWaveforms(QString filename) { if (filename.isEmpty()) return false; BinaryFile f; if (!f.Open(filename,BF_READ)) { qDebug() << "Couldn't open waveform file " << filename; return false; } quint32 t32; quint16 t16; quint8 t8; if (!f.Unpack(t32)) throw UnpackError(); // Magic Number if (t32!=magic) throw UnpackError(); if (!f.Unpack(t32)) throw UnpackError(); // MachineID if (!f.Unpack(t32)) throw UnpackError(); // Sessionid; s_session=t32; if (!f.Unpack(t16)) throw UnpackError(); // File Type if (t16!=2) throw UnpackError(); //wrong file type? if (!f.Unpack(t16)) throw UnpackError(); // File Version // dont give a crap yet.. if (!f.Unpack(t32)) throw UnpackError(); // Start time s_first=qint64(t32)*1000; if (!f.Unpack(t32)) throw UnpackError(); // Duration // (16bit==Limited to 18 hours) s_last=s_first+qint64(t32)*1000; qint16 wvsize; if (!f.Unpack(wvsize)) throw UnpackError(); // Summary size (number of Machine Code lists) MachineCode mc; qint64 date; qint64 seconds; qint32 samples; int chunks; SampleFormat min,max; for (int i=0; i