From fb13500bee8248f61ac9f9930d26916982964838 Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Thu, 14 Jul 2011 03:42:49 +1000 Subject: [PATCH] Built-in CMS50 Oximeter Serial Importer --- README | 3 +- mainwindow.ui | 6 +- oximetry.cpp | 353 +++++++++++++++++++++++++++++++++++++++++++++++++- oximetry.h | 29 +++++ oximetry.ui | 14 +- 5 files changed, 397 insertions(+), 8 deletions(-) diff --git a/README b/README index a7b1d0a3..ac0152fa 100644 --- a/README +++ b/README @@ -14,5 +14,6 @@ Licence Stuff ------------- This software is released under the GNU Public License. +Exceptions: Incorporates TinyXML.. see their readme for details in ./libs/tinyxml - +Incorporates QextSerialPort. Insert New BSD license here. diff --git a/mainwindow.ui b/mainwindow.ui index 5e3071cd..d63dfe5e 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 771 - 420 + 822 + 468 @@ -555,7 +555,7 @@ 0 0 - 771 + 822 25 diff --git a/oximetry.cpp b/oximetry.cpp index c4e6a621..2ece7278 100644 --- a/oximetry.cpp +++ b/oximetry.cpp @@ -1,13 +1,63 @@ #include +#include + #include "oximetry.h" #include "ui_oximetry.h" #include "qextserialport/qextserialenumerator.h" +#include "SleepLib/loader_plugins/cms50_loader.h" +#include "Graphs/gXAxis.h" +#include "Graphs/gBarChart.h" +#include "Graphs/gLineChart.h" +#include "Graphs/gYAxis.h" +#include "Graphs/gFooBar.h" Oximetry::Oximetry(QWidget *parent) : QWidget(parent), ui(new Ui::Oximetry) { ui->setupUi(this); + port=NULL; + QString prof=pref["Profile"].toString(); + profile=Profiles::Get(prof); + if (!profile) { + qWarning("Couldn't get profile.. Have to abort!"); + abort(); + } + + gSplitter=new QSplitter(Qt::Vertical,ui->scrollArea); + gSplitter->setStyleSheet("QSplitter::handle { background-color: 'dark grey'; }"); + gSplitter->setHandleWidth(2); + ui->graphLayout->addWidget(gSplitter); + + AddData(pulse=new WaveData(OXI_Pulse)); + PULSE=new gGraphWindow(gSplitter,tr("Pulse Rate"),(QGLWidget *)NULL); + PULSE->AddLayer(new gXAxis()); + PULSE->AddLayer(new gYAxis()); + PULSE->AddLayer(new gFooBar()); + PULSE->AddLayer(new gLineChart(pulse,Qt::red,65536,false,false,false)); + PULSE->setMinimumHeight(150); + + AddData(spo2=new WaveData(OXI_SPO2)); + SPO2=new gGraphWindow(gSplitter,tr("SPO2"),(QGLWidget *)NULL); + SPO2->AddLayer(new gXAxis()); + SPO2->AddLayer(new gYAxis()); + SPO2->AddLayer(new gFooBar()); + SPO2->AddLayer(new gLineChart(spo2,Qt::red,65536,false,false,false)); + SPO2->setMinimumHeight(150); + + AddData(plethy=new WaveData(OXI_Plethy)); + PLETHY=new gGraphWindow(gSplitter,tr("Plethysomogram"),(QGLWidget *)NULL); + PLETHY->AddLayer(new gXAxis()); + PLETHY->AddLayer(new gYAxis()); + PLETHY->AddLayer(new gFooBar()); + PLETHY->AddLayer(new gLineChart(plethy,Qt::red,65536,false,false,false)); + PLETHY->setMinimumHeight(150); + + portname=""; + + gSplitter->addWidget(PULSE); + gSplitter->addWidget(SPO2); + gSplitter->addWidget(PLETHY); on_RefreshPortsButton_clicked(); } @@ -22,9 +72,16 @@ void Oximetry::on_RefreshPortsButton_clicked() QList ports = QextSerialEnumerator::getPorts(); ui->SerialPortsCombo->clear(); + int z=0; + QString firstport; + bool current_found=false; for (int i = 0; i < ports.size(); i++) { - if (ports.at(i).friendName.toUpper().contains("USB")) - ui->SerialPortsCombo->addItem(ports.at(i).portName); + if (ports.at(i).friendName.toUpper().contains("USB")) { + if (firstport.isEmpty()) firstport=ports.at(i).physName; + if (!portname.isEmpty() && ports.at(i).physName==portname) current_found=true; + ui->SerialPortsCombo->addItem(ports.at(i).physName); + z++; + } //qDebug() << "port name:" << ports.at(i).portName; qDebug() << "Serial Port:" << ports.at(i).physName << ports.at(i).friendName; //qDebug() << "enumerator name:" << ports.at(i).enumName; @@ -32,4 +89,296 @@ void Oximetry::on_RefreshPortsButton_clicked() //qDebug() << "product ID:" << QString::number(ports.at(i).productID, 16); //qDebug() << "==================================="; } + + if (z>0) { + ui->RunButton->setEnabled(true); + ui->ImportButton->setEnabled(true); + if (!current_found) portname=firstport; + } else { + ui->RunButton->setEnabled(false); + ui->ImportButton->setEnabled(false); + portname=""; + } +} +void Oximetry::RedrawGraphs() +{ + for (list::iterator g=Graphs.begin();g!=Graphs.end();g++) { + (*g)->updateGL(); + } +} + +void Oximetry::on_RunButton_toggled(bool checked) +{ + if (checked) { + ui->RunButton->setText("&Stop"); + ui->SerialPortsCombo->setEnabled(false); + // Disconnect?? + port=new QextSerialPort(portname,QextSerialPort::EventDriven); + port->setBaudRate(BAUD19200); + port->setFlowControl(FLOW_OFF); + port->setParity(PAR_ODD); + port->setDataBits(DATA_8); + port->setStopBits(STOP_1); + if (port->open(QIODevice::ReadWrite) == true) { + connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(port, SIGNAL(dsrChanged(bool)), this, SLOT(onDsrChanged(bool))); + if (!(port->lineStatus() & LS_DSR)) + qDebug() << "warning: device is not turned on"; + qDebug() << "listening for data on" << port->portName(); + } else { + qDebug() << "device failed to open:" << port->errorString(); + } + portmode=PM_LIVE; + } else { + ui->RunButton->setText("&Start"); + ui->SerialPortsCombo->setEnabled(true); + delete port; + port=NULL; + + } +} + +void Oximetry::on_SerialPortsCombo_activated(const QString &arg1) +{ + portname=arg1; +} + +void Oximetry::onReadyRead() +{ + static int lastpulse=-1, lastspo2=-1; + static int lastsize=-1; + QByteArray bytes; + int a = port->bytesAvailable(); + bytes.resize(a); + port->read(bytes.data(), bytes.size()); + /*if (portmode!=PM_RECORDING) { + return; + } + if (bytes.size()==3) { // control & waveform bytes + if ((bytes[0]==0xf0) && (bytes[1]==0x80) && (bytes[2]==0x00)) portmode==PM_LIVE; + //qDebug() << "A=" << int(bytes[0]) << "B=" << int(bytes[1]) << "C=" << int(bytes[2]); + // Pulse data.. + } else if (bytes.size()==2) { // Data bytes in live mode + // Plethy data + if (lastpulse!=bytes[0]) + qDebug() << "Pulse=" << int(bytes[0]); + if (lastspo2!=bytes[1]) + qDebug() << "SpO2=" << int(bytes[1]); + + lastpulse=bytes[0]; + lastspo2=bytes[1]; + } else { + qDebug() << "bytes read:" << bytes.size(); */ + QString aa; + for (int i=0;iwrite(b); + portmode=PM_LIVE; + qDebug() << "Killing Record Retrieval Mode"; + + } */ + lastsize=bytes.size(); +} +void Oximetry::onDsrChanged(bool status) +{ + if (status) + qDebug() << "device was turned on"; + else + qDebug() << "device was turned off"; +} +extern QProgressBar *qprogress; +extern QLabel *qstatus; + +// Move this code to CMS50 Importer?? +void Oximetry::on_ImportButton_clicked() +{ + Machine *mach=profile->GetMachine(MT_OXIMETER); + if (!mach) { + CMS50Loader *l=dynamic_cast(GetLoader("CMS50")); + if (l) { + mach=l->CreateMachine(profile); + } + + qDebug() << "Needed to create Oximeter device"; + } + unsigned char b1[2]; + unsigned char b2[3]; + unsigned char rb[0x20]; + b1[0]=0xf5; + b1[1]=0xf5; + b2[0]=0xf6; + b2[1]=0xf6; + b2[2]=0xf6; + unsigned char * buffer=NULL; + ui->SerialPortsCombo->setEnabled(false); + ui->RunButton->setText("&Start"); + ui->RunButton->setChecked(false); + + // port->write((char *)b1,2); + // return; + if (port) { + port->close(); + delete port; + } + // Disconnect?? + //qDebug() << "Initiating Polling Mode"; + port=new QextSerialPort(portname,QextSerialPort::Polling); + port->setBaudRate(BAUD19200); + port->setFlowControl(FLOW_OFF); + port->setParity(PAR_ODD); + port->setDataBits(DATA_8); + port->setStopBits(STOP_1); + port->setTimeout(500); + if (port->open(QIODevice::ReadWrite) == true) { + // if (!(port->lineStatus() & LS_DSR)) + // qDebug() << "warning: device is not turned on"; // CMS50 doesn't do this.. + + } else { + delete port; + port=NULL; + ui->SerialPortsCombo->setEnabled(true); + + return; + } + bool done=false; + int res; + int blocks=0; + unsigned int bytes=0; + QString aa; + port->flush(); + bool oneoff=false; + + //qprogress->reset(); + qstatus->setText("Importing"); + qprogress->setValue(0); + qprogress->show(); + int fails=0; + while (!done) { + + //port->setRts(true); + if (port->write((char *)b1,2)==-1) { + qDebug() << "Couldn't write 2 lousy bytes to CMS50"; + } + //usleep(500); + // port->setRts(false); + //qDebug() << "Available " << port->bytesAvailable(); + blocks=0; + int startpos=0; + unsigned int length=0; + do { + bool fnd=false; + res=port->read((char *)rb,0x20); + + if (blocks==0) { + for (int i=0;isetValue((100.0/length)*bytes); + memcpy((char *)&buffer[bytes],(char *)rb,res); + bytes+=res; + } + //aa=""; + //for (int i=0;i4) break; + } + if (done) { + if (oneoff) bytes--; // this is retarded.. + + QDateTime date=QDateTime::currentDateTimeUtc(); + SessionID sid=date.toTime_t(); + Session *sess=new Session(mach,sid); + qDebug() << "Read " << bytes << "Bytes"; + qDebug() << "Creating session " << sid; + char pulse,spo2,lastpulse=-1,lastspo2=-1; + + qint64 tt=sid-(bytes/3); + tt*=1000; + sess->set_first(tt); + EventDataType data; + unsigned i=0; + while (iAddEvent(new Event(tt,OXI_Pulse,&data,1)); + //qDebug() << "Pulse: " << int(pulse); + } + if (spo2 != 0 && spo2!=lastspo2) { + data=spo2; + sess->AddEvent(new Event(tt,OXI_SPO2,&data,1)); + //qDebug() << "SpO2: " << int(spo2); + } + + lastpulse=pulse; + lastspo2=spo2; + tt+=1000; + } + data=pulse; + sess->AddEvent(new Event(tt,OXI_Pulse,&data,1)); + data=spo2; + sess->AddEvent(new Event(tt,OXI_SPO2,&data,1)); + sess->summary[OXI_PulseMin]=sess->min_event_field(OXI_Pulse,0); + sess->summary[OXI_PulseMax]=sess->max_event_field(OXI_Pulse,0); + sess->summary[OXI_PulseAverage]=sess->weighted_avg_event_field(OXI_Pulse,0); + sess->summary[OXI_SPO2Min]=sess->min_event_field(OXI_SPO2,0); + sess->summary[OXI_SPO2Max]=sess->max_event_field(OXI_SPO2,0); + sess->summary[OXI_SPO2Average]=sess->weighted_avg_event_field(OXI_SPO2,0); + sess->SetChanged(true); + mach->AddSession(sess,profile); + mach->Save(); + // Output Pulse & SPO2 here.. + delete [] buffer; + port->write((char *)b2,3); + } + delete port; + port=NULL; + qprogress->hide(); + ui->SerialPortsCombo->setEnabled(true); + qstatus->setText("Ready"); } diff --git a/oximetry.h b/oximetry.h index 92f87b37..7c64c4bf 100644 --- a/oximetry.h +++ b/oximetry.h @@ -2,11 +2,19 @@ #define OXIMETRY_H #include +#include +#include + +#include "SleepLib/profiles.h" +#include "Graphs/graphwindow.h" +#include "Graphs/graphdata_custom.h" namespace Ui { class Oximetry; } +enum PORTMODE { PM_LIVE, PM_RECORDING }; + class Oximetry : public QWidget { Q_OBJECT @@ -15,11 +23,32 @@ public: explicit Oximetry(QWidget *parent = 0); ~Oximetry(); + void AddData(gPointData *d) { Data.push_back(d); }; + void AddGraph(gGraphWindow *w) { Graphs.push_back(w); }; + void RedrawGraphs(); + private slots: void on_RefreshPortsButton_clicked(); + void on_RunButton_toggled(bool checked); + + void on_SerialPortsCombo_activated(const QString &arg1); + void onReadyRead(); + void onDsrChanged(bool status); + + void on_ImportButton_clicked(); + private: Ui::Oximetry *ui; + Profile *profile; + QSplitter *gSplitter; + gPointData *pulse,*spo2,*plethy; + gGraphWindow *PULSE,*SPO2,*PLETHY; + list Graphs; + list Data; + QextSerialPort *port; + QString portname; + PORTMODE portmode; }; #endif // OXIMETRY_H diff --git a/oximetry.ui b/oximetry.ui index 7f3398d4..dbf18b86 100644 --- a/oximetry.ui +++ b/oximetry.ui @@ -47,7 +47,7 @@ 0 - + 0 @@ -94,6 +94,16 @@ + + + + &Start + + + true + + + @@ -110,7 +120,7 @@ - Import from Device + &Import from Device