CMS50F37 debug test

This commit is contained in:
Mark Watkins 2014-08-18 09:29:30 +10:00
parent cbc6739587
commit 9e77305a0d
11 changed files with 558 additions and 19 deletions

View File

@ -203,7 +203,9 @@ skipcheck:
addDotLine(DottedLine(code, Calc_Perc,chan.calc[Calc_Perc].enabled));
addDotLine(DottedLine(code, Calc_Middle, chan.calc[Calc_Middle].enabled));
}
addDotLine(DottedLine(code, Calc_Min, chan.calc[Calc_Min].enabled));
if ((code != CPAP_Snore) && (code != CPAP_FlowLimit) && (code != CPAP_RDI) && (code != CPAP_AHI)) {
addDotLine(DottedLine(code, Calc_Min, chan.calc[Calc_Min].enabled));
}
}
if (m_codes[0] == CPAP_Leak) {
addDotLine(DottedLine(CPAP_Leak, Calc_UpperThresh, schema::channel[CPAP_Leak].calc[Calc_UpperThresh].enabled));
@ -592,7 +594,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion &region)
dot.visible = true;
QColor color = chan.calc[dot.type].color;
color.setAlpha(200);
painter.setPen(QPen(QBrush(color),1.5,Qt::DotLine));
painter.setPen(QPen(QBrush(color), p_profile->appearance->lineThickness(), Qt::DotLine));
EventDataType y=top + height + 1 - ((dot.value - miny) * ymult);
painter.drawLine(left + 1, y, left + 1 + width, y);

View File

@ -466,6 +466,7 @@ void SummaryChart::paint(QPainter &painter, gGraph &w, const QRegion &region)
qint64 xx = maxx - minx;
EventDataType miny = m_physminy;
EventDataType maxy = m_physmaxy;

View File

@ -60,6 +60,10 @@ CMS50Loader::~CMS50Loader()
bool CMS50Loader::Detect(const QString &path)
{
if (p_profile->oxi->oximeterType() == QString("Contec CMS50D+/E/F")) {
return true;
}
Q_UNUSED(path);
return false;
}

View File

@ -16,7 +16,7 @@ const int cms50_data_version = 4;
/*! \class CMS50Loader
\brief Bulk Importer for CMS50 SPO2Review format.. Deprecated, as the Oximetry module does a better job
\brief Importer for CMS50 Oximeter
*/
class CMS50Loader : public SerialOximeter
{

View File

@ -0,0 +1,414 @@
/* SleepLib CMS50X Loader Implementation
*
* Copyright (c) 2011 Mark Watkins <jedimark@users.sourceforge.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details. */
//********************************************************************************************
/// IMPORTANT!!!
//********************************************************************************************
// Please INCREMENT the cms50_data_version in cms50_loader.h when making changes to this loader
// that change loader behaviour or modify channels.
//********************************************************************************************
#include <QProgressBar>
#include <QApplication>
#include <QDir>
#include <QString>
#include <QDataStream>
#include <QDateTime>
#include <QFile>
#include <QDebug>
#include <QList>
#include <QMessageBox>
#include <QLabel>
#include <QVBoxLayout>
#include <QPushButton>
using namespace std;
#include "cms50f37_loader.h"
#include "SleepLib/machine.h"
#include "SleepLib/session.h"
extern QProgressBar *qprogress;
static unsigned char cms50_sequence[] = { 0xa7, 0xa2, 0xa0, 0xb0, 0xac, 0xb3, 0xad, 0xa3, 0xab, 0xa4, 0xa5, 0xaf, 0xa7, 0xa2, 0xa6 };
int cms50_seqlength = sizeof(cms50_sequence);
CMS50F37Loader::CMS50F37Loader()
{
m_type = MT_OXIMETER;
m_abort = false;
m_streaming = false;
m_importing = false;
imp_callbacks = 0;
m_vendorID = 0x10c4;
m_productID = 0xea60;
cms50dplus = false;
oxirec = nullptr;
startTimer.setParent(this);
resetTimer.setParent(this);
}
CMS50F37Loader::~CMS50F37Loader()
{
}
bool CMS50F37Loader::Detect(const QString &path)
{
if (p_profile->oxi->oximeterType() == QString("Contec CMS50F v3.7+")) {
return true;
}
Q_UNUSED(path);
return false;
}
int CMS50F37Loader::Open(QString path)
{
// Only one active Oximeter module at a time, set in preferences
m_itemCnt = 0;
m_itemTotal = 0;
m_abort = false;
m_importing = false;
started_import = false;
started_reading = false;
finished_import = false;
setStatus(NEUTRAL);
imp_callbacks = 0;
cb_reset = 0;
m_time.start();
if (oxirec) {
trashRecords();
}
// Cheating using path for two serial oximetry modes
if (path.compare("import") == 0) {
serial.clear();
sequence = 0;
nextCommand();
setStatus(DETECTING);
return 1;
} else if (path.compare("live") == 0) {
return 0;
}
QString ext = path.section(".",1);
if ((ext.compare("spo2", Qt::CaseInsensitive)==0) || (ext.compare("spo", Qt::CaseInsensitive)==0) || (ext.compare("spor", Qt::CaseInsensitive)==0)) {
// try to read and process SpoR file..
return readSpoRFile(path) ? 1 : 0;
}
return 0;
}
void CMS50F37Loader::processBytes(QByteArray bytes)
{
QStringList data;
int len = bytes.size();
for (int i=0; i < len; ++i) {
data.append(QString::number((unsigned char)bytes.at(i),16));
}
if (++sequence < cms50_seqlength) {
qDebug() << "Read:" << data.join(",");
nextCommand();
} else {
qDebug() << "Read:" << data.join(",");
}
m_status = NEUTRAL;
}
int CMS50F37Loader::doImportMode()
{
int available = buffer.size();
// Q_ASSERT(!finished_import);
int hour,minute;
int idx = 0;
while (idx < available) {
unsigned char c=(unsigned char)buffer.at(idx);
}
return idx;
}
int CMS50F37Loader::doLiveMode()
{
Q_ASSERT(oxirec != nullptr);
int available = buffer.size();
int idx = 0;
QByteArray plethy;
while (idx < available-5) {
if (((unsigned char)buffer.at(idx) & 0x80) != 0x80) {
idx++;
continue;
}
int pwave=(unsigned char)buffer.at(idx + 1);
int pbeat=(unsigned char)buffer.at(idx + 2);
int pulse=((unsigned char)buffer.at(idx + 3) & 0x7f) | ((pbeat & 0x40) << 1);
int spo2=(unsigned char)buffer.at(idx + 4) & 0x7f;
oxirec->append(OxiRecord(pulse, spo2));
plethy.append(pwave);
idx += 5;
}
emit updatePlethy(plethy);
return idx;
}
void CMS50F37Loader::sendCommand(unsigned char c)
{
static unsigned char cmd[] = { 0x7d, 0x81, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
cmd[2] = c;
QString out;
for (int i=0;i < 8;i++) out += QString().sprintf("%02X ",cmd[i]);
qDebug() << "Write:" << out;
if (serial.write((char *)cmd,8) == -1) {
qDebug() << "Couldn't write data reset bytes to CMS50";
}
}
void CMS50F37Loader::nextCommand()
{
if (sequence < cms50_seqlength)
sendCommand(cms50_sequence[sequence]);
}
void CMS50F37Loader::resetDevice() // Switch CMS50D+ device to live streaming mode
{
}
void CMS50F37Loader::requestData() // Switch CMS50D+ device to record transmission mode
{
}
void CMS50F37Loader::killTimers()
{
}
void CMS50F37Loader::startImportTimeout()
{
}
void CMS50F37Loader::resetImportTimeout()
{
if (finished_import) {
return;
}
if (imp_callbacks != cb_reset) {
// Still receiving data.. reset timer
qDebug() << "Still receiving data in resetImportTimeout()" << imp_callbacks << cb_reset;
if (resetTimer.isActive())
resetTimer.stop();
if (!finished_import) resetTimer.singleShot(2000, this, SLOT(resetImportTimeout()));
} else {
qDebug() << "Oximeter device stopped transmitting.. Transfer complete";
// We were importing, but now are done
if (!finished_import && (started_import && started_reading)) {
qDebug() << "Switching CMS50 back to live mode and finalizing import";
// Turn back on live streaming so the end of capture can be dealt with
resetTimer.stop();
resetDevice(); // Send Reset to CMS50D+
serial.flush();
QThread::msleep(200);
resetDevice(); // Send Reset to CMS50D+
serial.flush();
serial.clear();
//started_import = false;
// finished_import = true;
//m_streaming=false;
//closeDevice();
//emit transferComplete();
//doImportComplete();
return;
}
qDebug() << "Should CMS50 resetImportTimeout reach here?";
// else what???
}
cb_reset = imp_callbacks;
}
void CMS50F37Loader::shutdownPorts()
{
closeDevice();
}
bool CMS50F37Loader::readSpoRFile(QString path)
{
QFile file(path);
if (!file.exists()) {
return false;
}
if (!file.open(QFile::ReadOnly)) {
return false;
}
bool spo2header = false;
QString ext = path.section('.', -1);
if (ext.compare("spo2",Qt::CaseInsensitive) == 0) {
spo2header = true;
}
QByteArray data;
qint64 filesize = file.size();
data = file.readAll();
QDataStream in(data);
in.setByteOrder(QDataStream::LittleEndian);
quint16 pos;
in >> pos;
in.skipRawData(pos - 2);
//long size = data.size();
int bytes_per_record = 2;
if (!spo2header) {
// next is 0x0002
// followed by 16bit duration in seconds
// Read date and time (it's a 16bit charset)
char dchr[20];
int j = 0;
for (int i = 0; i < 18 * 2; i += 2) {
dchr[j++] = data.at(8 + i);
}
dchr[j] = 0;
if (dchr[0]) {
QString dstr(dchr);
m_startTime = QDateTime::fromString(dstr, "MM/dd/yy HH:mm:ss");
if (m_startTime.date().year() < 2000) { m_startTime = m_startTime.addYears(100); }
} else {
m_startTime = QDateTime(QDate::currentDate(), QTime(0,0,0));
}
} else { // !spo2header
quint32 samples = 0; // number of samples
quint32 year, month, day;
quint32 hour, minute, second;
if (data.at(pos) != 1) {
qWarning() << ".spo2 file" << path << "might be a different";
}
// Unknown cruft header...
in.skipRawData(200);
in >> year >> month >> day;
in >> hour >> minute >> second;
m_startTime = QDateTime(QDate(year, month, day), QTime(hour, minute, second));
// ignoring it for now
pos += 0x1c + 200;
in >> samples;
int remainder = filesize - pos;
bytes_per_record = remainder / samples;
qDebug() << samples << "samples of" << bytes_per_record << "bytes each";
// CMS50I .spo2 data have 4 digits, a 16bit, followed by spo2 then pulse
}
oxirec = new QVector<OxiRecord>;
oxisessions[m_startTime] = oxirec;
unsigned char o2, pr;
quint16 un;
// Read all Pulse and SPO2 data
do {
if (bytes_per_record > 2) {
in >> un;
}
in >> o2;
in >> pr;
if ((o2 == 0x7f) && (pr == 0xff)) {
o2 = pr = 0;
un = 0;
}
if (spo2header) {
oxirec->append(OxiRecord(pr, o2));
} else {
oxirec->append(OxiRecord(o2, pr));
}
} while (!in.atEnd());
// for (int i = pos; i < size - 2;) {
// o2 = (unsigned char)(data.at(i + 1));
// pr = (unsigned char)(data.at(i + 0));
// oxirec->append(OxiRecord(pr, o2));
// i += 2;
// }
// processing gets done later
return true;
}
void CMS50F37Loader::process()
{
// Just clean up any extra crap before oximeterimport parses the oxirecords..
return;
// if (!oxirec)
// return;
// int size=oxirec->size();
// if (size<10)
// return;
}
static bool cms50f37_initialized = false;
void CMS50F37Loader::Register()
{
if (cms50f37_initialized) { return; }
qDebug() << "Registering CMS50F37Loader";
RegisterLoader(new CMS50F37Loader());
cms50f37_initialized = true;
}

View File

@ -0,0 +1,105 @@
/* SleepLib CMS50X Loader Header
*
* Copyright (c) 2011-2014 Mark Watkins <jedimark@users.sourceforge.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details. */
#ifndef CMS50F37LOADER_H
#define CMS50F37LOADER_H
#include "SleepLib/serialoximeter.h"
const QString cms50f37_class_name = "CMS50F37";
const int cms50f37_data_version = 0;
/*! \class CMS5037Loader
\brief Bulk Importer for newer CMS50 oximeters
*/
class CMS50F37Loader : public SerialOximeter
{
Q_OBJECT
public:
CMS50F37Loader();
virtual ~CMS50F37Loader();
virtual bool Detect(const QString &path);
virtual int Open(QString path);
static void Register();
virtual int Version() { return cms50f37_data_version; }
virtual const QString &loaderName() { return cms50f37_class_name; }
virtual MachineInfo newInfo() {
return MachineInfo(MT_OXIMETER, 0, cms50f37_class_name, QObject::tr("Contec"), QObject::tr("CMS50F3.7"), QString(), QString(), QObject::tr("CMS50F"), QDateTime::currentDateTime(), cms50f37_data_version);
}
// Machine *CreateMachine();
virtual void process();
virtual bool isStartTimeValid() { return !cms50dplus; }
protected slots:
// virtual void dataAvailable();
virtual void resetImportTimeout();
virtual void startImportTimeout();
virtual void shutdownPorts();
void nextCommand();
protected:
bool readSpoRFile(QString path);
virtual void processBytes(QByteArray bytes);
int doImportMode();
int doLiveMode();
virtual void killTimers();
void sendCommand(unsigned char c);
QList<unsigned char> cmdQue;
// Switch device to live streaming mode
virtual void resetDevice();
// Switch device to record transmission mode
void requestData();
private:
int sequence;
EventList *PULSE;
EventList *SPO2;
QTime m_time;
QByteArray buffer;
bool started_import;
bool finished_import;
bool started_reading;
bool cms50dplus;
int cb_reset,imp_callbacks;
int received_bytes;
int m_itemCnt;
int m_itemTotal;
};
#endif // CMS50F37LOADER_H

View File

@ -19,7 +19,7 @@
#include "zlib.h"
class MachineLoader;
enum DeviceStatus { NEUTRAL, IMPORTING, LIVE };
enum DeviceStatus { NEUTRAL, IMPORTING, LIVE, DETECTING };

View File

@ -684,7 +684,7 @@ int MainWindow::importCPAP(ImportPath import, const QString &message)
popup->hide();
vlayout->removeWidget(qprogress);
ui->statusbar->insertWidget(2,qprogress,1);
ui->statusbar->insertWidget(1,qprogress,1);
qprogress->setVisible(false);
delete popup;

View File

@ -1498,7 +1498,7 @@ QToolBox::tab:selected {
<x>0</x>
<y>0</y>
<width>180</width>
<height>710</height>
<height>724</height>
</rect>
</property>
<property name="palette">
@ -1912,7 +1912,7 @@ border: 2px solid #56789a; border-radius: 30px;
<x>0</x>
<y>0</y>
<width>180</width>
<height>710</height>
<height>724</height>
</rect>
</property>
<property name="palette">
@ -3060,7 +3060,7 @@ border-radius: 10px;
<x>0</x>
<y>0</y>
<width>180</width>
<height>710</height>
<height>724</height>
</rect>
</property>
<property name="mouseTracking">
@ -3229,7 +3229,23 @@ border-radius: 10px;
<addaction name="menu_Data"/>
<addaction name="menu_Help"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QStatusBar" name="statusbar">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>7</height>
</size>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
</widget>
<action name="action_Import_Data">
<property name="text">
<string>&amp;Import Data</string>

View File

@ -51,7 +51,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>5</number>
<number>4</number>
</property>
<widget class="QWidget" name="importTab">
<attribute name="title">
@ -1384,17 +1384,12 @@ Try to sync it to your PC's clock (which should be synced to a timeserver)</stri
</property>
<item>
<property name="text">
<string>Contec CMS50D+</string>
<string>Contec CMS50D+/E/F</string>
</property>
</item>
<item>
<property name="text">
<string>Contec CMS50E/F</string>
</property>
</item>
<item>
<property name="text">
<string>Contec CMS50F v3.7</string>
<string>Contec CMS50F v3.7+</string>
</property>
</item>
</widget>

View File

@ -182,7 +182,8 @@ SOURCES += \
Graphs/gdailysummary.cpp \
Graphs/MinutesAtPressure.cpp \
SleepLib/journal.cpp \
SleepLib/progressdialog.cpp
SleepLib/progressdialog.cpp \
SleepLib/loader_plugins/cms50f37_loader.cpp
HEADERS += \
common_gui.h \
@ -242,7 +243,8 @@ HEADERS += \
Graphs/gdailysummary.h \
Graphs/MinutesAtPressure.h \
SleepLib/journal.h \
SleepLib/progressdialog.h
SleepLib/progressdialog.h \
SleepLib/loader_plugins/cms50f37_loader.h
FORMS += \
daily.ui \