mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-05 18:50:44 +00:00
More ResMed stuff, plus popout graph test
This commit is contained in:
parent
3e46269825
commit
acf0ddca3d
@ -1,4 +1,4 @@
|
||||
/* gGraphView Implementation
|
||||
/* gGraphView Implementation
|
||||
*
|
||||
* Copyright (c) 2011-2018 Mark Watkins <mark@jedimark.net>
|
||||
*
|
||||
@ -16,6 +16,9 @@
|
||||
#include <QFontMetrics>
|
||||
#include <QWidgetAction>
|
||||
#include <QGridLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDockWidget>
|
||||
#include <QMainWindow>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
# include <QWindow>
|
||||
@ -358,6 +361,7 @@ gGraphView::gGraphView(QWidget *parent, gGraphView *shared)
|
||||
use_pixmap_cache = AppSetting->usePixmapCaching();
|
||||
|
||||
pin_graph = nullptr;
|
||||
popout_graph = nullptr;
|
||||
// pixmapcache.setCacheLimit(10240*2);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
@ -377,6 +381,8 @@ gGraphView::gGraphView(QWidget *parent, gGraphView *shared)
|
||||
pin_action = context_menu->addAction(QString(), this, SLOT(togglePin()));
|
||||
pin_icon = QPixmap(":/icons/pushpin.png");
|
||||
|
||||
popout_action = context_menu->addAction(QObject::tr("Pop out Graph"), this, SLOT(popoutGraph()));
|
||||
|
||||
snap_action = context_menu->addAction(QString(), this, SLOT(onSnapshotGraphToggle()));
|
||||
context_menu->addSeparator();
|
||||
|
||||
@ -425,6 +431,114 @@ gGraphView::gGraphView(QWidget *parent, gGraphView *shared)
|
||||
#endif
|
||||
}
|
||||
|
||||
void MyDockWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
gGraphView::dock->deleteLater();
|
||||
gGraphView::dock=nullptr;
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
MyDockWindow * gGraphView::dock = nullptr;
|
||||
void gGraphView::popoutGraph()
|
||||
{
|
||||
if (popout_graph) {
|
||||
if (dock == nullptr) {
|
||||
dock = new MyDockWindow(mainwin->getDaily(), Qt::Window);
|
||||
dock->resize(width(),0);
|
||||
// QScrollArea
|
||||
}
|
||||
QDockWidget * widget = new QDockWidget(dock);
|
||||
widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
widget->setMouseTracking(true);
|
||||
int h = dock->height()+popout_graph->height()+30;
|
||||
if (h > height()) h = height();
|
||||
dock->resize(dock->width(), h);
|
||||
widget->resize(width(), popout_graph->height()+30);
|
||||
|
||||
gGraphView * gv = new gGraphView(widget, this);
|
||||
widget->setWidget(gv);
|
||||
gv->setMouseTracking(true);
|
||||
gv->setDay(this->day());
|
||||
dock->addDockWidget(Qt::BottomDockWidgetArea, widget,Qt::Vertical);
|
||||
|
||||
/////// Fix some resize glitches ///////
|
||||
// https://stackoverflow.com/questions/26286646/create-a-qdockwidget-that-resizes-to-its-contents?rq=1
|
||||
QDockWidget* dummy = new QDockWidget;
|
||||
dock->addDockWidget(Qt::BottomDockWidgetArea, dummy);
|
||||
dock->removeDockWidget(dummy);
|
||||
|
||||
QPoint mousePos = dock->mapFromGlobal(QCursor::pos());
|
||||
mousePos.setY(dock->rect().bottom()+2);
|
||||
QCursor::setPos(dock->mapToGlobal(mousePos));
|
||||
QMouseEvent* grabSeparatorEvent =
|
||||
new QMouseEvent(QMouseEvent::MouseButtonPress,mousePos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
|
||||
qApp->postEvent(dock, grabSeparatorEvent);
|
||||
/////////////////////////////////////////
|
||||
|
||||
// dock->updateGeometry();
|
||||
if (!dock->isVisible()) dock->show();
|
||||
|
||||
gGraph * graph = popout_graph;
|
||||
|
||||
QString basename = graph->title()+" - ";
|
||||
if (graph->m_day) {
|
||||
// append the date of the graph's left edge to the snapshot name
|
||||
// so the user knows what day the snapshot starts
|
||||
// because the name is displayed to the user, use local time
|
||||
QDateTime date = QDateTime::fromMSecsSinceEpoch(graph->min_x, Qt::LocalTime);
|
||||
basename += date.date().toString(Qt::SystemLocaleLongDate);
|
||||
}
|
||||
|
||||
QString newname = basename;
|
||||
|
||||
// Find a new name.. How many snapshots for each graph counts as stupid?
|
||||
|
||||
QString newtitle = graph->title();
|
||||
|
||||
widget->setWindowTitle(newname);
|
||||
gGraph * newgraph = new gGraph(newname, nullptr, newtitle, graph->units(), graph->height(), graph->group());
|
||||
newgraph->setHeight(graph->height());
|
||||
|
||||
short group = 0;
|
||||
gv->m_graphs.insert(m_graphs.indexOf(graph)+1, newgraph);
|
||||
gv->m_graphsbyname[newname] = newgraph;
|
||||
newgraph->m_graphview = gv;
|
||||
|
||||
for (int i=0; i < graph->m_layers.size(); ++i) {
|
||||
Layer * layer = graph->m_layers.at(i)->Clone();
|
||||
if (layer) {
|
||||
newgraph->m_layers.append(layer);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<m_graphs.size();i++) {
|
||||
gGraph *g = m_graphs.at(i);
|
||||
group = qMax(g->group(), group);
|
||||
}
|
||||
newgraph->setGroup(group+1);
|
||||
//newgraph->setMinHeight(pm.height());
|
||||
|
||||
newgraph->setDay(graph->m_day);
|
||||
if (graph->m_day) {
|
||||
graph->m_day->incUseCounter();
|
||||
}
|
||||
newgraph->min_x = graph->min_x;
|
||||
newgraph->max_x = graph->max_x;
|
||||
|
||||
newgraph->setBlockSelect(false);
|
||||
newgraph->setZoomY(graph->zoomY());
|
||||
|
||||
newgraph->setSnapshot(false);
|
||||
newgraph->setShowTitle(true);
|
||||
|
||||
|
||||
gv->resetLayout();
|
||||
gv->timedRedraw(0);
|
||||
//widget->setUpdatesEnabled(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void gGraphView::togglePin()
|
||||
{
|
||||
if (pin_graph) {
|
||||
@ -1396,7 +1510,7 @@ void gGraphView::paintGL()
|
||||
QString txt;
|
||||
if (m_showAuthorMessage) {
|
||||
if (emptyText() == STR_Empty_Brick) {
|
||||
txt = "\nI'm very sorry your machine doesn't record useful data to graph in Daily View :(";
|
||||
txt = QObject::tr("\nI'm very sorry your machine doesn't record useful data to graph in Daily View :(");
|
||||
} else {
|
||||
// not proud of telling them their machine is a Brick.. ;)
|
||||
txt = QObject::tr("SleepyHead is proudly brought to you by JediMark.");
|
||||
@ -2605,7 +2719,10 @@ void gGraphView::mousePressEvent(QMouseEvent *event)
|
||||
//done=true;
|
||||
} else if ((event->button() == Qt::RightButton) && (x < (titleWidth + gYAxis::Margin))) {
|
||||
this->setCursor(Qt::ArrowCursor);
|
||||
popout_action->setText(QObject::tr("Popout %1 Graph").arg(g->title()));
|
||||
popout_graph = g;
|
||||
pin_action->setText(QObject::tr("Pin %1 Graph").arg(g->title()));
|
||||
|
||||
pin_graph = g;
|
||||
populateMenu(g);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* gGraphView Header
|
||||
/* gGraphView Header
|
||||
*
|
||||
* Copyright (c) 2011-2015 Mark Watkins <jedimark@users.sourceforge.net>
|
||||
*
|
||||
@ -9,6 +9,7 @@
|
||||
#ifndef GGRAPHVIEW_H
|
||||
#define GGRAPHVIEW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QScrollBar>
|
||||
#include <QResizeEvent>
|
||||
#include <QThread>
|
||||
@ -278,6 +279,14 @@ struct SelectionHistoryItem {
|
||||
quint64 maxx;
|
||||
};
|
||||
|
||||
class MyDockWindow:public QMainWindow
|
||||
{
|
||||
public:
|
||||
MyDockWindow(QWidget * parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) {}
|
||||
void closeEvent(QCloseEvent *event);
|
||||
};
|
||||
|
||||
|
||||
/*! \class gGraphView
|
||||
\brief Main OpenGL Graph Area, derived from QGLWidget
|
||||
|
||||
@ -531,6 +540,7 @@ class gGraphView
|
||||
|
||||
QVector<SelectionHistoryItem> history;
|
||||
|
||||
static MyDockWindow * dock;
|
||||
protected:
|
||||
|
||||
bool event(QEvent * event) Q_DECL_OVERRIDE;
|
||||
@ -661,8 +671,10 @@ class gGraphView
|
||||
QTime horizScrollTime, vertScrollTime;
|
||||
QMenu * context_menu;
|
||||
QAction * pin_action;
|
||||
QAction * popout_action;
|
||||
QPixmap pin_icon;
|
||||
gGraph *pin_graph;
|
||||
gGraph *popout_graph;
|
||||
|
||||
QAction * snap_action;
|
||||
|
||||
@ -697,13 +709,13 @@ class gGraphView
|
||||
|
||||
bool hasSnapshots();
|
||||
|
||||
void popoutGraph();
|
||||
void togglePin();
|
||||
protected slots:
|
||||
void onLinesClicked(QAction *);
|
||||
void onPlotsClicked(QAction *);
|
||||
void onOverlaysClicked(QAction *);
|
||||
void onSnapshotGraphToggle();
|
||||
|
||||
};
|
||||
|
||||
#endif // GGRAPHVIEW_H
|
||||
|
@ -17,7 +17,8 @@
|
||||
EDFParser::EDFParser(QString name)
|
||||
{
|
||||
buffer = nullptr;
|
||||
Open(name);
|
||||
if (!name.isEmpty())
|
||||
Open(name);
|
||||
}
|
||||
EDFParser::~EDFParser()
|
||||
{
|
||||
|
@ -115,25 +115,19 @@ bool matchSignal(ChannelID ch, const QString & name)
|
||||
}
|
||||
|
||||
// This function parses a list of STR files and creates a date ordered map of individual records
|
||||
void ResmedLoader::ParseSTR(Machine *mach, const QStringList & strfiles)
|
||||
void ResmedLoader::ParseSTR(Machine *mach, QMap<QDate, STRFile> & STRmap)
|
||||
{
|
||||
int numSTRs = strfiles.size();
|
||||
if (!qprogress) {
|
||||
qWarning() << "What happened to qprogress object in ResmedLoader::ParseSTR()";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i=0; i< numSTRs; ++i) {
|
||||
QMap<QDate, STRFile>::iterator it;
|
||||
|
||||
const QString & strfile = strfiles.at(i);
|
||||
|
||||
// Open and Parse STR.edf file
|
||||
ResMedEDFParser str(strfile);
|
||||
if (!str.Parse()) continue;
|
||||
if (mach->serial() != str.serialnumber) {
|
||||
qDebug() << "Trying to import a STR.edf from another machine, skipping" << mach->serial() << "!=" << str.serialnumber << "in" << strfile;
|
||||
continue;
|
||||
}
|
||||
for (it = STRmap.begin(); it!= STRmap.end(); ++it) {
|
||||
STRFile & file = it.value();
|
||||
QString & strfile = file.filename;
|
||||
ResMedEDFParser & str = *file.edf;
|
||||
|
||||
QDate date = str.startdate_orig.date(); // each STR.edf record starts at 12 noon
|
||||
|
||||
@ -157,7 +151,7 @@ void ResmedLoader::ParseSTR(Machine *mach, const QStringList & strfiles)
|
||||
// For each data record, representing 1 day each
|
||||
for (int rec = 0; rec < size; ++rec, date = date.addDays(1)) {
|
||||
|
||||
QHash<QDate, ResMedDay>::iterator rit = resdayList.find(date);
|
||||
QMap<QDate, ResMedDay>::iterator rit = resdayList.find(date);
|
||||
if (rit != resdayList.end()) {
|
||||
// Already seen this record.. should check if the data is the same, but meh.
|
||||
continue;
|
||||
@ -262,22 +256,78 @@ void ResmedLoader::ParseSTR(Machine *mach, const QStringList & strfiles)
|
||||
R.maskdur = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupLabel("Leak Med"))) {
|
||||
if ((sig = str.lookupLabel("Leak Med")) || (sig = str.lookupLabel("Leak.50"))) {
|
||||
float gain = sig->gain * 60.0;
|
||||
R.leakgain = gain;
|
||||
R.leakmed = EventDataType(sig->data[rec]) * gain + sig->offset;
|
||||
R.leak50 = EventDataType(sig->data[rec]) * gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("Leak Max"))) {
|
||||
if ((sig = str.lookupLabel("Leak Max"))|| (sig = str.lookupLabel("Leak.Max"))) {
|
||||
float gain = sig->gain * 60.0;
|
||||
R.leakgain = gain;
|
||||
R.leakmax = EventDataType(sig->data[rec]) * gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("Leak 95"))) {
|
||||
if ((sig = str.lookupLabel("Leak 95")) || (sig = str.lookupLabel("Leak.95"))) {
|
||||
float gain = sig->gain * 60.0;
|
||||
R.leakgain = gain;
|
||||
R.leak95 = EventDataType(sig->data[rec]) * gain + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupLabel("RespRate.50"))) {
|
||||
R.rr50 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("RespRate.Max"))) {
|
||||
R.rrmax = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("RespRate.95"))) {
|
||||
R.rr95 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("MinVent.50"))) {
|
||||
R.mv50 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("MinVent.Max"))) {
|
||||
R.mvmax = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("MinVent.95"))) {
|
||||
R.mv95 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TidVol.50"))) {
|
||||
R.tv50 = EventDataType(sig->data[rec]) * (sig->gain*1000.0) + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TidVol.Max"))) {
|
||||
R.tvmax = EventDataType(sig->data[rec]) * (sig->gain*1000.0) + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TidVol.95"))) {
|
||||
R.tv95 = EventDataType(sig->data[rec]) * (sig->gain*1000.0) + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupLabel("MaskPress.50"))) {
|
||||
R.mp50 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("MaskPress.Max"))) {
|
||||
R.mpmax = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("MaskPress.95"))) {
|
||||
R.mp95 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupLabel("TgtEPAP.50"))) {
|
||||
R.tgtepap50 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TgtEPAP.Max"))) {
|
||||
R.tgtepapmax = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TgtEPAP.95"))) {
|
||||
R.tgtepap95 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupLabel("TgtIPAP.50"))) {
|
||||
R.tgtipap50 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TgtIPAP.Max"))) {
|
||||
R.tgtipapmax = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupLabel("TgtIPAP.95"))) {
|
||||
R.tgtipap95 = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
|
||||
|
||||
bool haveipap = false;
|
||||
// if (R.mode == MODE_BILEVEL_FIXED) {
|
||||
if ((sig = str.lookupSignal(CPAP_IPAP))) {
|
||||
@ -329,35 +379,35 @@ void ResmedLoader::ParseSTR(Machine *mach, const QStringList & strfiles)
|
||||
}
|
||||
}
|
||||
|
||||
if ((sig = str.lookupSignal(CPAP_PressureMax))) {
|
||||
R.max_pressure = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_PressureMin))) {
|
||||
R.min_pressure = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(RMS9_SetPressure))) {
|
||||
R.set_pressure = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_PressureMax))) {
|
||||
R.max_pressure = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_PressureMin))) {
|
||||
R.min_pressure = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(RMS9_SetPressure))) {
|
||||
R.set_pressure = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupSignal(CPAP_EPAPHi))) {
|
||||
R.max_epap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_EPAPLo))) {
|
||||
R.min_epap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_EPAPHi))) {
|
||||
R.max_epap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_EPAPLo))) {
|
||||
R.min_epap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
|
||||
if ((sig = str.lookupSignal(CPAP_IPAPHi))) {
|
||||
R.max_ipap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
haveipap = true;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_IPAPLo))) {
|
||||
R.min_ipap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
haveipap = true;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_PS))) {
|
||||
R.ps = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
// }
|
||||
if ((sig = str.lookupSignal(CPAP_IPAPHi))) {
|
||||
R.max_ipap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
haveipap = true;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_IPAPLo))) {
|
||||
R.min_ipap = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
haveipap = true;
|
||||
}
|
||||
if ((sig = str.lookupSignal(CPAP_PS))) {
|
||||
R.ps = EventDataType(sig->data[rec]) * sig->gain + sig->offset;
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
// Okay, problem here: THere are TWO PSMin & MAX values on the 36037 with the same string
|
||||
@ -1430,7 +1480,7 @@ int ResmedLoader::scanFiles(Machine * mach, const QString & datalog_path)
|
||||
// EDFType type = lookupEDFType(ext);
|
||||
|
||||
// Find or create ResMedDay object for this date
|
||||
QHash<QDate, ResMedDay>::iterator rd = resdayList.find(date);
|
||||
QMap<QDate, ResMedDay>::iterator rd = resdayList.find(date);
|
||||
if (rd == resdayList.end()) {
|
||||
rd = resdayList.insert(date, ResMedDay());
|
||||
rd.value().date = date;
|
||||
@ -1708,6 +1758,92 @@ int ResmedLoader::scanFiles(Machine * mach, const QString & datalog_path)
|
||||
|
||||
return c;
|
||||
}*/
|
||||
void DetectPAPMode(Session *sess)
|
||||
{
|
||||
if (sess->channelDataExists(CPAP_Pressure)) {
|
||||
// Determine CPAP or APAP?
|
||||
EventDataType min = sess->Min(CPAP_Pressure);
|
||||
EventDataType max = sess->Max(CPAP_Pressure);
|
||||
if ((max-min)<0.1) {
|
||||
sess->settings[CPAP_Mode] = MODE_CPAP;
|
||||
sess->settings[CPAP_Pressure] = qRound(max * 10.0)/10.0;
|
||||
// early call.. It's CPAP mode
|
||||
} else {
|
||||
// Ramp is ugly
|
||||
if (sess->length() > 1800000L) { // half an our
|
||||
}
|
||||
sess->settings[CPAP_Mode] = MODE_APAP;
|
||||
sess->settings[CPAP_PressureMin] = qRound(min * 10.0)/10.0;
|
||||
sess->settings[CPAP_PressureMax] = qRound(max * 10.0)/10.0;
|
||||
}
|
||||
|
||||
} else if (sess->eventlist.contains(CPAP_IPAP)) {
|
||||
sess->settings[CPAP_Mode] = MODE_BILEVEL_AUTO_VARIABLE_PS;
|
||||
// Determine BiPAP or ASV
|
||||
}
|
||||
|
||||
}
|
||||
void StoreSummarySettings(Session * sess, STRRecord & R)
|
||||
{
|
||||
if (R.mode >= 0) {
|
||||
if (R.mode == MODE_CPAP) {
|
||||
} else if (R.mode == MODE_APAP) {
|
||||
}
|
||||
}
|
||||
|
||||
if (R.leak95 >= 0) {
|
||||
// sess->setp95(CPAP_Leak, R.leak95);
|
||||
}
|
||||
if (R.leak50 >= 0) {
|
||||
// sess->setp50(CPAP_Leak, R.leak50);
|
||||
}
|
||||
if (R.leakmax >= 0) {
|
||||
sess->setMax(CPAP_Leak, R.leakmax);
|
||||
}
|
||||
|
||||
if (R.rr95 >= 0) {
|
||||
// sess->setp95(CPAP_RespRate, R.rr95);
|
||||
}
|
||||
if (R.rr50 >= 0) {
|
||||
// sess->setp50(CPAP_RespRate, R.rr50);
|
||||
}
|
||||
if (R.rrmax >= 0) {
|
||||
sess->setMax(CPAP_RespRate, R.rrmax);
|
||||
}
|
||||
|
||||
if (R.mv95 >= 0) {
|
||||
// sess->setp95(CPAP_MinuteVent, R.mv95);
|
||||
}
|
||||
if (R.mv50 >= 0) {
|
||||
// sess->setp50(CPAP_MinuteVent, R.mv50);
|
||||
}
|
||||
if (R.mvmax >= 0) {
|
||||
sess->setMax(CPAP_MinuteVent, R.mvmax);
|
||||
}
|
||||
|
||||
if (R.tv95 >= 0) {
|
||||
// sess->setp95(CPAP_TidalVolume, R.tv95);
|
||||
}
|
||||
if (R.tv50 >= 0) {
|
||||
// sess->setp50(CPAP_TidalVolume, R.tv50);
|
||||
}
|
||||
if (R.tvmax >= 0) {
|
||||
sess->setMax(CPAP_TidalVolume, R.tvmax);
|
||||
}
|
||||
|
||||
if (R.mp95 >= 0) {
|
||||
// sess->setp95(CPAP_MaskPressure, R.mp95);
|
||||
}
|
||||
if (R.mp50 >= 0) {
|
||||
// sess->setp50(CPAP_MaskPressure, R.mp50);
|
||||
}
|
||||
if (R.mpmax >= 0) {
|
||||
sess->setMax(CPAP_MaskPressure, R.mpmax);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void StoreSettings(Session * sess, STRRecord & R)
|
||||
{
|
||||
@ -1851,23 +1987,32 @@ void ResDayTask::run()
|
||||
return;
|
||||
}
|
||||
// Summary only day, create one session and tag it summary only
|
||||
SessionID sid = resday->str.maskon[0];
|
||||
STRRecord & R = resday->str;
|
||||
|
||||
Session * sess = new Session(mach, sid);
|
||||
StoreSettings(sess, R);
|
||||
for (int i=0;i<resday->str.maskon.size();++i) {
|
||||
quint32 maskon = resday->str.maskon[i];
|
||||
quint32 maskoff = resday->str.maskoff[i];
|
||||
if ((maskon>0) && (maskoff>0)) {
|
||||
Session * sess = new Session(mach, maskon);
|
||||
sess->set_first(quint64(maskon) * 1000L);
|
||||
sess->set_last(quint64(maskoff) * 1000L);
|
||||
// Process the STR.edf settings
|
||||
StoreSettings(sess, R);
|
||||
// We want the summary information too otherwise we've got nothing.
|
||||
StoreSummarySettings(sess, R);
|
||||
|
||||
sess->setSummaryOnly(true);
|
||||
sess->SetChanged(true);
|
||||
sess->setSummaryOnly(true);
|
||||
sess->SetChanged(true);
|
||||
sess->Store(mach->getDataPath());
|
||||
|
||||
loader->sessionMutex.lock();
|
||||
mach->AddSession(sess);
|
||||
loader->sessionCount++;
|
||||
loader->sessionMutex.unlock();
|
||||
|
||||
sess->Store(mach->getDataPath());
|
||||
sess->TrashEvents();
|
||||
loader->sessionMutex.lock();
|
||||
mach->AddSession(sess);
|
||||
loader->sessionCount++;
|
||||
loader->sessionMutex.unlock();
|
||||
|
||||
//sess->TrashEvents();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2032,6 +2177,13 @@ void ResDayTask::run()
|
||||
sess->AddEventList(CPAP_Apnea, EVL_Event);
|
||||
sess->AddEventList(CPAP_Hypopnea, EVL_Event);
|
||||
}
|
||||
sess->setSummaryOnly(false);
|
||||
sess->SetChanged(true);
|
||||
|
||||
if (sess->length()>0) {
|
||||
// we want empty sessions even though they are crap
|
||||
}
|
||||
|
||||
if (resday->str.date.isValid()) {
|
||||
STRRecord & R = resday->str;
|
||||
|
||||
@ -2047,37 +2199,59 @@ void ResDayTask::run()
|
||||
StoreSettings(sess, R);
|
||||
|
||||
} else {
|
||||
// We have no Summary or Settings data... we need to do something to indicate this, and detect the mode
|
||||
if (sess->eventlist.contains(CPAP_Pressure)) {
|
||||
EventList * pressure = sess->eventlist[CPAP_Pressure];
|
||||
// No corresponding STR.edf record, but we have EDF files
|
||||
|
||||
bool foundprev = false;
|
||||
// This is yuck.. we need to find the LAST date with valid settings data
|
||||
QDate first = p_profile->FirstDay(MT_CPAP);
|
||||
for (QDate d = resday->date.addDays(-1); d >= first; d = d.addDays(-1)) {
|
||||
loader->sessionMutex.lock();
|
||||
Day * day = p_profile->GetDay(d, MT_CPAP);
|
||||
bool hasmachine = day && day->hasMachine(mach);
|
||||
loader->sessionMutex.unlock();
|
||||
|
||||
if (!day) continue;
|
||||
if (!hasmachine) continue;
|
||||
|
||||
QList<Session *> sessions = day->getSessions(MT_CPAP);
|
||||
|
||||
if (sessions.size() > 0) {
|
||||
Session *chksess = sessions[0];
|
||||
sess->settings = chksess->settings;
|
||||
foundprev = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
sess->settings[CPAP_BrokenSummary] = true;
|
||||
|
||||
if (!foundprev) {
|
||||
// We have no Summary or Settings data... we need to do something to indicate this, and detect the mode
|
||||
if (sess->channelDataExists(CPAP_Pressure)) {
|
||||
DetectPAPMode(sess);
|
||||
}
|
||||
}
|
||||
}
|
||||
sess->setSummaryOnly(false);
|
||||
sess->SetChanged(true);
|
||||
|
||||
if (sess->length() > 0) {
|
||||
loader->addSession(sess);
|
||||
sess->UpdateSummaries();
|
||||
sess->UpdateSummaries();
|
||||
|
||||
// Save is not threadsafe?
|
||||
// loader->saveMutex.lock();
|
||||
//backup file...
|
||||
sess->Store(mach->getDataPath());
|
||||
// loader->saveMutex.unlock();
|
||||
// Save is not threadsafe?
|
||||
// loader->saveMutex.lock();
|
||||
sess->Store(mach->getDataPath());
|
||||
// loader->saveMutex.unlock();
|
||||
|
||||
// Free the memory used by this session
|
||||
sess->TrashEvents();
|
||||
loader->sessionMutex.lock();
|
||||
loader->sessionCount++;
|
||||
loader->sessionMutex.unlock();
|
||||
} else {
|
||||
delete sess;
|
||||
}
|
||||
loader->sessionMutex.lock();
|
||||
mach->AddSession(sess);
|
||||
loader->sessionMutex.unlock();
|
||||
|
||||
// Free the memory used by this session
|
||||
sess->TrashEvents();
|
||||
loader->sessionMutex.lock();
|
||||
loader->sessionCount++;
|
||||
loader->sessionMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ResmedLoader::Open(const QString & dirpath)
|
||||
{
|
||||
|
||||
@ -2195,6 +2369,7 @@ int ResmedLoader::Open(const QString & dirpath)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
Machine *mach = p_profile->CreateMachine(info);
|
||||
|
||||
bool importing_backups = false;
|
||||
bool create_backups = p_profile->session->backupCardData();
|
||||
bool compress_backups = p_profile->session->compressBackupData();
|
||||
|
||||
@ -2202,6 +2377,7 @@ int ResmedLoader::Open(const QString & dirpath)
|
||||
|
||||
if (path == backup_path) {
|
||||
// Don't create backups if importing from backup folder
|
||||
importing_backups = true;
|
||||
create_backups = false;
|
||||
}
|
||||
|
||||
@ -2213,48 +2389,152 @@ int ResmedLoader::Open(const QString & dirpath)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Open and Parse STR.edf file
|
||||
// Open and Parse STR.edf files (including those listed in STR_Backup)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
resdayList.clear();
|
||||
|
||||
// List all STR.edf backups and tag on latest for processing
|
||||
QStringList strfiles;
|
||||
strfiles.push_back(strpath);
|
||||
QDir dir(path + "STR_Backup");
|
||||
dir.setFilter(QDir::Files | QDir::Hidden | QDir::Readable);
|
||||
QFileInfoList flist = dir.entryInfoList();
|
||||
QMap<QDate, STRFile> STRmap;
|
||||
|
||||
int size = flist.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
QFileInfo fi = flist.at(i);
|
||||
filename = fi.fileName();
|
||||
if (filename.startsWith("STR", Qt::CaseInsensitive)) {
|
||||
strfiles.push_back(fi.filePath());
|
||||
QDir dir;
|
||||
|
||||
// Create the STR_Backup folder if it doesn't exist
|
||||
QString strBackupPath = backup_path + "STR_Backup";
|
||||
if (!dir.exists(strBackupPath)) dir.mkpath(strBackupPath);
|
||||
|
||||
if (!importing_backups ) {
|
||||
QStringList strfiles;
|
||||
// add primary STR.edf
|
||||
strfiles.push_back(strpath);
|
||||
|
||||
// Just in case we are importing into a new folder, process SleepyHead backup structures
|
||||
dir.setPath(path + "STR_Backup");
|
||||
dir.setFilter(QDir::Files | QDir::Hidden | QDir::Readable);
|
||||
QFileInfoList flist = dir.entryInfoList();
|
||||
|
||||
int size = flist.size();
|
||||
// Add any STR_Backup versions to the file list
|
||||
for (int i = 0; i < size; i++) {
|
||||
QFileInfo fi = flist.at(i);
|
||||
filename = fi.fileName();
|
||||
if (!filename.startsWith("STR", Qt::CaseInsensitive))
|
||||
continue;
|
||||
if (!(filename.endsWith("edf.gz", Qt::CaseInsensitive) || filename.endsWith("edf", Qt::CaseInsensitive)))
|
||||
continue;
|
||||
strfiles.push_back(fi.canonicalFilePath());
|
||||
}
|
||||
|
||||
// Now place any of these files in the Backup folder sorted by the file date
|
||||
for (int i=0;i<strfiles.size();i++) {
|
||||
QString filename = strfiles.at(i);
|
||||
ResMedEDFParser * stredf = new ResMedEDFParser(filename);
|
||||
if (!stredf->Parse()) {
|
||||
qDebug() << "Faulty STR file" << filename;
|
||||
delete stredf;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stredf->serialnumber != info.serial) {
|
||||
qDebug() << "Identification.tgt Serial number doesn't match" << filename;
|
||||
delete stredf;
|
||||
continue;
|
||||
}
|
||||
|
||||
QDate date = stredf->startdate_orig.date();
|
||||
date = QDate(date.year(), date.month(), 1);
|
||||
if (STRmap.contains(date)) {
|
||||
delete stredf;
|
||||
continue;
|
||||
}
|
||||
QString newname = "STR-"+date.toString("yyyyMM")+"."+STR_ext_EDF;
|
||||
|
||||
QString backupfile = strBackupPath+"/"+newname;
|
||||
|
||||
if (compress_backups) backupfile += STR_ext_gz;
|
||||
|
||||
if (!QFile::exists(backupfile)) {
|
||||
if (filename.endsWith(STR_ext_gz,Qt::CaseInsensitive)) {
|
||||
if (compress_backups) {
|
||||
QFile::copy(filename, backupfile);
|
||||
} else {
|
||||
uncompressFile(filename, backupfile);
|
||||
}
|
||||
} else {
|
||||
if (compress_backups) {
|
||||
// already compressed, keep it.
|
||||
compressFile(filename, backupfile);
|
||||
} else {
|
||||
QFile::copy(filename, backupfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STRmap[date] = STRFile(backupfile, stredf);
|
||||
}
|
||||
}
|
||||
|
||||
ParseSTR(mach, strfiles);
|
||||
// Now we open the REAL STR_Backup, and open the rest for later parsing
|
||||
|
||||
// This is ugly, we only need the starting date for backup purposes
|
||||
ResMedEDFParser stredf(strpath);
|
||||
if (!stredf.Parse()) {
|
||||
qDebug() << "Faulty file" << RMS9_STR_strfile;
|
||||
return 0;
|
||||
dir.setPath(backup_path + "STR_Backup");
|
||||
dir.setFilter(QDir::Files | QDir::Hidden | QDir::Readable);
|
||||
QFileInfoList flist = dir.entryInfoList();
|
||||
QDate date;
|
||||
|
||||
int size = flist.size();
|
||||
// Add any STR_Backup versions to the file list
|
||||
for (int i = 0; i < size; i++) {
|
||||
QFileInfo fi = flist.at(i);
|
||||
filename = fi.fileName();
|
||||
if (!filename.startsWith("STR", Qt::CaseInsensitive))
|
||||
continue;
|
||||
if (!(filename.endsWith("edf.gz", Qt::CaseInsensitive) || filename.endsWith("edf", Qt::CaseInsensitive)))
|
||||
continue;
|
||||
QString datestr = filename.section("STR-",-1).section(".edf",0,0)+"01";
|
||||
date = QDate().fromString(datestr,"yyyyMMdd");
|
||||
|
||||
if (STRmap.contains(date)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResMedEDFParser * stredf = new ResMedEDFParser(fi.canonicalFilePath());
|
||||
if (!stredf->Parse()) {
|
||||
qDebug() << "Faulty STR file" << filename;
|
||||
delete stredf;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stredf->serialnumber != info.serial) {
|
||||
qDebug() << "Identification.tgt Serial number doesn't match" << filename;
|
||||
delete stredf;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't trust the filename date, pick the one inside the STR...
|
||||
date = stredf->startdate_orig.date();
|
||||
date = QDate(date.year(), date.month(), 1);
|
||||
|
||||
STRmap[date] = STRFile(fi.canonicalFilePath(), stredf);
|
||||
}
|
||||
|
||||
if (stredf.serialnumber != info.serial) {
|
||||
qDebug() << "Identification.tgt Serial number doesn't match STR.edf!";
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Build a Date map of all records in STR.edf files, populating ResDayList
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
ParseSTR(mach, STRmap);
|
||||
|
||||
// We are done with the Parsed STR EDF objects, so delete them
|
||||
QMap<QDate, STRFile>::iterator it;
|
||||
for (it=STRmap.begin(); it!= STRmap.end(); ++it) {
|
||||
delete it.value().edf;
|
||||
}
|
||||
|
||||
|
||||
// Creating early as we need the object
|
||||
dir.setPath(newpath);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Create the backup folder for storing a copy of everything in..
|
||||
// (Unless we are importing from this backup folder)
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
dir.setPath(newpath);
|
||||
if (create_backups) {
|
||||
if (!dir.exists(backup_path)) {
|
||||
if (!dir.mkpath(backup_path + RMS9_STR_datalog)) {
|
||||
@ -2266,68 +2546,10 @@ int ResmedLoader::Open(const QString & dirpath)
|
||||
QFile::copy(path + RMS9_STR_idfile + STR_ext_TGT, backup_path + RMS9_STR_idfile + STR_ext_TGT);
|
||||
QFile::copy(path + RMS9_STR_idfile + STR_ext_CRC, backup_path + RMS9_STR_idfile + STR_ext_CRC);
|
||||
|
||||
QDateTime dts = QDateTime::fromMSecsSinceEpoch(stredf.startdate, Qt::UTC);
|
||||
dir.mkpath(backup_path + "STR_Backup");
|
||||
QString strmonthly = backup_path + "STR_Backup/STR-" + dts.toString("yyyyMM") + "." + STR_ext_EDF;
|
||||
|
||||
//copy STR files to backup folder
|
||||
if (strpath.endsWith(STR_ext_gz)) { // Already compressed. Don't bother decompressing..
|
||||
QFile::copy(strpath, backup_path + RMS9_STR_strfile + STR_ext_EDF + STR_ext_gz);
|
||||
} else { // Compress STR file to backup folder
|
||||
QString strf = backup_path + RMS9_STR_strfile + STR_ext_EDF;
|
||||
|
||||
// Copy most recent to STR.edf
|
||||
if (QFile::exists(strf)) {
|
||||
QFile::remove(strf);
|
||||
}
|
||||
|
||||
if (QFile::exists(strf + STR_ext_gz)) {
|
||||
QFile::remove(strf + STR_ext_gz);
|
||||
}
|
||||
|
||||
compress_backups ?
|
||||
compressFile(strpath, strf)
|
||||
:
|
||||
QFile::copy(strpath, strf);
|
||||
|
||||
}
|
||||
|
||||
// Keep one STR.edf backup every month
|
||||
if (!QFile::exists(strmonthly) && !QFile::exists(strmonthly + ".gz")) {
|
||||
compress_backups ?
|
||||
compressFile(strpath, strmonthly)
|
||||
:
|
||||
QFile::copy(strpath, strmonthly);
|
||||
}
|
||||
|
||||
// Meh.. these can be calculated if ever needed for ResScan SDcard export
|
||||
QFile::copy(path + "STR.crc", backup_path + "STR.crc");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Process the actual STR.edf data
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
qint64 numrecs = stredf.GetNumDataRecords();
|
||||
qint64 duration = numrecs * stredf.GetDuration();
|
||||
|
||||
int days = duration / 86400000L; // GetNumDataRecords = this.. Duh!
|
||||
|
||||
if (days<0) {
|
||||
qDebug() << "Error: Negative number of days in STR.edf, aborting import";
|
||||
days=0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Process STR.edf and find first and last time for each day
|
||||
|
||||
QVector<qint8> dayused;
|
||||
dayused.resize(days);
|
||||
|
||||
//time_t time = stredf.startdate / 1000L; // == 12pm on first day
|
||||
|
||||
// reset time to first day
|
||||
//time = stredf.startdate / 1000;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Scan DATALOG files, sort, and import any new sessions
|
||||
@ -2342,7 +2564,7 @@ int ResmedLoader::Open(const QString & dirpath)
|
||||
// Now at this point we have resdayList populated with processable summary and EDF files data
|
||||
// that can be processed in threads..
|
||||
|
||||
QHash<QDate, ResMedDay>::iterator rdi;
|
||||
QMap<QDate, ResMedDay>::iterator rdi;
|
||||
|
||||
for (rdi = resdayList.begin(); rdi != resdayList.end(); rdi++) {
|
||||
QDate date = rdi.key();
|
||||
|
@ -71,10 +71,33 @@ struct STRRecord
|
||||
uai = -1;
|
||||
cai = -1;
|
||||
|
||||
leakmed = -1;
|
||||
leak50 = -1;
|
||||
leak95 = -1;
|
||||
leakmax = -1;
|
||||
leakgain = 0;
|
||||
|
||||
rr50 = -1;
|
||||
rr95 = -1;
|
||||
rrmax = -1;
|
||||
|
||||
mv50 = -1;
|
||||
mv95 = -1;
|
||||
mvmax = -1;
|
||||
|
||||
tv50 = -1;
|
||||
tv95 = -1;
|
||||
tvmax = -1;
|
||||
|
||||
mp50 = -1;
|
||||
mp95 = -1;
|
||||
mpmax = -1;
|
||||
|
||||
tgtepap50 = -1;
|
||||
tgtepap95 = -1;
|
||||
tgtepapmax = -1;
|
||||
|
||||
tgtipap50 = -1;
|
||||
tgtipap95 = -1;
|
||||
tgtipapmax = -1;
|
||||
|
||||
s_RampTime = -1;
|
||||
s_RampEnable = -1;
|
||||
@ -124,10 +147,29 @@ struct STRRecord
|
||||
uai = copy.uai;
|
||||
cai = copy.cai;
|
||||
date = copy.date;
|
||||
leakmed = copy.leakmed;
|
||||
leak50 = copy.leak50;
|
||||
leak95 = copy.leak95;
|
||||
leakmax = copy.leakmax;
|
||||
leakgain = copy.leakgain;
|
||||
rr50 = copy.rr50;
|
||||
rr95 = copy.rr95;
|
||||
rrmax = copy.rrmax;
|
||||
mv50 = copy.mv50;
|
||||
mv95 = copy.mv95;
|
||||
mvmax = copy.mvmax;
|
||||
tv50 = copy.tv50;
|
||||
tv95 = copy.tv95;
|
||||
tvmax = copy.tvmax;
|
||||
mp50 = copy.mp50;
|
||||
mp95 = copy.mp95;
|
||||
mpmax = copy.mpmax;
|
||||
|
||||
tgtepap50 = copy.tgtepap50;
|
||||
tgtepap95 = copy.tgtepap95;
|
||||
tgtepapmax = copy.tgtepapmax;
|
||||
tgtipap50 = copy.tgtipap50;
|
||||
tgtipap95 = copy.tgtipap95;
|
||||
tgtipapmax = copy.tgtipapmax;
|
||||
|
||||
s_EPREnable = copy.s_EPREnable;
|
||||
s_EPR_ClinEnable = copy.s_EPREnable;
|
||||
s_RampEnable = copy.s_RampEnable;
|
||||
@ -173,10 +215,28 @@ struct STRRecord
|
||||
EventDataType hi;
|
||||
EventDataType uai;
|
||||
EventDataType cai;
|
||||
EventDataType leakmed;
|
||||
EventDataType leak50;
|
||||
EventDataType leak95;
|
||||
EventDataType leakmax;
|
||||
EventDataType leakgain;
|
||||
EventDataType rr50;
|
||||
EventDataType rr95;
|
||||
EventDataType rrmax;
|
||||
EventDataType mv50;
|
||||
EventDataType mv95;
|
||||
EventDataType mvmax;
|
||||
EventDataType tv50;
|
||||
EventDataType tv95;
|
||||
EventDataType tvmax;
|
||||
EventDataType mp50;
|
||||
EventDataType mp95;
|
||||
EventDataType mpmax;
|
||||
EventDataType tgtepap50;
|
||||
EventDataType tgtepap95;
|
||||
EventDataType tgtepapmax;
|
||||
EventDataType tgtipap50;
|
||||
EventDataType tgtipap95;
|
||||
EventDataType tgtipapmax;
|
||||
|
||||
EventDataType ramp_pressure;
|
||||
QDate date;
|
||||
|
||||
@ -263,6 +323,21 @@ protected:
|
||||
ResMedDay * resday;
|
||||
};
|
||||
|
||||
struct STRFile {
|
||||
STRFile() :
|
||||
filename(QString()), edf(nullptr) {}
|
||||
STRFile(QString name, ResMedEDFParser *str) :
|
||||
filename(name), edf(str) {}
|
||||
STRFile(const STRFile & copy) {
|
||||
filename = copy.filename;
|
||||
edf = copy.edf;
|
||||
}
|
||||
~STRFile() {
|
||||
}
|
||||
|
||||
QString filename;
|
||||
ResMedEDFParser * edf;
|
||||
};
|
||||
|
||||
/*class ResmedImport:public ImportTask
|
||||
{
|
||||
@ -368,7 +443,7 @@ class ResmedLoader : public CPAPLoader
|
||||
volatile int sessionCount;
|
||||
|
||||
protected:
|
||||
void ParseSTR(Machine *mach, const QStringList & strfiles);
|
||||
void ParseSTR(Machine *, QMap<QDate, STRFile> &);
|
||||
|
||||
|
||||
//! \brief Scan for new files to import, group into sessions and add to task que
|
||||
@ -379,7 +454,7 @@ protected:
|
||||
QMap<SessionID, QStringList> sessfiles;
|
||||
QMap<quint32, STRRecord> strsess;
|
||||
QMap<QDate, QList<STRRecord *> > strdate;
|
||||
QHash<QDate, ResMedDay> resdayList;
|
||||
QMap<QDate, ResMedDay> resdayList;
|
||||
|
||||
#ifdef DEBUG_EFFICIENCY
|
||||
QHash<ChannelID, qint64> channel_efficiency;
|
||||
|
@ -118,25 +118,74 @@ void MachineLoader::finishAddingSessions()
|
||||
|
||||
}
|
||||
|
||||
bool compressFile(QString inpath, QString outpath)
|
||||
bool uncompressFile(QString infile, QString outfile)
|
||||
{
|
||||
if (outpath.isEmpty()) {
|
||||
outpath = inpath + ".gz";
|
||||
} else if (!outpath.endsWith(".gz")) {
|
||||
outpath += ".gz";
|
||||
if (!infile.endsWith(".gz",Qt::CaseInsensitive)) {
|
||||
qDebug() << "uncompressFile()" << outfile << "missing .gz extension???";
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile f(inpath);
|
||||
if (QFile::exists(outfile)) {
|
||||
qDebug() << "uncompressFile()" << outfile << "already exists";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.exists(inpath)) {
|
||||
qDebug() << "compressFile()" << inpath << "does not exist";
|
||||
// Get file length from inside gzip file
|
||||
QFile fi(infile);
|
||||
|
||||
if (!fi.open(QFile::ReadOnly) || !fi.seek(fi.size() - 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char ch[4];
|
||||
fi.read((char *)ch, 4);
|
||||
quint32 datasize = ch[0] | (ch [1] << 8) | (ch[2] << 16) | (ch[3] << 24);
|
||||
|
||||
// Open gzip file for reading
|
||||
gzFile f = gzopen(infile.toLatin1(), "rb");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Decompressed header and data block
|
||||
char * buffer = new char [datasize];
|
||||
gzread(f, buffer, datasize);
|
||||
gzclose(f);
|
||||
|
||||
QFile out(outfile);
|
||||
if (out.open(QFile::WriteOnly)) {
|
||||
out.write(buffer, datasize);
|
||||
out.close();
|
||||
}
|
||||
|
||||
delete [] buffer;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool compressFile(QString infile, QString outfile)
|
||||
{
|
||||
if (outfile.isEmpty()) {
|
||||
outfile = infile + ".gz";
|
||||
} else if (!outfile.endsWith(".gz")) {
|
||||
outfile += ".gz";
|
||||
}
|
||||
if (QFile::exists(outfile)) {
|
||||
qDebug() << "compressFile()" << outfile << "already exists";
|
||||
}
|
||||
|
||||
QFile f(infile);
|
||||
|
||||
if (!f.exists(infile)) {
|
||||
qDebug() << "compressFile()" << infile << "does not exist";
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 size = f.size();
|
||||
|
||||
if (!f.open(QFile::ReadOnly)) {
|
||||
qDebug() << "compressFile() Couldn't open" << inpath;
|
||||
qDebug() << "compressFile() Couldn't open" << infile;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -144,16 +193,16 @@ bool compressFile(QString inpath, QString outpath)
|
||||
|
||||
if (!f.read(buf, size)) {
|
||||
delete [] buf;
|
||||
qDebug() << "compressFile() Couldn't read all of" << inpath;
|
||||
qDebug() << "compressFile() Couldn't read all of" << infile;
|
||||
return false;
|
||||
}
|
||||
|
||||
f.close();
|
||||
gzFile gz = gzopen(outpath.toLatin1(), "wb");
|
||||
gzFile gz = gzopen(outfile.toLatin1(), "wb");
|
||||
|
||||
//gzbuffer(gz,65536*2);
|
||||
if (!gz) {
|
||||
qDebug() << "compressFile() Couldn't open" << outpath << "for writing";
|
||||
qDebug() << "compressFile() Couldn't open" << outfile << "for writing";
|
||||
delete [] buf;
|
||||
return false;
|
||||
}
|
||||
@ -174,6 +223,7 @@ void MachineLoader::runTasks(bool threaded)
|
||||
|
||||
m_totaltasks=m_tasklist.size();
|
||||
if (m_totaltasks == 0) return;
|
||||
qprogress->setMaximum(m_totaltasks);
|
||||
m_currenttask=0;
|
||||
|
||||
threaded=AppSetting->multithreading();
|
||||
@ -182,11 +232,9 @@ void MachineLoader::runTasks(bool threaded)
|
||||
while (!m_tasklist.isEmpty()) {
|
||||
ImportTask * task = m_tasklist.takeFirst();
|
||||
task->run();
|
||||
float f = float(m_currenttask) / float(m_totaltasks) * 100.0;
|
||||
|
||||
m_currenttask++;
|
||||
if ((m_currenttask % 10)==0) {
|
||||
qprogress->setValue(f);
|
||||
if ((m_currenttask++ % 10)==0) {
|
||||
qprogress->setValue(m_currenttask);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
@ -194,7 +242,6 @@ void MachineLoader::runTasks(bool threaded)
|
||||
ImportTask * task = m_tasklist[0];
|
||||
|
||||
QThreadPool * threadpool = QThreadPool::globalInstance();
|
||||
qprogress->setMaximum(m_totaltasks);
|
||||
|
||||
while (true) {
|
||||
|
||||
@ -206,8 +253,7 @@ void MachineLoader::runTasks(bool threaded)
|
||||
task = m_tasklist[0];
|
||||
|
||||
// update progress bar
|
||||
m_currenttask++;
|
||||
if ((m_currenttask % 10) == 0) {
|
||||
if ((m_currenttask++ % 10) == 0) {
|
||||
qprogress->setValue(m_currenttask);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ MachineLoader * lookupLoader(QString loaderName);
|
||||
void DestroyLoaders();
|
||||
|
||||
bool compressFile(QString inpath, QString outpath = "");
|
||||
bool uncompressFile(QString infile, QString outfile);
|
||||
|
||||
QList<MachineLoader *> GetLoaders(MachineType mt = MT_UNKNOWN);
|
||||
|
||||
|
@ -217,7 +217,16 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr, "Missing argument to --profile\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else if (args[i] == "--datadir") { // mltam's idea
|
||||
QString datadir ;
|
||||
if ((i+1) < args.size()) {
|
||||
datadir = args[++i];
|
||||
settings.setValue("Settings/AppRoot", datadir);
|
||||
} else {
|
||||
fprintf(stderr, "Missing argument to --datadir\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializeLogger();
|
||||
|
@ -445,13 +445,16 @@ void MainWindow::OpenProfile(QString profileName)
|
||||
for (QList<Machine *>::iterator it = machines.begin(); it != machines.end(); ++it) {
|
||||
QString mclass=(*it)->loaderName();
|
||||
if (mclass == STR_MACH_ResMed) {
|
||||
qDebug() << "ResMed machine found.. locking Session splitting capabilities";
|
||||
qDebug() << "ResMed machine found.. dumbing down SleepyHead to suit it's dodgy summary system";
|
||||
|
||||
// Have to sacrifice these features to get access to summary data.
|
||||
p_profile->session->setCombineCloseSessions(0);
|
||||
p_profile->session->setDaySplitTime(QTime(12,0,0));
|
||||
p_profile->session->setIgnoreShortSessions(false);
|
||||
p_profile->session->setLockSummarySessions(true);
|
||||
p_profile->general->setPrefCalcPercentile(95.0); // 95%
|
||||
p_profile->general->setPrefCalcMiddle(0); // Median (50%)
|
||||
p_profile->general->setPrefCalcMax(1); // Dodgy max
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -71,9 +71,25 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) :
|
||||
profile->session->setIgnoreShortSessions(0);
|
||||
profile->session->setCombineCloseSessions(0);
|
||||
profile->session->setLockSummarySessions(true);
|
||||
p_profile->general->setPrefCalcPercentile(95.0); // 95%
|
||||
p_profile->general->setPrefCalcMiddle(0); // Median (50%)
|
||||
p_profile->general->setPrefCalcMax(1); // 99.9th percentile max
|
||||
ui->prefCalcMax->setEnabled(false);
|
||||
ui->prefCalcMiddle->setEnabled(false);
|
||||
ui->prefCalcPercentile->setEnabled(false);
|
||||
ui->showUnknownFlags->setEnabled(false);
|
||||
ui->calculateUnintentionalLeaks->setEnabled(false);
|
||||
|
||||
p_profile->session->setBackupCardData(true);
|
||||
ui->createSDBackups->setChecked(true);
|
||||
ui->createSDBackups->setEnabled(false);
|
||||
|
||||
}
|
||||
ui->resmedPrefCalcsNotice->setVisible(haveResMed);
|
||||
#endif
|
||||
|
||||
ui->culminativeIndices->setEnabled(false);
|
||||
|
||||
QLocale locale = QLocale::system();
|
||||
QString shortformat = locale.dateFormat(QLocale::ShortFormat);
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="importTab">
|
||||
<attribute name="title">
|
||||
@ -1311,20 +1311,6 @@ Defaults to 60 minutes.. Highly recommend it's left at this value.</string>
|
||||
<string>Preferred Calculation Methods</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_42">
|
||||
<property name="text">
|
||||
<string>Upper Percentile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_43">
|
||||
<property name="text">
|
||||
<string>Maximum Calcs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="prefCalcPercentile">
|
||||
<property name="toolTip">
|
||||
@ -1355,6 +1341,27 @@ as this is the only value available on summary-only days.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_43">
|
||||
<property name="text">
|
||||
<string>Maximum Calcs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_42">
|
||||
<property name="text">
|
||||
<string>Upper Percentile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_29">
|
||||
<property name="text">
|
||||
<string>Culminative Indices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="prefCalcMiddle">
|
||||
<property name="toolTip">
|
||||
@ -1430,16 +1437,41 @@ as this is the only value available on summary-only days.</string>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_29">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="resmedPrefCalcsNotice">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Culminative Indices</string>
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">Note: </span>Due to summary design limitations, ResMed machines do not support changing these settings.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
Loading…
Reference in New Issue
Block a user