/* SleepLib Session Implementation This stuff contains the base calculation smarts Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net> License: GPL */ #include "session.h" #include <cmath> #include <QDir> #include <QDebug> #include <QMessageBox> #include <QMetaType> #include <algorithm> #include "SleepLib/calcs.h" #include "SleepLib/profiles.h" using namespace std; const quint16 filetype_summary=0; 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=11; const quint16 events_version=10; Session::Session(Machine * m,SessionID session) { if (!session) { session=m->CreateSessionID(); } s_machine=m; s_session=session; s_changed=false; s_events_loaded=false; _first_session=true; s_enabled=-1; s_first=s_last=0; s_eventfile=""; s_evchecksum_checked=false; } Session::~Session() { TrashEvents(); } void Session::TrashEvents() // Trash this sessions Events and release memory. { QHash<ChannelID,QVector<EventList *> >::iterator i; QVector<EventList *>::iterator j; for (i=eventlist.begin(); i!=eventlist.end(); i++) { for (j=i.value().begin(); j!=i.value().end(); j++) { delete *j; } } s_events_loaded=false; eventlist.clear(); } //const int max_pack_size=128; bool Session::OpenEvents() { if (s_events_loaded) return true; s_events_loaded=eventlist.size() > 0; if (s_events_loaded) return true; if (!s_eventfile.isEmpty()) { bool b=LoadEvents(s_eventfile); if (!b) { qWarning() << "Error Unpacking Events" << s_eventfile; return false; } } return s_events_loaded=true; } 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; bool a; a=StoreSummary(base+".000"); // if actually has events //qDebug() << " Summary done"; if (eventlist.size()>0) { s_eventfile=base+".001"; StoreEvents(s_eventfile); } else { // who cares.. //qDebug() << "Trying to save empty events file"; } //qDebug() << " Events done"; s_changed=false; s_events_loaded=true; //TrashEvents(); //} else { // qDebug() << "Session::Store() No event data saved" << s_session; //} return a; } bool Session::StoreSummary(QString filename) { QFile file(filename); file.open(QIODevice::WriteOnly); QDataStream out(&file); out.setVersion(QDataStream::Qt_4_6); out.setByteOrder(QDataStream::LittleEndian); out << (quint32)magic; out << (quint16)summary_version; out << (quint16)filetype_summary; out << (quint32)s_machine->id(); out << (quint32)s_session; out << s_first; // Session Start Time out << s_last; // Duration of sesion in seconds. //out << (quint16)settings.size(); out << settings; out << m_cnt; out << m_sum; out << m_avg; out << m_wavg; out << m_min; out << m_max; out << m_cph; out << m_sph; out << m_firstchan; out << m_lastchan; out << m_valuesummary; out << m_timesummary; out << m_gain; file.close(); return true; } bool Session::LoadSummary(QString filename) { if (filename.isEmpty()) { qDebug() << "Empty summary filename"; return false; } QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Couldn't open summary file" << filename; return false; } QDataStream in(&file); in.setVersion(QDataStream::Qt_4_6); in.setByteOrder(QDataStream::LittleEndian); quint32 t32; quint16 t16; //QHash<ChannelID,MCDataType> mctype; //QVector<ChannelID> mcorder; in >> t32; if (t32!=magic) { qDebug() << "Wrong magic number in " << filename; return false; } quint16 version; in >> version; // DB Version if (version<6) { //throw OldDBVersion(); qWarning() << "Old dbversion "<< version << "summary file.. Sorry, you need to purge and reimport"; return false; } in >> t16; // File Type if (t16!=filetype_summary) { qDebug() << "Wrong file type"; //wrong file type return false; } qint32 ts32; in >> ts32; // MachineID (dont need this result) if (ts32!=s_machine->id()) { qWarning() << "Machine ID does not match in" << filename << " I will try to load anyway in case you know what your doing."; } in >> t32; // Sessionid; s_session=t32; in >> s_first; // Start time in >> s_last; // Duration // (16bit==Limited to 18 hours) QHash<ChannelID,EventDataType> cruft; if (version<7) { QHash<QString,QVariant> v1; in >> v1; settings.clear(); ChannelID code; for (QHash<QString,QVariant>::iterator i=v1.begin();i!=v1.end();i++) { code=schema::channel[i.key()].id(); settings[code]=i.value(); } QHash<QString,int> zcnt; in >> zcnt; for (QHash<QString,int>::iterator i=zcnt.begin();i!=zcnt.end();i++) { code=schema::channel[i.key()].id(); m_cnt[code]=i.value(); } QHash<QString,double> zsum; in >> zsum; for (QHash<QString,double>::iterator i=zsum.begin();i!=zsum.end();i++) { code=schema::channel[i.key()].id(); m_cnt[code]=i.value(); } QHash<QString,EventDataType> ztmp; in >> ztmp; // avg for (QHash<QString,EventDataType>::iterator i=ztmp.begin();i!=ztmp.end();i++) { code=schema::channel[i.key()].id(); m_avg[code]=i.value(); } ztmp.clear(); in >> ztmp; // wavg for (QHash<QString,EventDataType>::iterator i=ztmp.begin();i!=ztmp.end();i++) { code=schema::channel[i.key()].id(); m_wavg[code]=i.value(); } ztmp.clear(); in >> ztmp; // 90p ztmp.clear(); in >> ztmp; // min for (QHash<QString,EventDataType>::iterator i=ztmp.begin();i!=ztmp.end();i++) { code=schema::channel[i.key()].id(); m_min[code]=i.value(); } ztmp.clear(); in >> ztmp; // max for (QHash<QString,EventDataType>::iterator i=ztmp.begin();i!=ztmp.end();i++) { code=schema::channel[i.key()].id(); m_max[code]=i.value(); } ztmp.clear(); in >> ztmp; // cph for (QHash<QString,EventDataType>::iterator i=ztmp.begin();i!=ztmp.end();i++) { code=schema::channel[i.key()].id(); m_cph[code]=i.value(); } ztmp.clear(); in >> ztmp; // sph for (QHash<QString,EventDataType>::iterator i=ztmp.begin();i!=ztmp.end();i++) { code=schema::channel[i.key()].id(); m_sph[code]=i.value(); } QHash<QString,quint64> ztim; in >> ztim; //firstchan for (QHash<QString,quint64>::iterator i=ztim.begin();i!=ztim.end();i++) { code=schema::channel[i.key()].id(); m_firstchan[code]=i.value(); } ztim.clear(); in >> ztim; // lastchan for (QHash<QString,quint64>::iterator i=ztim.begin();i!=ztim.end();i++) { code=schema::channel[i.key()].id(); m_lastchan[code]=i.value(); } //SetChanged(true); } else { in >> settings; in >> m_cnt; in >> m_sum; in >> m_avg; in >> m_wavg; if (version < 11) { cruft.clear(); in >> cruft; // 90% if (version >= 10) { cruft.clear(); in >> cruft;// med cruft.clear(); in >> cruft; //p95 } } in >> m_min; in >> m_max; in >> m_cph; in >> m_sph; in >> m_firstchan; in >> m_lastchan; if (version >= 8) { in >> m_valuesummary; in >> m_timesummary; if (version >= 9) { in >> m_gain; } } } if (version<summary_version) { qDebug() << "Upgrading Summary file to version" << summary_version; UpdateSummaries(); StoreSummary(filename); } return true; } const quint16 compress_method=1; bool Session::StoreEvents(QString filename) { QFile file(filename); file.open(QIODevice::WriteOnly); QByteArray headerbytes; QDataStream header(&headerbytes,QIODevice::WriteOnly); header.setVersion(QDataStream::Qt_4_6); header.setByteOrder(QDataStream::LittleEndian); header << (quint32)magic; // New Magic Number header << (quint16)events_version; // File Version header << (quint16)filetype_data; // File type 1 == Event header << (quint32)s_machine->id();// Machine Type header << (quint32)s_session; // This session's ID header << s_first; header << s_last; quint16 compress=0; if (p_profile->session->compressSessionData()) compress=compress_method; header << (quint16)compress; header << (quint16)s_machine->GetType();// Machine Type QByteArray databytes; QDataStream out(&databytes,QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_6); out.setByteOrder(QDataStream::LittleEndian); out << (qint16)eventlist.size(); // Number of event categories QHash<ChannelID,QVector<EventList *> >::iterator i; for (i=eventlist.begin(); i!=eventlist.end(); i++) { out << i.key(); // ChannelID out << (qint16)i.value().size(); for (int j=0;j<i.value().size();j++) { EventList &e=*i.value()[j]; out << e.first(); out << e.last(); out << (qint32)e.count(); out << (qint8)e.type(); out << e.rate(); out << e.gain(); out << e.offset(); out << e.Min(); out << e.Max(); out << e.dimension(); out << e.hasSecondField(); if (e.hasSecondField()) { out << e.min2(); out << e.max2(); } } } for (i=eventlist.begin(); i!=eventlist.end(); i++) { for (int j=0;j<i.value().size();j++) { EventList &e=*i.value()[j]; // ****** This is assuming little endian ****** // Store the raw event list data in EventStoreType (16bit short) EventStoreType *ptr=e.m_data.data(); out.writeRawData((char *)ptr,e.count() << 1); //*** Don't delete these comments *** // for (quint32 c=0;c<e.count();c++) { // out << *ptr++;//e.raw(c); // } // Store the second field, only if there if (e.hasSecondField()) { ptr=e.m_data2.data(); out.writeRawData((char *)ptr,e.count() << 1); //*** Don't delete these comments *** // for (quint32 c=0;c<e.count();c++) { // out << *ptr++; //e.raw2(c); // } } // Store the time delta fields for non-waveform EventLists if (e.type()!=EVL_Waveform) { quint32 * tptr=e.m_time.data(); out.writeRawData((char *)tptr,e.count() << 2); //*** Don't delete these comments *** // for (quint32 c=0;c<e.count();c++) { // out << *tptr++; //e.getTime()[c]; // } } } } qint32 datasize=databytes.size(); // Checksum the _uncompressed_ data quint16 chk=qChecksum(databytes.data(),databytes.size()); header << datasize; header << chk; QByteArray data; if (compress>0) { data=qCompress(databytes); } else { data=databytes; } file.write(headerbytes); file.write(data); file.close(); return true; } bool Session::LoadEvents(QString filename) { quint32 magicnum,machid,sessid; quint16 version,type,crc16,machtype,compmethod; quint8 t8; qint32 datasize; if (filename.isEmpty()) { qDebug() << "Session::LoadEvents() Filename is empty"; return false; } QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Couldn't open file" << filename; return false; } QByteArray headerbytes=file.read(42); QDataStream header(headerbytes); header.setVersion(QDataStream::Qt_4_6); header.setByteOrder(QDataStream::LittleEndian); header >> magicnum; // Magic Number (quint32) header >> version; // Version (quint16) header >> type; // File type (quint16) header >> machid; // Machine ID (quint32) header >> sessid; //(quint32) header >> s_first; //(qint64) header >> s_last; //(qint64) if (type!=filetype_data) { qDebug() << "Wrong File Type in " << filename; return false; } if (magicnum!=magic) { qWarning() << "Wrong Magic number in " << filename; return false; } if (version<6) { // prior to version 6 is too old to deal with qDebug() << "Old File Version, can't open file"; return false; } if (version<10) { file.seek(32); } else { header >> compmethod; // Compression Method (quint16) header >> machtype; // Machine Type (quint16) header >> datasize; // Size of Uncompressed Data (quint32) header >> crc16; // CRC16 of Uncompressed Data (quint16) } QByteArray databytes,temp=file.readAll(); file.close(); if (version>=10) { if (compmethod>0) { databytes=qUncompress(temp); if (!s_evchecksum_checked) { if (databytes.size()!=datasize) { qDebug() << "File" << filename << "has returned wrong datasize"; return false; } quint16 crc=qChecksum(databytes.data(),databytes.size()); if (crc!=crc16) { qDebug() << "CRC Doesn't match in" << filename; return false; } s_evchecksum_checked=true; } } else { databytes=temp; } } else databytes=temp; QDataStream in(databytes); in.setVersion(QDataStream::Qt_4_6); in.setByteOrder(QDataStream::LittleEndian); qint16 mcsize; in >> mcsize; // number of Machine Code lists ChannelID code; qint64 ts1,ts2; qint32 evcount; EventListType elt; EventDataType rate,gain,offset,mn,mx; qint16 size2; QVector<ChannelID> mcorder; QVector<qint16> sizevec; QString dim; for (int i=0;i<mcsize;i++) { if (version<8) { QString txt; in >> txt; code=schema::channel[txt].id(); } else { in >> code; } mcorder.push_back(code); in >> size2; sizevec.push_back(size2); for (int j=0;j<size2;j++) { in >> ts1; in >> ts2; in >> evcount; in >> t8; elt=(EventListType)t8; in >> rate; in >> gain; in >> offset; in >> mn; in >> mx; in >> dim; bool second_field=false; if (version>=7) // version 7 added this field in >> second_field; EventList *elist=AddEventList(code,elt,gain,offset,mn,mx,rate,second_field); elist->setDimension(dim); //eventlist[code].push_back(elist); elist->m_count=evcount; elist->m_first=ts1; elist->m_last=ts2; if (second_field) { EventDataType min,max; in >> min; in >> max; elist->setMin2(min); elist->setMax2(max); } } } //EventStoreType t; //quint32 x; for (int i=0;i<mcsize;i++) { code=mcorder[i]; size2=sizevec[i]; for (int j=0;j<size2;j++) { EventList &evec=*eventlist[code][j]; evec.m_data.resize(evec.m_count); EventStoreType *ptr=evec.m_data.data(); // ****** This is assuming little endian ****** in.readRawData((char *)ptr, evec.m_count << 1); //*** Don't delete these comments *** // for (quint32 c=0;c<evec.m_count;c++) { // in >> t; // *ptr++=t; // } if (evec.hasSecondField()) { evec.m_data2.resize(evec.m_count); ptr=evec.m_data2.data(); in.readRawData((char *)ptr,evec.m_count << 1); //*** Don't delete these comments *** // for (quint32 c=0;c<evec.m_count;c++) { // in >> t; // *ptr++=t; // } } if (evec.type()!=EVL_Waveform) { evec.m_time.resize(evec.m_count); quint32 * tptr=evec.m_time.data(); in.readRawData((char *)tptr,evec.m_count << 2); //*** Don't delete these comments *** // for (quint32 c=0;c<evec.m_count;c++) { // in >> x; // *tptr++=x; // } } } } if (version<events_version) { qDebug() << "Upgrading Events file" << filename << "to version" << events_version; UpdateSummaries(); StoreEvents(filename); } return true; } void Session::updateCountSummary(ChannelID code) { QHash<ChannelID,QVector<EventList *> >::iterator ev=eventlist.find(code); if (ev==eventlist.end()) return; QHash<ChannelID,QHash<EventStoreType, EventStoreType> >::iterator vs=m_valuesummary.find(code); if (vs!=m_valuesummary.end()) // already calculated? return; QHash<EventStoreType, EventStoreType> valsum; QHash<EventStoreType, quint32> timesum; EventDataType raw,lastraw=0; qint64 start,time,lasttime=0; qint32 len,cnt; quint32 * tptr; EventStoreType * dptr; for (int i=0;i<ev.value().size();i++) { EventList & e=*(ev.value()[i]); start=e.first(); cnt=e.count(); dptr=e.rawData(); EventDataType rate=0; m_gain[code]=e.gain(); if (e.type()==EVL_Event) { lastraw=*dptr; tptr=e.rawTime(); lasttime=start + *tptr; // Event version for (int j=0;j<cnt;j++) { time=start + *tptr++; raw=*dptr++; valsum[raw]++; // elapsed time in seconds since last event occurred len=(time-lasttime) / 1000L; timesum[lastraw]+=len; lastraw=raw; lasttime=time; } } else { // Waveform version, first just count for (int j=0;j<cnt;j++) { raw=*dptr++; valsum[raw]++; } // Then process the list of values, time is simply (rate * count) rate=e.rate(); EventDataType t; for (QHash<EventStoreType, EventStoreType>::iterator it=valsum.begin(); it!=valsum.end(); it++) { t=EventDataType(it.value())*rate; timesum[it.key()]+=t; } } } if (valsum.size()==0) return; m_valuesummary[code]=valsum; m_timesummary[code]=timesum; } void Session::UpdateSummaries() { ChannelID id; QHash<ChannelID,QVector<EventList *> >::iterator c; calcAHIGraph(this); // Calculates RespRate and related waveforms (Tv, MV, Te, Ti) if missing calcRespRate(this); calcLeaks(this); calcSPO2Drop(this); calcPulseChange(this); for (c=eventlist.begin();c!=eventlist.end();c++) { id=c.key(); if (schema::channel[id].type()==schema::DATA) { //sum(id); // avg calculates this and cnt. if (c.value().size()>0) { EventList * el=c.value()[0]; EventDataType gain=el->gain(); m_gain[id]=gain; } if (!((id==CPAP_FlowRate) || (id==CPAP_MaskPressureHi) || (id==CPAP_RespEvent) || (id==CPAP_MaskPressure))) updateCountSummary(id); Min(id); Max(id); count(id); last(id); first(id); if (((id==CPAP_FlowRate) || (id==CPAP_MaskPressureHi) || (id==CPAP_RespEvent) || (id==CPAP_MaskPressure))) continue; cph(id); sph(id); avg(id); wavg(id); } } } bool Session::SearchEvent(ChannelID code, qint64 time, qint64 dist) { qint64 t,start; QHash<ChannelID,QVector<EventList *> >::iterator it; it=eventlist.find(code); quint32 * tptr; int cnt; //qint64 rate; if (it!=eventlist.end()) { for (int i=0;i<it.value().size();i++) { EventList *el=it.value()[i]; // rate=el->rate(); cnt=el->count(); // why would this be necessary??? if (el->type()==EVL_Waveform) { qDebug() << "Called SearchEvent on a waveform object!"; return false; } else { start=el->first(); tptr=el->rawTime(); for (int j=0;j<cnt;j++) { t=start + *tptr++; if (qAbs(time - t) < dist) return true; } } } } return false; } bool Session::enabled() { if (s_enabled>=0) { return s_enabled; } if (!settings.contains(SESSION_ENABLED)) { settings[SESSION_ENABLED]=s_enabled=true; return true; } s_enabled=settings[SESSION_ENABLED].toBool(); return s_enabled; } void Session::setEnabled(bool b) { settings[SESSION_ENABLED]=s_enabled=b; SetChanged(true); } EventDataType Session::Min(ChannelID id) { QHash<ChannelID,EventDataType>::iterator i=m_min.find(id); if (i!=m_min.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { m_min[id]=0; return 0; } QVector<EventList *> & evec=j.value(); bool first=true; EventDataType min=0,t1; for (int i=0;i<evec.size();i++) { if (evec[i]->count()==0) continue; t1=evec[i]->Min(); if (t1==0 && t1==evec[i]->Max()) continue; if (first) { min=t1; first=false; } else { if (min>t1) min=t1; } } m_min[id]=min; return min; } EventDataType Session::Max(ChannelID id) { QHash<ChannelID,EventDataType>::iterator i=m_max.find(id); if (i!=m_max.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { m_max[id]=0; return 0; } QVector<EventList *> & evec=j.value(); bool first=true; EventDataType max=0,t1; for (int i=0;i<evec.size();i++) { if (evec[i]->count()==0) continue; t1=evec[i]->Max(); if (t1==0 && t1==evec[i]->Min()) continue; if (first) { max=t1; first=false; } else { if (max<t1) max=t1; } } m_max[id]=max; return max; } qint64 Session::first(ChannelID id) { QHash<ChannelID,quint64>::iterator i=m_firstchan.find(id); if (i!=m_firstchan.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) return 0; QVector<EventList *> & evec=j.value(); bool first=true; qint64 min=0,t1; for (int i=0;i<evec.size();i++) { t1=evec[i]->first(); if (first) { min=t1; first=false; } else { if (min>t1) min=t1; } } m_firstchan[id]=min; return min; } qint64 Session::last(ChannelID id) { QHash<ChannelID,quint64>::iterator i=m_lastchan.find(id); if (i!=m_lastchan.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) return 0; QVector<EventList *> & evec=j.value(); bool first=true; qint64 max=0,t1; for (int i=0;i<evec.size();i++) { t1=evec[i]->last(); if (first) { max=t1; first=false; } else { if (max<t1) max=t1; } } m_lastchan[id]=max; return max; } bool Session::channelExists(ChannelID id) { if (!enabled()) return false; if (s_events_loaded) { QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) // eventlist not loaded. return false; } else { QHash<ChannelID,int>::iterator q=m_cnt.find(id); if (q==m_cnt.end()) return false; if (q.value()==0) return false; } return true; } int Session::rangeCount(ChannelID id, qint64 first,qint64 last) { QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { return 0; } QVector<EventList *> & evec=j.value(); int sum=0,cnt; qint64 t,start; for (int i=0;i<evec.size();i++) { EventList & ev=*evec[i]; if ((ev.last() < first) || (ev.first() > last)) continue; if (ev.type() == EVL_Waveform) { qint64 et=last; if (et > ev.last()) et=ev.last(); qint64 st=first; if (st < ev.first()) st=ev.first(); t=(et - st) / ev.rate(); sum+=t; } else { cnt=ev.count(); start=ev.first(); quint32 * tptr=ev.rawTime(); for (int j=0;j<cnt;j++) { t=start + *tptr++; if (t >= first) { if (t<=last) { sum++; } else break; } } } } return sum; } double Session::rangeSum(ChannelID id, qint64 first,qint64 last) { QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { return 0; } QVector<EventList *> & evec=j.value(); double sum=0,gain; qint64 t,start; EventStoreType * dptr; quint32 * tptr; int cnt,idx; qint64 rate; for (int i=0;i < evec.size();i++) { EventList & ev=*evec[i]; if ((ev.last() < first) || (ev.first() > last)) continue; start=ev.first(); dptr=ev.rawData(); cnt=ev.count(); gain=ev.gain(); rate=ev.rate(); if (ev.type()==EVL_Waveform) { if (first > ev.first()) { // Skip the samples before first idx=(first - ev.first()) / rate; } t=start; for (int j=idx;j<cnt;j++) { if (t <= last) { sum+=EventDataType(*dptr) * gain; } else break; dptr++; t+=rate; } } else { tptr=ev.rawTime(); for (int j=0;j < cnt;j++) { t=start + *tptr++; if (t >= first) { if (t <= last) { sum+=EventDataType(*dptr) * gain; } else break; } dptr++; } } } return sum; } EventDataType Session::rangeMin(ChannelID id, qint64 first,qint64 last) { QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { return 0; } QVector<EventList *> & evec=j.value(); EventDataType gain,v,min=999999999; qint64 t,start,rate; EventStoreType * dptr; quint32 * tptr; int cnt,idx; for (int i=0;i<evec.size();i++) { EventList & ev=*evec[i]; if ((ev.last() < first) || (ev.first() > last)) continue; dptr=ev.rawData(); start=ev.first(); cnt=ev.count(); gain=ev.gain(); if (ev.type()==EVL_Waveform) { rate=ev.rate(); t=start; idx=0; if (first > ev.first()) { // Skip the samples before first idx=(first - ev.first())/rate; } for (int j=idx;j<cnt;j++) { if (t<=last) { v=EventDataType(*dptr) * gain; if (v<min) min=v; } else break; dptr++; t+=rate; } } else { tptr=ev.rawTime(); for (int j=0;j<cnt;j++) { t=start + *tptr++; if (t >= first) { if (t <= last) { v=EventDataType(*dptr) * gain; if (v<min) min=v; } else break; } dptr++; } } } return min; } EventDataType Session::rangeMax(ChannelID id, qint64 first,qint64 last) { QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { return 0; } QVector<EventList *> & evec=j.value(); EventDataType gain,v,max=-999999999; qint64 t,start,rate; EventStoreType * dptr; quint32 * tptr; int cnt,idx; for (int i=0;i<evec.size();i++) { EventList & ev=*evec[i]; if ((ev.last() < first) || (ev.first() > last)) continue; start=ev.first(); dptr=ev.rawData(); cnt=ev.count(); gain=ev.gain(); if (ev.type()==EVL_Waveform) { rate=ev.rate(); t=start; idx=0; if (first > ev.first()) { // Skip the samples before first idx=(first - ev.first())/rate; } for (int j=idx;j<cnt;j++) { if (t<=last) { v=EventDataType(*dptr) * gain; if (v>max) max=v; } else break; dptr++; t+=rate; } } else { tptr=ev.rawTime(); for (int j=0;j<cnt;j++) { t=start + *tptr++; if (t>=first) { if (t<=last) { v=EventDataType(*dptr) * gain; if (v>max) max=v; } else break; } dptr++; } } } return max; } int Session::count(ChannelID id) { QHash<ChannelID,int>::iterator i=m_cnt.find(id); if (i!=m_cnt.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { m_cnt[id]=0; return 0; } QVector<EventList *> & evec=j.value(); int sum=0; for (int i=0;i<evec.size();i++) { sum+=evec[i]->count(); } m_cnt[id]=sum; return sum; } double Session::sum(ChannelID id) { QHash<ChannelID,double>::iterator i=m_sum.find(id); if (i!=m_sum.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { m_sum[id]=0; return 0; } QVector<EventList *> & evec=j.value(); double gain,sum=0; EventStoreType * dptr; int cnt; for (int i=0;i<evec.size();i++) { EventList & ev=*(evec[i]); gain=ev.gain(); cnt=ev.count(); dptr=ev.rawData(); for (int j=0;j<cnt;j++) { sum+=double(*dptr) * gain; } } m_sum[id]=sum; return sum; } EventDataType Session::avg(ChannelID id) { QHash<ChannelID,EventDataType>::iterator i=m_avg.find(id); if (i!=m_avg.end()) return i.value(); QHash<ChannelID,QVector<EventList *> >::iterator j=eventlist.find(id); if (j==eventlist.end()) { m_avg[id]=0; return 0; } QVector<EventList *> & evec=j.value(); double val=0,gain; int cnt=0; EventStoreType * dptr; int es; for (int i=0;i<evec.size();i++) { EventList & ev=*(evec[i]); dptr=ev.rawData(); gain=ev.gain(); es=ev.count(); for (int j=0;j<es;j++) { val+=double(*dptr++) * gain; cnt++; } } if (cnt>0) { // Shouldn't really happen.. Should aways contain data val/=double(cnt); } m_avg[id]=val; return val; } EventDataType Session::cph(ChannelID id) // count per hour { QHash<ChannelID,EventDataType>::iterator i=m_cph.find(id); if (i!=m_cph.end()) return i.value(); EventDataType val=count(id); val/=hours(); m_cph[id]=val; return val; } EventDataType Session::sph(ChannelID id) // sum per hour, assuming id is a time field in seconds { QHash<ChannelID,EventDataType>::iterator i=m_sph.find(id); if (i!=m_sph.end()) return i.value(); EventDataType val=sum(id)/3600.0; val=100.0 / hours() * val; m_sph[id]=val; return val; } bool sortfunction (EventStoreType i,EventStoreType j) { return (i<j); } EventDataType Session::percentile(ChannelID id,EventDataType percent) { QHash<ChannelID,QVector<EventList *> >::iterator jj=eventlist.find(id); if (jj==eventlist.end()) return 0; QVector<EventList *> & evec=jj.value(); if (percent > 1.0) { qWarning() << "Session::percentile() called with > 1.0"; return 0; } int size=evec.size(); if (size==0) return 0; QVector<EventStoreType> array; EventDataType gain=evec[0]->gain(); EventStoreType * dptr, * sptr; int tt=0,cnt; for (int i=0;i<size;i++) { EventList & ev=*evec[i]; cnt=ev.count(); tt+=cnt; } array.resize(tt); for (int i=0;i<size;i++) { EventList & ev=*evec[i]; sptr=ev.rawData(); dptr=array.data(); for (int j=0;j<cnt;j++) { *dptr++ = * sptr++; //array.push_back(evec[i]->raw(j)); } } int n=array.size()*percent; if (n>array.size()-1) n--; nth_element(array.begin(), array.begin()+n, array.end()); // slack, no averaging.. fixme if this function is ever used.. return array[n] * gain; } EventDataType Session::wavg(ChannelID id) { QHash<EventStoreType,quint32> vtime; QHash<ChannelID,EventDataType>::iterator i=m_wavg.find(id); if (i!=m_wavg.end()) return i.value(); updateCountSummary(id); QHash<ChannelID,QHash<EventStoreType, quint32> >::iterator j2=m_timesummary.find(id); if (j2==m_timesummary.end()) return 0; QHash<EventStoreType, quint32> & timesum=j2.value(); if (!m_gain.contains(id)) return 0; double s0=0,s1=0,s2; EventDataType val, gain=m_gain[id]; for (QHash<EventStoreType, quint32>::iterator vi=timesum.begin();vi!=timesum.end();vi++) { val=vi.key() * gain; s2=vi.value(); s0+=s2; s1+=val * s2; } if (s0>0) { val=s1/s0; } else val=0; m_wavg[id]=val; return val; } EventList * Session::AddEventList(ChannelID code, EventListType et,EventDataType gain,EventDataType offset,EventDataType min, EventDataType max,EventDataType rate,bool second_field) { schema::Channel * channel=&schema::channel[code]; if (!channel) { qWarning() << "Channel" << code << "does not exist!"; //return NULL; } EventList * el=new EventList(et,gain,offset,min,max,rate,second_field); eventlist[code].push_back(el); //s_machine->registerChannel(chan); return el; } void Session::offsetSession(qint64 offset) { //qDebug() << "Session starts" << QDateTime::fromTime_t(s_first/1000).toString("yyyy-MM-dd HH:mm:ss"); s_first+=offset; s_last+=offset; QHash<ChannelID,quint64>::iterator it; for (it=m_firstchan.begin();it!=m_firstchan.end();it++) { if (it.value()>0) it.value()+=offset; } for (it=m_lastchan.begin();it!=m_lastchan.end();it++) { if (it.value()>0) it.value()+=offset; } QHash<ChannelID,QVector<EventList *> >::iterator i; for (i=eventlist.begin();i!=eventlist.end();i++) { for (int j=0;j<i.value().size();j++) { EventList *e=i.value()[j]; e->setFirst(e->first()+offset); e->setLast(e->last()+offset); } } qDebug() << "Session now starts" << QDateTime::fromTime_t(s_first/1000).toString("yyyy-MM-dd HH:mm:ss"); }