mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-09 12:40:43 +00:00
Session times replacement chart (work in progress)
This commit is contained in:
parent
5601be1b91
commit
23ce39efad
@ -32,6 +32,7 @@
|
||||
#include "Graphs/glcommon.h"
|
||||
#include "Graphs/gLineChart.h"
|
||||
#include "Graphs/gSummaryChart.h"
|
||||
#include "Graphs/gSessionTimesChart.h"
|
||||
#include "Graphs/gYAxis.h"
|
||||
#include "Graphs/gFlagsLine.h"
|
||||
#include "SleepLib/profiles.h"
|
||||
@ -1269,6 +1270,9 @@ void gGraphView::paintGL()
|
||||
|
||||
if (!graphs_drawn) { // No graphs drawn? show something useful :)
|
||||
QString txt = QObject::tr("SleepyHead is proudly brought to you by JediMark.");
|
||||
if (emptyText() == STR_Empty_Brick) {
|
||||
txt += "\nI'm very sorry your machine doesn't record useful data to graph in Daily View :(";
|
||||
}
|
||||
|
||||
// int x2, y2;
|
||||
// GetTextExtent(m_emptytext, x2, y2, bigfont);
|
||||
@ -1868,9 +1872,11 @@ void gGraphView::populateMenu(gGraph * graph)
|
||||
|
||||
gLineChart * lc = dynamic_cast<gLineChart *>(findLayer(graph,LT_LineChart));
|
||||
SummaryChart * sc = dynamic_cast<SummaryChart *>(findLayer(graph,LT_SummaryChart));
|
||||
gSessionTimesChart * stg = dynamic_cast<gSessionTimesChart *>(findLayer(graph,LT_SessionTimes));
|
||||
|
||||
|
||||
limits_menu->clear();
|
||||
if (lc || sc) {
|
||||
if (lc || sc || stg) {
|
||||
QWidgetAction * widget = new QWidgetAction(this);
|
||||
MinMaxWidget * minmax = new MinMaxWidget(graph, this);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
gSessionTimesChart::gSessionTimesChart(QString label, MachineType machtype)
|
||||
:Layer(NoChannel), m_label(label), m_machtype(machtype)
|
||||
{
|
||||
m_layertype = LT_SessionTimes;
|
||||
QDateTime d1 = QDateTime::currentDateTime();
|
||||
QDateTime d2 = d1;
|
||||
d1.setTimeSpec(Qt::UTC); // CHECK: Does this deal with DST?
|
||||
@ -34,106 +35,213 @@ void gSessionTimesChart::SetDay(Day *unused_day)
|
||||
Q_UNUSED(unused_day)
|
||||
Layer::SetDay(nullptr);
|
||||
|
||||
QDate firstday = p_profile->FirstDay(m_machtype);
|
||||
QDate lastday = p_profile->LastDay(m_machtype);
|
||||
firstday = p_profile->FirstDay(m_machtype);
|
||||
lastday = p_profile->LastDay(m_machtype);
|
||||
|
||||
QDate date = firstday;
|
||||
do {
|
||||
QMap<QDate, Day *>::iterator di = p_profile->daylist.find(date);
|
||||
Day * day = di.value();
|
||||
if (di == p_profile->daylist.end()) {
|
||||
}
|
||||
} while ((date = date.addDays(1)) < lastday);
|
||||
|
||||
m_minx = QDateTime(firstday, QTime(0,0,0)).toMSecsSinceEpoch();
|
||||
m_maxx = QDateTime(lastday, QTime(23,59,59)).toMSecsSinceEpoch();
|
||||
m_miny = 0;
|
||||
m_maxy = 30;
|
||||
m_empty = false;
|
||||
|
||||
// Get list of valid day records in supplied date range
|
||||
QList<Day *> daylist = p_profile->getDays(m_machtype, firstday, lastday);
|
||||
|
||||
if (daylist.size() == 0) {
|
||||
m_miny = m_maxy = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int cnt = 0;
|
||||
bool first = true;
|
||||
QList<Day *>::iterator end = daylist.end();
|
||||
|
||||
quint32 dn; // day number
|
||||
|
||||
// For each day
|
||||
for (QList<Day *>::iterator it = daylist.begin(); it != end; ++it) {
|
||||
Day * day = (*it);
|
||||
|
||||
if (day->size() == 0) continue;
|
||||
dn = day->first() / 86400000L;
|
||||
|
||||
// For each session
|
||||
for (int i=0; i < day->size(); i++) {
|
||||
Session * session = (*day)[i];
|
||||
if (!session->enabled()) continue;
|
||||
|
||||
// calculate start and end hours
|
||||
float start = ((session->first() / 1000L) % 86400) / 3600.0;
|
||||
float end = ((session->last() / 1000L) % 86400) / 3600.0;
|
||||
|
||||
// apply tzoffset??
|
||||
|
||||
// update min & max Y values
|
||||
if (first) {
|
||||
first = false;
|
||||
m_miny = start;
|
||||
m_maxy = end;
|
||||
} else {
|
||||
if (start < m_miny) m_miny = start;
|
||||
if (end > m_maxy) m_maxy = end;
|
||||
}
|
||||
|
||||
sessiontimes[cnt].push_back(TimeSpan(start,end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gSessionTimesChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
|
||||
QColor brighten(QColor color, float mult = 2.0);
|
||||
|
||||
|
||||
void gSessionTimesChart::paint(QPainter &painter, gGraph &graph, const QRegion ®ion)
|
||||
{
|
||||
Q_UNUSED(painter)
|
||||
Q_UNUSED(w)
|
||||
Q_UNUSED(region)
|
||||
QRect rect = region.boundingRect();
|
||||
|
||||
QMap<quint32, QList<TimeSpan> >::iterator st_end = sessiontimes.end();
|
||||
QMap<quint32, QList<TimeSpan> >::iterator it;
|
||||
painter.setPen(QColor(Qt::black));
|
||||
painter.drawRect(rect);
|
||||
|
||||
m_minx = graph.min_x;
|
||||
m_maxx = graph.max_x;
|
||||
|
||||
EventDataType miny;
|
||||
EventDataType maxy;
|
||||
|
||||
graph.roundY(miny, maxy);
|
||||
|
||||
QDateTime date2 = QDateTime::fromMSecsSinceEpoch(m_minx);
|
||||
QDateTime enddate2 = QDateTime::fromMSecsSinceEpoch(m_maxx);
|
||||
|
||||
QDate date = date2.date();
|
||||
QDate enddate = enddate2.date();
|
||||
|
||||
QString text = QString("Work in progress, I know about the bugs :P There is a very good and urgent reason for redoing this graph... "); //.arg(date2.toString("yyyyMMdd hh:mm:ss")).arg(enddate2.toString("yyyyMMdd hh:mm:ss"));
|
||||
painter.setFont(*defaultfont);
|
||||
painter.drawText(rect.left(), rect.top()-4, text);
|
||||
|
||||
int days = ceil(double(m_maxx - m_minx) / 86400000.0);
|
||||
|
||||
float barw = float(rect.width()) / float(days);
|
||||
|
||||
QTime split = p_profile->session->daySplitTime();
|
||||
QDateTime splittime;
|
||||
|
||||
|
||||
for (it = sessiontimes.begin(); it != st_end; ++it) {
|
||||
// int dn = it.key();
|
||||
QList<TimeSpan> & st = it.value();
|
||||
int stsize = st.size();
|
||||
//float maxy = m_maxy;
|
||||
float ymult = float(rect.height()) / (maxy-miny);
|
||||
|
||||
// Skip if empty
|
||||
if (stsize == 0) continue;
|
||||
int dn = 0;
|
||||
float lasty1 = rect.bottom();
|
||||
float lastx1 = rect.left();
|
||||
|
||||
do {
|
||||
QMap<QDate, Day *>::iterator di = p_profile->daylist.find(date);
|
||||
|
||||
if (di == p_profile->daylist.end()) {
|
||||
dn++;
|
||||
lasty1 = rect.bottom();
|
||||
lastx1 += barw;
|
||||
continue;
|
||||
}
|
||||
Day * day = di.value();
|
||||
// if (day->first() > m_maxx) { //|| (day->last() < m_minx)) {
|
||||
// continue;
|
||||
// }
|
||||
splittime = QDateTime(date, split);
|
||||
|
||||
float x1 = lastx1 + barw;
|
||||
QList<Session *>::iterator si;
|
||||
|
||||
|
||||
}
|
||||
if ((lastx1 + barw) > (rect.left()+rect.width()+1))
|
||||
break;
|
||||
bool hl = false;
|
||||
|
||||
QPoint mouse = graph.graphView()->currentMousePos();
|
||||
|
||||
QRect rec2(lastx1, rect.top(), barw, rect.height());
|
||||
if (rec2.contains(mouse)) {
|
||||
QColor col2(255,0,0,64);
|
||||
painter.fillRect(rec2, QBrush(col2));
|
||||
hl = true;
|
||||
}
|
||||
|
||||
bool haveoxi = day->hasMachine(MT_OXIMETER);
|
||||
|
||||
QColor goodcolor = haveoxi ? QColor(128,196,255) : Qt::blue;
|
||||
|
||||
for (si = day->begin(); si != day->end(); ++si) {
|
||||
Session *sess = (*si);
|
||||
if (!sess->enabled() || (sess->machine()->type() != m_machtype)) continue;
|
||||
|
||||
int slize = sess->m_slices.size();
|
||||
if (slize > 0) {
|
||||
// segments
|
||||
for (int i=0; i<slize; ++i) {
|
||||
const SessionSlice & slice = sess->m_slices.at(i);
|
||||
float s1 = float(splittime.secsTo(QDateTime::fromMSecsSinceEpoch(slice.start))) / 3600.0;
|
||||
|
||||
float s2 = double(slice.end - slice.start) / 3600000.0;
|
||||
|
||||
float y1 = (s1 * ymult);
|
||||
float y2 = (s2 * ymult);
|
||||
|
||||
QColor col = (slice.status == EquipmentOn) ? goodcolor : Qt::black;
|
||||
QColor col2 = brighten(col,2.5);
|
||||
|
||||
|
||||
QRect rec(lastx1, rect.bottom() - y1 - y2, barw, y2);
|
||||
QLinearGradient gradient(lastx1, rect.bottom(), lastx1+barw, rect.bottom());
|
||||
|
||||
if (rec.contains(mouse)) {
|
||||
// if (hl) {
|
||||
col = Qt::yellow;
|
||||
}
|
||||
|
||||
gradient.setColorAt(0,col);
|
||||
gradient.setColorAt(1,col2);
|
||||
painter.fillRect(rec, QBrush(gradient));
|
||||
painter.setPen(QPen(Qt::black,1));
|
||||
painter.drawRect(rec);
|
||||
|
||||
}
|
||||
} else {
|
||||
qint64 sf = sess->first();
|
||||
QDateTime st = QDateTime::fromMSecsSinceEpoch(sf);
|
||||
float s1 = float(splittime.secsTo(st)) / 3600.0;
|
||||
|
||||
float s2 = sess->hours();
|
||||
|
||||
float y1 = (s1 * ymult);
|
||||
float y2 = (s2 * ymult);
|
||||
|
||||
QColor col = goodcolor;
|
||||
|
||||
QLinearGradient gradient(lastx1, rect.bottom(), lastx1+barw, rect.bottom());
|
||||
QRect rec(lastx1, rect.bottom() - y1 - y2, barw, y2);
|
||||
if (rec.contains(mouse)) {
|
||||
QString text = QObject::tr("%1\nBedtime:%2\nLength:%3").arg(st.date().toString(Qt::SystemLocaleDate)).arg(st.time().toString("hh:mm:ss")).arg(s2,0,'f',2);
|
||||
graph.ToolTip(text,mouse.x() - 15,mouse.y() + 15, TT_AlignRight);
|
||||
|
||||
col = QColor("gold");
|
||||
}
|
||||
|
||||
QColor col2 = brighten(col,2.5);
|
||||
|
||||
gradient.setColorAt(0,col);
|
||||
gradient.setColorAt(1,col2);
|
||||
|
||||
painter.fillRect(rec, QBrush(gradient));
|
||||
painter.setPen(QPen(Qt::black,1));
|
||||
painter.drawRect(rec);
|
||||
|
||||
|
||||
// no segments
|
||||
}
|
||||
}
|
||||
|
||||
// float y = double(day->total_time(m_machtype)) / 3600000.0;
|
||||
// float y1 = rect.bottom() - (y * ymult);
|
||||
// float x1 = lastx1 + barw;
|
||||
// painter.drawLine(lastx1, lasty1, lastx1,y1);
|
||||
// painter.drawLine(lastx1, y1, x1, y1);
|
||||
|
||||
dn++;
|
||||
// lasty1 = y1;
|
||||
lastx1 = x1;
|
||||
|
||||
} while ((date = date.addDays(1)) <= enddate);
|
||||
|
||||
}
|
||||
|
||||
bool gSessionTimesChart::keyPressEvent(QKeyEvent *event, gGraph *graph)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
Q_UNUSED(graph)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gSessionTimesChart::mouseMoveEvent(QMouseEvent *event, gGraph *graph)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
Q_UNUSED(graph)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gSessionTimesChart::mousePressEvent(QMouseEvent *event, gGraph *graph)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
Q_UNUSED(graph)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gSessionTimesChart::mouseReleaseEvent(QMouseEvent *event, gGraph *graph)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
Q_UNUSED(graph)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,8 @@ protected:
|
||||
int hl_day;
|
||||
int tz_offset;
|
||||
float tz_hours;
|
||||
QDate firstday;
|
||||
QDate lastday;
|
||||
QMap<quint32, QList<TimeSpan> > sessiontimes;
|
||||
};
|
||||
|
||||
|
@ -320,11 +320,11 @@ const QString gYAxisTime::Format(EventDataType v, int dp)
|
||||
int m = int(v * 60) % 60;
|
||||
int s = int(v * 3600) % 60;
|
||||
|
||||
char pm[3] = {"am"};
|
||||
char pm[3] = {"pm"};
|
||||
|
||||
if (show_12hr) {
|
||||
|
||||
h >= 12 ? pm[0] = 'p' : pm[0] = 'a';
|
||||
h >= 12 ? pm[0] = 'a' : pm[0] = 'p';
|
||||
h %= 12;
|
||||
|
||||
if (h == 0) { h = 12; }
|
||||
|
@ -26,7 +26,7 @@ enum LayerPosition { LayerLeft, LayerRight, LayerTop, LayerBottom, LayerCenter,
|
||||
|
||||
enum ToolTipAlignment { TT_AlignCenter, TT_AlignLeft, TT_AlignRight };
|
||||
|
||||
enum LayerType { LT_Other = 0, LT_LineChart, LT_SummaryChart, LT_EventFlags, LT_Spacer };
|
||||
enum LayerType { LT_Other = 0, LT_LineChart, LT_SummaryChart, LT_EventFlags, LT_Spacer, LT_SessionTimes };
|
||||
|
||||
/*! \class Layer
|
||||
\brief The base component for all individual Graph layers
|
||||
|
@ -91,6 +91,8 @@ class Day
|
||||
//! \brief Returns if the cache contains SummaryType information about the requested code
|
||||
bool hasData(ChannelID code, SummaryType type);
|
||||
|
||||
inline bool hasMachine(MachineType mt) const { return machines.contains(mt); }
|
||||
|
||||
//! \brief Returns the Average of all Sessions setting 'code' for this day
|
||||
EventDataType settings_avg(ChannelID code);
|
||||
|
||||
@ -278,7 +280,9 @@ class Day
|
||||
int useCounter() { return d_useCounter; }
|
||||
|
||||
protected:
|
||||
//! \brief A Vector containing all sessions for this day
|
||||
|
||||
|
||||
|
||||
QHash<ChannelID, QHash<EventDataType, EventDataType> > perc_cache;
|
||||
//qint64 d_first,d_last;
|
||||
private:
|
||||
|
@ -1173,13 +1173,39 @@ bool PRS1Import::ParseCompliance()
|
||||
session->settings[PRS1_HumidStatus] = (bool)(data[0x0A] & 0x80); // Humidifier Connected
|
||||
session->settings[PRS1_HumidLevel] = (int)(data[0x0A] & 7); // Humidifier Value
|
||||
|
||||
// need to parse a repeating structure here containing lengths of mask on/off..
|
||||
// 0x03 = mask on
|
||||
// 0x01 = mask off
|
||||
|
||||
// This is probably wrong
|
||||
summary_duration = data[0x12] | data[0x13] << 8;
|
||||
qint64 start = qint64(compliance->timestamp) * 1000L;
|
||||
qint64 tt = start;
|
||||
|
||||
session->set_first(qint64(compliance->timestamp) * 1000L);
|
||||
session->set_last(qint64(compliance->timestamp + (summary_duration * 2)) * 1000L);
|
||||
int len = compliance->size()-3;
|
||||
int pos = 0x11;
|
||||
do {
|
||||
quint8 c = data[pos++];
|
||||
quint64 duration = data[pos] | data[pos+1] << 8;
|
||||
pos+=2;
|
||||
duration *= 1000L;
|
||||
SliceStatus status;
|
||||
if (c == 0x03) {
|
||||
status = EquipmentOn;
|
||||
} else if (c == 0x02) {
|
||||
status = EquipmentLeaking;
|
||||
} else if (c == 0x01) {
|
||||
status = EquipmentOff;
|
||||
} else {
|
||||
qDebug() << compliance->sessionid << "Wasn't expecting" << c;
|
||||
break;
|
||||
}
|
||||
session->m_slices.append(SessionSlice(tt, tt + duration, status));
|
||||
qDebug() << compliance->sessionid << "Added Slice" << tt << (tt+duration) << status;
|
||||
|
||||
tt += duration;
|
||||
} while (pos < len);
|
||||
|
||||
session->set_first(start);
|
||||
session->set_last(tt);
|
||||
|
||||
// Bleh!! There is probably 10 different formats for these useless piece of junk machines
|
||||
return true;
|
||||
|
@ -23,6 +23,29 @@
|
||||
//class EventList;
|
||||
class Machine;
|
||||
|
||||
enum SliceStatus {
|
||||
UnknownStatus=0, EquipmentOff, EquipmentLeaking, EquipmentOn
|
||||
};
|
||||
|
||||
class SessionSlice
|
||||
{
|
||||
public:
|
||||
SessionSlice() {
|
||||
start = end = 0;
|
||||
status = UnknownStatus;
|
||||
}
|
||||
SessionSlice(const SessionSlice & copy) {
|
||||
start = copy.start;
|
||||
end = copy.end;
|
||||
status = copy.status;
|
||||
}
|
||||
SessionSlice(qint64 start, qint64 end, SliceStatus status):start(start), end(end), status(status) {}
|
||||
|
||||
qint64 start;
|
||||
qint64 end;
|
||||
SliceStatus status;
|
||||
};
|
||||
|
||||
/*! \class Session
|
||||
\brief Contains a single Sessions worth of machine event/waveform information.
|
||||
|
||||
@ -179,6 +202,8 @@ class Session
|
||||
|
||||
QList<ChannelID> m_availableChannels;
|
||||
|
||||
QList<SessionSlice> m_slices;
|
||||
|
||||
const QList<ChannelID> & availableChannels() { return m_availableChannels; }
|
||||
|
||||
//! \brief Generates sum and time data for each distinct value in 'code' events..
|
||||
|
@ -124,6 +124,10 @@ Overview::Overview(QWidget *parent, gGraphView *shared) :
|
||||
}
|
||||
|
||||
|
||||
STG = createGraph("New Session", tr("Session Times2"), tr("Session Times"), YT_Time);
|
||||
stg = new gSessionTimesChart("STG", MT_CPAP);
|
||||
STG->AddLayer(stg);
|
||||
|
||||
UC = createGraph(STR_GRAPH_Usage, tr("Usage"), tr("Usage\n(hours)"));
|
||||
|
||||
FL = createGraph(schema::channel[CPAP_FlowLimit].code(), schema::channel[CPAP_FlowLimit].label(), STR_TR_FlowLimit);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "SleepLib/profiles.h"
|
||||
#include "Graphs/gGraphView.h"
|
||||
#include "Graphs/gSummaryChart.h"
|
||||
#include "Graphs/gSessionTimesChart.h"
|
||||
|
||||
namespace Ui {
|
||||
class Overview;
|
||||
@ -62,11 +63,13 @@ class Overview : public QWidget
|
||||
gGraph *createGraph(QString code, QString name, QString units = "", YTickerType yttype = YT_Number);
|
||||
gGraph *AHI, *AHIHR, *UC, *FL, *SA, *US, *PR, *LK, *NPB, *SET, *SES, *RR, *MV, *TV, *PTB, *PULSE, *SPO2, *NLL,
|
||||
// gGraph *AHI, *AHIHR, *UC, *FL, *US, *PR, *LK, *NPB, *SET, *SES, *RR, *MV, *TV, *PTB, *PULSE, *SPO2,
|
||||
*WEIGHT, *ZOMBIE, *BMI, *TGMV, *TOTLK;
|
||||
*WEIGHT, *ZOMBIE, *BMI, *TGMV, *TOTLK, *STG;
|
||||
SummaryChart *bc, *uc, *fl, *sa, *us, *pr, *lk, *npb, *set, *ses, *rr, *mv, *tv, *ptb, *pulse, *spo2,
|
||||
// SummaryChart *bc, *uc, *fl, *us, *pr, *lk, *npb, *set, *ses, *rr, *mv, *tv, *ptb, *pulse, *spo2,
|
||||
*weight, *zombie, *bmi, *ahihr, *tgmv, *totlk, *nll;
|
||||
|
||||
gSessionTimesChart * stg;
|
||||
|
||||
//! \breif List of SummaryCharts shown on the overview page
|
||||
QVector<SummaryChart *> OverviewCharts;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user