Add YAML output for PRS1DataChunks.

Also move a data check from PRS1Import::ImportSummary to PRS1DataChunk::ParseSummary
to prevent crashing in regression tests.
This commit is contained in:
sawinglogz 2019-05-29 12:11:53 -04:00
parent ea008273ab
commit 135ac25c1e
3 changed files with 261 additions and 12 deletions

View File

@ -1180,6 +1180,15 @@ enum PRS1ParsedSettingType
PRS1_SETTING_SHOW_AHI, PRS1_SETTING_SHOW_AHI,
}; };
#if UNITTEST_MODE
static QString timeStr(int t);
static QString byteList(QByteArray data, int limit=-1);
static QString hex(int i);
static QString parsedSettingTypeName(PRS1ParsedSettingType t);
#endif
class PRS1ParsedEvent class PRS1ParsedEvent
{ {
public: public:
@ -1196,19 +1205,31 @@ public:
static const PRS1ParsedEventType TYPE = EV_PRS1_UNKNOWN; static const PRS1ParsedEventType TYPE = EV_PRS1_UNKNOWN;
static constexpr float GAIN = 1.0; static constexpr float GAIN = 1.0;
static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_NONE; static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_NONE;
virtual QMap<QString,QString> contents(void) = 0;
protected: protected:
PRS1ParsedEvent(PRS1ParsedEventType type, int start) PRS1ParsedEvent(PRS1ParsedEventType type, int start)
: m_type(type), m_start(start), m_duration(0), m_value(0), m_offset(0.0), m_gain(GAIN), m_unit(UNIT) : m_type(type), m_start(start), m_duration(0), m_value(0), m_offset(0.0), m_gain(GAIN), m_unit(UNIT)
{ {
} }
~PRS1ParsedEvent() virtual ~PRS1ParsedEvent()
{ {
} }
}; };
class PRS1ParsedDurationEvent : public PRS1ParsedEvent class PRS1ParsedDurationEvent : public PRS1ParsedEvent
{ {
public:
virtual QMap<QString,QString> contents(void)
{
QMap<QString,QString> out;
out["start"] = timeStr(m_start);
out["duration"] = timeStr(m_duration);
return out;
}
protected: protected:
static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_S; static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_S;
@ -1216,22 +1237,51 @@ protected:
}; };
const PRS1ParsedEventUnit PRS1ParsedDurationEvent::UNIT; const PRS1ParsedEventUnit PRS1ParsedDurationEvent::UNIT;
class PRS1ParsedValueEvent : public PRS1ParsedEvent class PRS1ParsedValueEvent : public PRS1ParsedEvent
{ {
public:
virtual QMap<QString,QString> contents(void)
{
QMap<QString,QString> out;
out["start"] = timeStr(m_start);
out["value"] = QString::number(value());
return out;
}
protected: protected:
PRS1ParsedValueEvent(PRS1ParsedEventType type, int start, int value) : PRS1ParsedEvent(type, start) { m_value = value; } PRS1ParsedValueEvent(PRS1ParsedEventType type, int start, int value) : PRS1ParsedEvent(type, start) { m_value = value; }
}; };
class PRS1UnknownValueEvent : public PRS1ParsedValueEvent class PRS1UnknownValueEvent : public PRS1ParsedValueEvent
{ {
public: public:
virtual QMap<QString,QString> contents(void)
{
QMap<QString,QString> out;
out["start"] = timeStr(m_start);
out["code"] = hex(m_code);
out["value"] = QString::number(value());
return out;
}
int m_code; int m_code;
PRS1UnknownValueEvent(int code, int start, int value, float gain=1.0) : PRS1ParsedValueEvent(TYPE, start, value), m_code(code) { m_gain = gain; } PRS1UnknownValueEvent(int code, int start, int value, float gain=1.0) : PRS1ParsedValueEvent(TYPE, start, value), m_code(code) { m_gain = gain; }
}; };
class PRS1UnknownDataEvent : public PRS1ParsedEvent class PRS1UnknownDataEvent : public PRS1ParsedEvent
{ {
public: public:
virtual QMap<QString,QString> contents(void)
{
QMap<QString,QString> out;
out["pos"] = QString::number(m_pos);
out["data"] = byteList(m_data);
return out;
}
static const PRS1ParsedEventType TYPE = EV_PRS1_RAW; static const PRS1ParsedEventType TYPE = EV_PRS1_RAW;
int m_pos; int m_pos;
@ -1282,6 +1332,13 @@ const PRS1ParsedEventType PRS1TidalVolumeEvent::TYPE;
class PRS1ParsedSettingEvent : public PRS1ParsedValueEvent class PRS1ParsedSettingEvent : public PRS1ParsedValueEvent
{ {
public: public:
virtual QMap<QString,QString> contents(void)
{
QMap<QString,QString> out;
out[parsedSettingTypeName(m_setting)] = QString::number(value());
return out;
}
static const PRS1ParsedEventType TYPE = EV_PRS1_SETTING; static const PRS1ParsedEventType TYPE = EV_PRS1_SETTING;
PRS1ParsedSettingType m_setting; PRS1ParsedSettingType m_setting;
@ -1305,6 +1362,22 @@ public:
class PRS1ParsedSliceEvent : public PRS1ParsedDurationEvent class PRS1ParsedSliceEvent : public PRS1ParsedDurationEvent
{ {
public: 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;
case EquipmentOff: s = "EquipmentOff"; break;
case EquipmentLeaking: s = "EquipmentLeaking"; break;
case UnknownStatus: s = "Unknown"; break;
}
out["status"] = s;
return out;
}
static const PRS1ParsedEventType TYPE = EV_PRS1_SLICE; static const PRS1ParsedEventType TYPE = EV_PRS1_SLICE;
SliceStatus m_status; SliceStatus m_status;
@ -1354,6 +1427,131 @@ PRS1_VALUE_EVENT(PRS1FlowRateEvent, EV_PRS1_FLOWRATE); // TODO: is this a singl
PRS1_VALUE_EVENT(PRS1Test1Event, EV_PRS1_TEST1); PRS1_VALUE_EVENT(PRS1Test1Event, EV_PRS1_TEST1);
PRS1_VALUE_EVENT(PRS1Test2Event, EV_PRS1_TEST2); PRS1_VALUE_EVENT(PRS1Test2Event, EV_PRS1_TEST2);
//********************************************************************************************
#if UNITTEST_MODE
static QString hex(int i)
{
return QString("0x") + QString::number(i, 16).toUpper();
}
#define ENUMSTRING(ENUM) case ENUM: s = #ENUM; break
static QString parsedEventTypeName(PRS1ParsedEventType t)
{
QString s;
switch (t) {
ENUMSTRING(EV_PRS1_RAW);
ENUMSTRING(EV_PRS1_UNKNOWN);
ENUMSTRING(EV_PRS1_TB);
ENUMSTRING(EV_PRS1_OA);
ENUMSTRING(EV_PRS1_CA);
ENUMSTRING(EV_PRS1_FL);
ENUMSTRING(EV_PRS1_PB);
ENUMSTRING(EV_PRS1_LL);
ENUMSTRING(EV_PRS1_HY);
ENUMSTRING(EV_PRS1_TOTLEAK);
ENUMSTRING(EV_PRS1_LEAK);
ENUMSTRING(EV_PRS1_PRESSURE);
ENUMSTRING(EV_PRS1_IPAP);
ENUMSTRING(EV_PRS1_IPAPLOW);
ENUMSTRING(EV_PRS1_IPAPHIGH);
ENUMSTRING(EV_PRS1_EPAP);
ENUMSTRING(EV_PRS1_FLEX);
ENUMSTRING(EV_PRS1_RR);
ENUMSTRING(EV_PRS1_PTB);
ENUMSTRING(EV_PRS1_MV);
ENUMSTRING(EV_PRS1_TV);
ENUMSTRING(EV_PRS1_SNORE);
ENUMSTRING(EV_PRS1_VS);
ENUMSTRING(EV_PRS1_PP);
ENUMSTRING(EV_PRS1_RERA);
ENUMSTRING(EV_PRS1_NRI);
ENUMSTRING(EV_PRS1_FLOWRATE);
ENUMSTRING(EV_PRS1_TEST1);
ENUMSTRING(EV_PRS1_TEST2);
ENUMSTRING(EV_PRS1_SETTING);
ENUMSTRING(EV_PRS1_SLICE);
default:
s = hex(t);
qDebug() << "Unknown PRS1ParsedEventType type:" << qPrintable(s);
return s;
}
return s.mid(8).toLower(); // lop off initial EV_PRS1_
}
static QString parsedSettingTypeName(PRS1ParsedSettingType t)
{
QString s;
switch (t) {
ENUMSTRING(PRS1_SETTING_CPAP_MODE);
ENUMSTRING(PRS1_SETTING_PRESSURE);
ENUMSTRING(PRS1_SETTING_PRESSURE_MIN);
ENUMSTRING(PRS1_SETTING_PRESSURE_MAX);
ENUMSTRING(PRS1_SETTING_EPAP);
ENUMSTRING(PRS1_SETTING_EPAP_MIN);
ENUMSTRING(PRS1_SETTING_EPAP_MAX);
ENUMSTRING(PRS1_SETTING_IPAP);
ENUMSTRING(PRS1_SETTING_IPAP_MIN);
ENUMSTRING(PRS1_SETTING_IPAP_MAX);
ENUMSTRING(PRS1_SETTING_PS);
ENUMSTRING(PRS1_SETTING_PS_MIN);
ENUMSTRING(PRS1_SETTING_PS_MAX);
ENUMSTRING(PRS1_SETTING_FLEX_MODE);
ENUMSTRING(PRS1_SETTING_FLEX_LEVEL);
ENUMSTRING(PRS1_SETTING_RAMP_TIME);
ENUMSTRING(PRS1_SETTING_RAMP_PRESSURE);
ENUMSTRING(PRS1_SETTING_HUMID_STATUS);
ENUMSTRING(PRS1_SETTING_HUMID_LEVEL);
ENUMSTRING(PRS1_SETTING_HEATED_TUBING);
ENUMSTRING(PRS1_SETTING_SYSTEMONE_RESIST_LOCK);
ENUMSTRING(PRS1_SETTING_SYSTEMONE_RESIST_SETTING);
ENUMSTRING(PRS1_SETTING_SYSTEMONE_RESIST_STATUS);
ENUMSTRING(PRS1_SETTING_HOSE_DIAMETER);
ENUMSTRING(PRS1_SETTING_AUTO_ON);
ENUMSTRING(PRS1_SETTING_AUTO_OFF);
ENUMSTRING(PRS1_SETTING_MASK_ALERT);
ENUMSTRING(PRS1_SETTING_SHOW_AHI);
default:
s = hex(t);
qDebug() << "Unknown PRS1ParsedSettingType type:" << qPrintable(s);
return s;
}
return s.mid(13).toLower(); // lop off initial PRS1_SETTING_
}
static QString timeStr(int t)
{
int h = t / 3600;
int m = (t - (h * 3600)) / 60;
int s = t % 60;
return QString("%1:%2:%3").arg(h, 2, 10, QChar('0')).arg(m, 2, 10, QChar('0')).arg(s, 2, 10, QChar('0'));
}
static QString byteList(QByteArray data, int limit)
{
int count = data.size();
if (limit == -1 || limit > count) limit = count;
QStringList l;
for (int i = 0; i < limit; i++) {
l.push_back(QString( "%1" ).arg((int) data[i] & 0xFF, 2, 16, QChar('0') ).toUpper());
}
if (limit < count) l.push_back("...");
QString s = l.join(" ");
return s;
}
QString _PRS1ParsedEventName(PRS1ParsedEvent* e)
{
return parsedEventTypeName(e->m_type);
}
QMap<QString,QString> _PRS1ParsedEventContents(PRS1ParsedEvent* e)
{
return e->contents();
}
#endif
//******************************************************************************************** //********************************************************************************************
@ -1530,6 +1728,7 @@ bool PRS1DataChunk::ParseEventsF5V3(void)
qDebug() << "1: (" << int(lastcode) << hex << lastpos << ")"; qDebug() << "1: (" << int(lastcode) << hex << lastpos << ")";
qDebug() << "2: (" << int(lastcode2) << hex << lastpos2 << ")"; qDebug() << "2: (" << int(lastcode2) << hex << lastpos2 << ")";
qDebug() << "3: (" << int(lastcode3) << hex << lastpos3 << ")"; qDebug() << "3: (" << int(lastcode3) << hex << lastpos3 << ")";
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos));
return false; return false;
} }
delta = buffer[pos]; delta = buffer[pos];
@ -1628,7 +1827,8 @@ bool PRS1DataChunk::ParseEventsF5V3(void)
break; break;
default: default:
qDebug() << "Unknown code:" << hex << code << "in" << this->sessionid << "at" << pos; qDebug() << "Unknown code:" << hex << code << "in" << this->sessionid << "at" << startpos;
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos));
} }
@ -1831,6 +2031,7 @@ bool PRS1DataChunk::ParseEventsF5V012(void)
qDebug() << "1: (" << int(lastcode) << hex << lastpos << ")"; qDebug() << "1: (" << int(lastcode) << hex << lastpos << ")";
qDebug() << "2: (" << int(lastcode2) << hex << lastpos2 << ")"; qDebug() << "2: (" << int(lastcode2) << hex << lastpos2 << ")";
qDebug() << "3: (" << int(lastcode3) << hex << lastpos3 << ")"; qDebug() << "3: (" << int(lastcode3) << hex << lastpos3 << ")";
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos));
return false; return false;
} }
@ -2072,6 +2273,7 @@ bool PRS1DataChunk::ParseEventsF5V012(void)
default: // ERROR!!! default: // ERROR!!!
qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos - 1; qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos - 1;
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos));
badcode = true; badcode = true;
break; break;
} }
@ -2745,6 +2947,7 @@ bool PRS1DataChunk::ParseEventsF0(CPAPMode mode)
qDebug() << "1: (" << hex << int(lastcode) << hex << lastpos << ")"; qDebug() << "1: (" << hex << int(lastcode) << hex << lastpos << ")";
qDebug() << "2: (" << hex << int(lastcode2) << hex << lastpos2 << ")"; qDebug() << "2: (" << hex << int(lastcode2) << hex << lastpos2 << ")";
qDebug() << "3: (" << hex << int(lastcode3) << hex << lastpos3 << ")"; qDebug() << "3: (" << hex << int(lastcode3) << hex << lastpos3 << ")";
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos));
return false; return false;
} }
@ -2922,6 +3125,7 @@ bool PRS1DataChunk::ParseEventsF0(CPAPMode mode)
// ERROR!!! // ERROR!!!
qWarning() << "Some new fandangled PRS1 code detected in" << this->sessionid << hex qWarning() << "Some new fandangled PRS1 code detected in" << this->sessionid << hex
<< int(code) << " at " << pos - 1; << int(code) << " at " << pos - 1;
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos));
return false; return false;
} }
} }
@ -3526,12 +3730,6 @@ bool PRS1Import::ImportSummary()
{ {
if (!summary) return false; if (!summary) return false;
// All machines have a first byte zero for clean summary
if (summary->m_data.constData()[0] != 0) {
qDebug() << "Non zero hblock[0] indicator";
return false;
}
session->set_first(qint64(summary->timestamp) * 1000L); session->set_first(qint64(summary->timestamp) * 1000L);
session->setPhysMax(CPAP_LeakTotal, 120); session->setPhysMax(CPAP_LeakTotal, 120);
@ -3657,6 +3855,12 @@ bool PRS1Import::ImportSummary()
bool PRS1DataChunk::ParseSummary() bool PRS1DataChunk::ParseSummary()
{ {
// All machines have a first byte zero for clean summary
if (this->m_data.constData()[0] != 0) {
qDebug() << "Non zero hblock[0] indicator";
return false;
}
// TODO: The below mainblock creation is probably wrong. It should move to to its own function when it gets fixed. // TODO: The below mainblock creation is probably wrong. It should move to to its own function when it gets fixed.
/* Example data block /* Example data block
000000c6@0000: 00 [10] 01 [00 01 02 01 01 00 02 01 00 04 01 40 07 000000c6@0000: 00 [10] 01 [00 01 02 01 01 00 02 01 00 04 01 40 07

View File

@ -193,6 +193,13 @@ protected:
bool ExtractStoredCrc(int size); bool ExtractStoredCrc(int size);
}; };
#if UNITTEST_MODE
QString _PRS1ParsedEventName(PRS1ParsedEvent* e);
QMap<QString,QString> _PRS1ParsedEventContents(PRS1ParsedEvent* e);
#endif
class PRS1Loader; class PRS1Loader;
/*! \class PRS1Import /*! \class PRS1Import

View File

@ -134,7 +134,7 @@ static QString byteList(QByteArray data, int limit=-1)
l.push_back(QString( "%1" ).arg((int) data[i] & 0xFF, 2, 16, QChar('0') ).toUpper()); l.push_back(QString( "%1" ).arg((int) data[i] & 0xFF, 2, 16, QChar('0') ).toUpper());
} }
if (limit < count) l.push_back("..."); if (limit < count) l.push_back("...");
QString s = l.join(""); QString s = l.join(" ");
return s; return s;
} }
@ -183,7 +183,36 @@ void ChunkToYaml(QFile & file, PRS1DataChunk* chunk)
} }
// data // data
out << " data: " << byteList(chunk->m_data, 100) << endl; bool dump_data = true;
if (chunk->m_parsedData.size() > 0) {
dump_data = false;
out << " events:" << endl;
for (auto & e : chunk->m_parsedData) {
QString name = _PRS1ParsedEventName(e);
if (name == "raw" || name == "unknown") {
dump_data = true;
}
QMap<QString,QString> contents = _PRS1ParsedEventContents(e);
if (name == "setting" && contents.size() == 1) {
out << " - set_" << contents.firstKey() << ": " << contents.first() << endl;
}
else {
out << " - " << name << ":" << endl;
// Always emit start first if present
if (contents.contains("start")) {
out << " " << "start" << ": " << contents["start"] << endl;
}
for (auto & key : contents.keys()) {
if (key == "start") continue;
out << " " << key << ": " << contents[key] << endl;
}
}
}
}
if (dump_data) {
out << " data: " << byteList(chunk->m_data, 100) << endl;
}
// data CRC // data CRC
out << " crc: " << hex << chunk->storedCrc << endl; out << " crc: " << hex << chunk->storedCrc << endl;
@ -255,8 +284,17 @@ void parseAndEmitChunkYaml(const QString & path)
// Parse the chunks in the file. // Parse the chunks in the file.
QList<PRS1DataChunk *> chunks = s_loader->ParseFile(inpath); QList<PRS1DataChunk *> chunks = s_loader->ParseFile(inpath);
for (int i=0; i < chunks.size(); i++) { for (int i=0; i < chunks.size(); i++) {
// Emit the YAML.
PRS1DataChunk * chunk = chunks.at(i); PRS1DataChunk * chunk = chunks.at(i);
// Parse the inner data.
switch (chunk->ext) {
case 0: chunk->ParseCompliance(); break;
case 1: chunk->ParseSummary(); break;
case 2: chunk->ParseEvents(MODE_UNKNOWN); break;
default: break;
}
// Emit the YAML.
ChunkToYaml(file, chunk); ChunkToYaml(file, chunk);
delete chunk; delete chunk;
} }
@ -268,7 +306,7 @@ void parseAndEmitChunkYaml(const QString & path)
void PRS1Tests::testChunksToYaml() void PRS1Tests::testChunksToYaml()
{ {
//iterateTestCards(TESTDATA_PATH "prs1/input/", parseAndEmitChunkYaml); iterateTestCards(TESTDATA_PATH "prs1/input/", parseAndEmitChunkYaml);
} }