Convert compliance "slices" to events with a starting delta.

Also fix the related enums and add more value checks.
Also add YAML output of the cumulative mask-on slice time.
This commit is contained in:
sawinglogz 2019-06-03 20:12:17 -04:00
parent a6455b6b05
commit 1a0a4bbf52
5 changed files with 59 additions and 35 deletions

View File

@ -896,10 +896,10 @@ void gSessionTimesChart::paint(QPainter &painter, gGraph &graph, const QRegion &
float s2 = double(slice.end - slice.start) / 3600000.0;
QColor col = (slice.status == EquipmentOn) ? goodcolor : Qt::black;
QColor col = (slice.status == MaskOn) ? goodcolor : Qt::black;
QString txt = QObject::tr("%1\nLength: %3\nStart: %2\n").arg(datestr).arg(st.time().toString("hh:mm:ss")).arg(s2,0,'f',2);
txt += (slice.status == EquipmentOn) ? QObject::tr("Mask On") : QObject::tr("Mask Off");
txt += (slice.status == MaskOn) ? QObject::tr("Mask On") : QObject::tr("Mask Off");
slices.append(SummaryChartSlice(&calcitems[0], s1, s2, txt, col));
}
} else {

View File

@ -657,7 +657,7 @@ qint64 Day::total_time()
}
} else {
for (auto & slice : sess->m_slices) {
if (slice.status == EquipmentOn) {
if (slice.status == MaskOn) {
range.insert(slice.start, 0);
range.insert(slice.end, 1);
d_totaltime += slice.end - slice.start;
@ -727,7 +727,7 @@ qint64 Day::total_time(MachineType type)
}
} else {
for (const auto & slice : sess->m_slices) {
if (slice.status == EquipmentOn) {
if (slice.status == MaskOn) {
range.insert(slice.start, 0);
range.insert(slice.end, 1);
d_totaltime += slice.end - slice.start;

View File

@ -1384,19 +1384,18 @@ public:
}
};
class PRS1ParsedSliceEvent : public PRS1ParsedDurationEvent
class PRS1ParsedSliceEvent : public PRS1ParsedValueEvent
{
public:
virtual QMap<QString,QString> contents(void)
{
QMap<QString,QString> out;
out["start"] = timeStr(m_start);
out["duration"] = timeStr(m_duration);
QString s;
switch (m_status) {
case EquipmentOn: s = "EquipmentOn"; break;
switch ((SliceStatus) m_value) {
case MaskOn: s = "MaskOn"; break;
case MaskOff: s = "MaskOff"; break;
case EquipmentOff: s = "EquipmentOff"; break;
case EquipmentLeaking: s = "EquipmentLeaking"; break;
case UnknownStatus: s = "Unknown"; break;
}
out["status"] = s;
@ -1404,9 +1403,8 @@ public:
}
static const PRS1ParsedEventType TYPE = EV_PRS1_SLICE;
SliceStatus m_status;
PRS1ParsedSliceEvent(int start, int duration, SliceStatus status) : PRS1ParsedDurationEvent(TYPE, start, duration), m_status(status) {}
PRS1ParsedSliceEvent(int start, SliceStatus status) : PRS1ParsedValueEvent(TYPE, start, (int) status) {}
};
@ -3169,8 +3167,11 @@ bool PRS1Import::ImportCompliance()
if (e->m_type == PRS1ParsedSliceEvent::TYPE) {
PRS1ParsedSliceEvent* s = (PRS1ParsedSliceEvent*) e;
qint64 tt = start + qint64(s->m_start) * 1000L;
qint64 duration = qint64(s->m_duration) * 1000L;
session->m_slices.append(SessionSlice(tt, tt + duration, s->m_status));
if (!session->m_slices.isEmpty()) {
SessionSlice & prevSlice = session->m_slices.last();
prevSlice.end = tt;
}
session->m_slices.append(SessionSlice(tt, tt, (SliceStatus) s->m_value));
continue;
} else if (e->m_type != PRS1ParsedSettingEvent::TYPE) {
qWarning() << "Compliance had non-setting event:" << (int) e->m_type;
@ -3267,9 +3268,6 @@ bool PRS1DataChunk::ParseCompliance(void)
CHECK_VALUE(data[0x0b], 1);
CHECK_VALUE(data[0x0c], 0);
CHECK_VALUE(data[0x0d], 0);
CHECK_VALUE(data[0x0e], 2);
CHECK_VALUE(data[0x0f], 0);
CHECK_VALUE(data[0x10], 0);
// TODO: What are slices, and why would only bricks have them? That seems very weird.
@ -3281,31 +3279,32 @@ bool PRS1DataChunk::ParseCompliance(void)
int tt = start;
int len = this->size()-3;
int pos = 0x11;
int pos = 0x0e;
do {
quint8 c = data[pos++];
// TODO: This isn't duration, it's a start time! Why else would an EquipmentOff
// slice have a nonzero value here? In one session, there's a big black span
// during which the machine is counting blower time but not usage, corresponding
// to the EquipmentOff delta. So these aren't slices with durations, they're events
// with a delta offset!
int duration = data[pos] | data[pos+1] << 8;
// These aren't really slices as originally thought, they're events with a delta offset.
// We'll convert them to slices in the importer.
int delta = data[pos] | data[pos+1] << 8;
pos+=2;
SliceStatus status;
if (c == 0x03) {
status = EquipmentOn;
} else if (c == 0x02) {
status = EquipmentLeaking;
if (c == 0x02) {
status = MaskOn;
if (tt == 0) {
CHECK_VALUE(delta, 0); // we've never seen the initial MaskOn have any delta
} else {
if (delta % 60) UNEXPECTED_VALUE(delta, "even minutes"); // mask-off events seem to be whole minutes?
}
} else if (c == 0x03) {
status = MaskOff;
} else if (c == 0x01) {
status = EquipmentOff;
CHECK_VALUE(duration, 0);
// This has a delta if the mask was removed before the machine was shut off.
} else {
qDebug() << this->sessionid << "unknown slice status" << c;
break;
}
this->AddEvent(new PRS1ParsedSliceEvent(tt, duration, status));
tt += duration;
tt += delta;
this->AddEvent(new PRS1ParsedSliceEvent(tt, status));
} while (pos < len);
// also seems to be a trailing 01 00 81 after the slices?
@ -3563,6 +3562,8 @@ void PRS1DataChunk::ParseFlexSetting(quint8 flex, CPAPMode cpapmode)
// c0 Split CFlex then None
// c8 Split CFlex+ then None
if (flex & (0x20 | 0x04)) UNEXPECTED_VALUE(flex, "known bits");
flex &= 0xf8;
bool split = false;
@ -3591,11 +3592,18 @@ void PRS1DataChunk::ParseFlexSetting(quint8 flex, CPAPMode cpapmode)
void PRS1DataChunk::ParseHumidifierSetting(int humid, bool supportsHeatedTubing)
{
if (humid & (0x40 | 0x20 | 0x08)) UNEXPECTED_VALUE(humid, "known bits");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HUMID_STATUS, (humid & 0x80) != 0)); // Humidifier Connected
if (supportsHeatedTubing) {
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HEATED_TUBING, (humid & 0x10) != 0)); // Heated Hose??
} else {
CHECK_VALUE(humid & 0x10, 0);
}
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HUMID_LEVEL, (humid & 7))); // Humidifier Value
int humidlevel = humid & 7;
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HUMID_LEVEL, humidlevel)); // Humidifier Value
if (humidlevel > 5) UNEXPECTED_VALUE(humidlevel, "<= 5");
}

View File

@ -24,7 +24,7 @@
class Machine;
enum SliceStatus {
UnknownStatus=0, EquipmentOff, EquipmentLeaking, EquipmentOn
UnknownStatus=0, EquipmentOff, MaskOn, MaskOff // is there an EquipmentOn?
};
class SessionSlice
@ -137,7 +137,7 @@ class Session
// t = 0;
// for (int i=0; i<size; ++i) {
// const SessionSlice & slice = m_slices.at(i);
// if (slice.status == EquipmentOn) {
// if (slice.status == MaskOn) {
// t += slice.end - slice.start;
// }
// }
@ -187,7 +187,7 @@ class Session
t = 0;
for (int i=0; i<size; ++i) {
const SessionSlice & slice = m_slices.at(i);
if (slice.status == EquipmentOn) {
if (slice.status == MaskOn) {
t += slice.end - slice.start;
}
}

View File

@ -19,6 +19,17 @@ static QString hex(int i)
return QString("0x") + QString::number(i, 16).toUpper();
}
static QString dur(qint64 msecs)
{
qint64 s = msecs / 1000L;
int h = s / 3600; s -= h * 3600;
int m = s / 60; s -= m * 60;
return QString("%1:%2:%3")
.arg(h, 2, 10, QChar('0'))
.arg(m, 2, 10, QChar('0'))
.arg(s, 2, 10, QChar('0'));
}
#define ENUMSTRING(ENUM) case ENUM: s = #ENUM; break
static QString eventListTypeName(EventListType t)
{
@ -171,6 +182,11 @@ void SessionToYaml(QString filepath, Session* session)
out << " start: " << ts(session->first()) << endl;
out << " end: " << ts(session->last()) << endl;
Day day;
day.addSession(session);
out << " total_time: " << dur(day.total_time()) << endl;
day.removeSession(session);
out << " settings:" << endl;
// We can't get deterministic ordering from QHash iterators, so we need to create a list