example / ui_capno / capnografo.cpp
capnografo.cpp
Raw
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * paquete 83 * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Start Character: 1 The first byte of the packet. Fixed as 0xFA.
 * Packet Length: 1 The packet length is the number of bytes from the start character to the checksum.
 * Parameter Type: 1 The parameter type is the packet identification of different parameter module.
 * Packet ID: 1 It is used for the identification of packet ID.
 * Serial Number: 4 In the process of command answer, the serial number is used to distinguish that command answer packet (DA) responds which control
 * Data content: Undefined The specific format and length depend on the packet ID.
 * Checksum: 1 Considering the time consuming of CRC calculation, the checksum is calculated by the method of single-byte accumulative total.
 * The calculation includes the packet length, the parameter type, packet type, packet ID, serial number, and data contents, except the start character (0xFA).
 * La parte de Data content está formada por 17 bytes, que contienen información de la siguiente manera:
 * * Char1: Low 8 bits of real-time CO2 concentration in airway
 * * Char2: High 8 bits of real-time CO2 concentration in airway
 * * Char3: Low 8 bits of real-time N2O concentrationin airway
 * * Char 4: High 8 bits of real-time N2O concentration in airway
 * * Char5: Low 8 bits of real-time primary anesthetic gas concentration in airway
 * * Char6: High 8 bits of real-time primary anesthetic gas concentration in airway
 * * Char7: Low 8 bits of real-time auxiliary anesthetic gas concentration in airway
 * * Char8: High 8 bits of real-time auxiliary anesthetic gas concentration in airway
 * * Char9:Low 8 bits of real-timeO2concentrationin airway
 * * Char10:High 8 bits of real-time O2 concentration in airway
 * * Char11~Char16:0x00
 * * Char17:The ID of anesthetic gas
 * ** The anesthetic gas ID definition:
 * *** Bit0~Bit3: The ID of primary anesthetic gas
 * **** 0000 = Halothane Hal
 * **** 0001 = Enflurane
 * **** 0010 = Isoflurane
 * **** 0100 = Sevoflurane
 * **** 0101 = Desflurane
 * **** 1111 = The ID of auxiliary anesthetic gas is invalid
 * *** Bit4~Bit7: The ID of auxiliary anesthetic gas
 * **** 0000 = Halothane Hal
 * **** 0001 = Enflurane
 * **** 0010 = Isoflurane
 * **** 0100 = Sevoflurane
 * **** 0101 = Desflurane
 * **** 1111 = The ID of auxiliary anesthetic gas is invalid
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * paquete 84 * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 24 bytes
 * Byte1: EtCO2 Calculation result
 * Byte2: InsCO2 Calculation result
 * Byte3: AwRR Calculation results
 * Byte4: EtN2O calculation result
 * Byte5: InsN2O calculation result
 * Byte6: Et primary anesthetic gas calculation result
 * Byte7: Ins primary anesthetic gas calculation result
 * Byte8: Et auxiliary anesthetic gas calculation result
 * Byte9: Ins auxiliary anesthetic gas calculation result
 * Byte10: EtO2 calculation result
 * Char11: InsO2 calculation result
 * Char12: System operation error code 1
 * Char13: The status of system operation
 * Char14: The bit of zero calibration status
 * Char15: Sampling method
 * Char16: O2 compensation concentration
 * Char17: N2O compensation concentration
 * Char18: Anesthetic gas compensation concentration
 * Char19: System operation mode
 * Char20: The time of timeout apnea
 * Char21: Average time
 * Char22: BTPS compensation
 * Char23: System operation error code 2
 * Char24: System operation error code 3
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/

#include "capnografo.h"
#include <QDebug>
#include <QThread>
#include <math.h>
#include <QRandomGenerator>
#include <cstdlib>
#include <ctime>

static double yCO2;
QVariantList interpolatedPoints;

CapnographSerialThread::CapnographSerialThread(QObject *parent)
    : QObject(parent)
{
    comCapnographController = new QSerialPort(this);
    connect(comCapnographController, &QSerialPort::readyRead, this, &CapnographSerialThread::handleReadyRead);
    inicializarSensor();
}

CapnographSerialThread::~CapnographSerialThread()
{
    closeCapnographSerialPort();
    //delete timer;
}

void CapnographSerialThread::openCapnographSerialPort()
{
    comCapnographController->setPortName("ttyUSB0");

    if (comCapnographController->open(QIODevice::ReadWrite)) {
        comCapnographController->setBaudRate(QSerialPort::Baud19200);
        comCapnographController->setDataBits(QSerialPort::Data8);
        comCapnographController->setParity(QSerialPort::NoParity);
        comCapnographController->setStopBits(QSerialPort::OneStop);
        comCapnographController->setFlowControl(QSerialPort::NoFlowControl);
        qDebug() << "Port opened successfully";
    } else {
        qDebug() << "Failed to open port:" << comCapnographController->errorString();
    }
}

void CapnographSerialThread::closeCapnographSerialPort()
{
    if (comCapnographController->isOpen()) {
        comCapnographController->close();
        qDebug() << "Port closed";
    }
}

void CapnographSerialThread::handleReadyRead()
{
    const QByteArray data = comCapnographController->readAll();
    receivedData.append(data); // Añadir los datos recibidos al buffer

    while (receivedData.size() >= 2) {
        // Verificar el header
        if (static_cast<quint8>(receivedData.at(0)) == 0xFA) {
            if (static_cast<quint8>(receivedData.at(4)) == 0x91){
                if (static_cast<quint8>(receivedData.at(0)) == 0xFA) {
                    //qDebug() << "Data received:" << data;
                    inicializarPaquete84(data);
                }
            }

            int packetLength = static_cast<quint8>(receivedData.at(1));
            if (receivedData.size() >= packetLength) {
                QByteArray packet = receivedData.left(packetLength);
                receivedData.remove(0, packetLength);
                processPacket(packet);
                // Procesar los puntos de datos
                m_xValue = std::fmod(m_xValue, 100) + 1;
                m_yValue = yCO2;
                QVariantMap point;
                point["xValue"] = m_xValue;
                point["yValue"] = m_yValue;
                m_points.append(point);
                interpolatePoints(m_points,3);
                //qDebug() << "-------------------";
                //qDebug() << interpolatedPoints;
                //qDebug() << "++++++++++++++++++++";
                emit xValueChanged(m_xValue);
                emit yValueChanged(m_yValue);
            } else {
                break; // Esperar más datos
            }
        } else {
            // Descartar datos hasta encontrar un header válido
            receivedData.remove(0, 1);
        }
    }
}

/* Interpolated points
 * void CapnographSerialThread::handleReadyRead()
{
    const QByteArray data = comCapnographController->readAll();
    //qDebug() << "Data received:" << data.toHex();
    receivedData.append(data); // Añadir los datos recibidos al buffer

    while (receivedData.size() >= 2) {
        if (static_cast<quint8>(receivedData.at(0)) == 0xFA) {
            int packetLength = static_cast<quint8>(receivedData.at(1));
            if (receivedData.size() >= packetLength) {
                QByteArray packet = receivedData.left(packetLength);
                receivedData.remove(0, packetLength);
                processPacket(packet);
                // Procesar los puntos de datos
                m_xValue = std::fmod(m_xValue, 100) + 1;
                m_yValue = yCO2;
                QMap<QString, QVariant> point;
                point["xValue"] = m_xValue;
                point["yValue"] = m_yValue;
                m_points.append(point);

                // Interpolación y emisión de valores interpolados
                for (int i = 0; i < m_points.size() - 1; ++i) {
                    QMap<QString, QVariant> point1 = m_points[i];
                    QMap<QString, QVariant> point2 = m_points[i + 1];

                    double x1 = point1["xValue"].toDouble();
                    double y1 = point1["yValue"].toDouble();
                    double x2 = point2["xValue"].toDouble();
                    double y2 = point2["yValue"].toDouble();

                    for (int j = 0; j < 10; ++j) { // numInterpolatedPoints = 3
                        double t = static_cast<double>(j) / 3;
                        double xInterp = x1 + t * (x2 - x1);
                        double yInterp = y1 + t * (y2 - y1);

                        QMap<QString, QVariant> interpPoint;
                        interpPoint["xValue"] = xInterp;
                        interpPoint["yValue"] = yInterp;

                        // Emitir los valores interpolados
                        //emit xValueChanged(xInterp);
                        //emit yValueChanged(yInterp);

                        // Debug output
                        qDebug() << "Interpolated xValue:" << xInterp;
                        qDebug() << "Interpolated yValue:" << yInterp;
                    }
                }

                // Emitir el último punto original
                emit xValueChanged(m_xValue);
                emit yValueChanged(m_yValue);

            } else {
                break;
            }
        } else {
            receivedData.remove(0, 1);
        }
    }
}*/

void CapnographSerialThread::sendPacket(QByteArray packet)
{
    comCapnographController->write(packet);
    comCapnographController->waitForBytesWritten();
}

QByteArray CapnographSerialThread::powerOnHandshake(quint8 command, quint8 data1, quint8 data2, quint8 data3, quint8 data4, quint8 data5)
{
    QByteArray packet;
    packet.append(0xFA);
    packet.append(0x0A);
    packet.append(0x04);
    packet.append(0x01);
    packet.append(command);
    packet.append(data1);
    packet.append(data2);
    packet.append(data3);
    packet.append(data4);
    packet.append(data5);
    // Calcular el checksum
    quint8 checksum = 0x0A + 0x04 + 0x01 + command + data1 + data2 + data3 + data4 + data5;
    packet.append(checksum);
    return packet;
}

void CapnographSerialThread::processPacket(QByteArray packet)
{
    if (packet.size() < 6) {
        return;
    }

    //quint8 startCharacter = static_cast<quint8>(packet.at(0));
    quint8 packetLength = static_cast<quint8>(packet.at(1));
    //quint8 parameterType = static_cast<quint8>(packet.at(2));
    quint8 packetID = static_cast<quint8>(packet.at(4));
    //QByteArray serialNumber = packet.mid(5, 4);
    QByteArray dataContent = packet.mid(9, packetLength - 9);
    quint8 checksum = static_cast<quint8>(packet.at(packetLength - 1));

    // Verificar el checksum
    quint8 calculatedChecksum = 0;
    for (int i = 1; i < packetLength - 1; ++i) {
        calculatedChecksum += static_cast<quint8>(packet.at(i));
    }
    if (calculatedChecksum != checksum) {
        //qDebug() << "Checksum fuera de lo aceptado";
        return;
    }

    //qDebug() << "startCharacter:" << startCharacter;
    //qDebug() << "packetLength:" << packetLength;
    //qDebug() << "parameterType:" << parameterType;
    //qDebug() << "Serial Number:" << serialNumber;
    //qDebug() << "Packet ID:" << packetID;
    //qDebug() << "dataContent:" << dataContent;

    if (packetID == 131) { //131
        if (dataContent.size() >= 17) {
            quint16 realTimeCO2 = static_cast<quint8>(dataContent.at(0)) | (static_cast<quint8>(dataContent.at(1)) << 8);
            quint16 realTimeO2 = static_cast<quint8>(dataContent.at(8)) | (static_cast<quint8>(dataContent.at(9)) << 8);

            // Procesar y mostrar los datos aquí
            //qDebug() << "---------------------------------";
            yCO2 = std::round(realTimeCO2*0.1)/10;
            qDebug() << "Real-time CO2:" << yCO2;
            //qDebug() << "Real-time O2:" << realTimeO2;
            //qDebug() << "----------------------------------";
            emit dataPacket83(realTimeCO2, realTimeO2);

            //llamada a la función de interpolación lineal
            interpolacionLineal((realTimeCO2*0.1)/100);
            emit displayedData(QString::number(yCO2),QString::number(realTimeO2));

        } else {
            qDebug() << "Invalid data content size for packet ID 83";
        }
    } else if (packetID == 132) {
        if (dataContent.size() >= 24) {
            quint8 EtCO2 = static_cast<quint8>(dataContent.at(0));
            quint8 InsCO2 = static_cast<quint8>(dataContent.at(1));
            quint8 AwRR = static_cast<quint8>(dataContent.at(2));
            quint8 EtO2 = static_cast<quint8>(dataContent.at(9));
            quint8 InsO2 = static_cast<quint8>(dataContent.at(10));

            // Procesar y mostrar los datos aquí
            //qDebug() << "---------------------------------";
            //qDebug() << "EtCO2:" << EtCO2;
            //qDebug() << "InsCO2:" << InsCO2;
            //qDebug() << "AwRR:" << AwRR;
            //qDebug() << "EtO2:" << EtO2;
            //qDebug() << "InsO2:" << InsO2;
            //qDebug() << "---------------------------------";
            emit dataPacket84(EtCO2, InsCO2, AwRR, EtO2, InsO2);
        } else {
            qDebug() << "Invalid data content size for packet ID 84";
        }
    } else {
        //qDebug() << "No es un packetID válido";
    }
}

void CapnographSerialThread::inicializarSensor()
{
    if (!comCapnographController->isOpen()) {
        openCapnographSerialPort();
    }
    if (comCapnographController->isOpen()) {
        // Paso 1: Enviar solicitud de handshake (DD)
        QByteArray handshakeSolicitud = powerOnHandshake(0x1, 0x0, 0x00, 0x00, 0x00, 0x10);
        sendPacket(handshakeSolicitud);
        qDebug() << "Handshake request sent:" << handshakeSolicitud;
    } else {
        qDebug() << "No se pudo abrir el puerto para la inicialización.";
    }
}

void CapnographSerialThread::inicializarPaquete84(const QByteArray response)
{
    if (!comCapnographController->isOpen()) {
        openCapnographSerialPort();
    }
    if (comCapnographController->isOpen()) {
        // Paso 2: Enviar solicitud para recibir el paquete ID=0x84
        // FA 14 04 04 91 response.at(5)+19 38 02 data844+1 0B checksum
        QByteArray requestPacket84;
        requestPacket84.append(0xFA);
        requestPacket84.append(0x14);
        requestPacket84.append(0x04);
        requestPacket84.append(0x04);
        requestPacket84.append(0x91);
        //quint8 data84 = response.at(5)+19;
        quint8 data84 = 0x00;
        requestPacket84.append(data84);
        requestPacket84.append(0x39);
        requestPacket84.append(0x02);
        quint8 data844 = 0x00; //0X01
        requestPacket84.append(data844);
        data844 += 1;
        requestPacket84.append(0x0B);
        quint8 checksum = 0x14 + 0x04 + 0x04 + 0x91 + data84 + 0x39 + 0x02 + data844 + 0x0B;
        requestPacket84.append(checksum); // Checksum
        sendPacket(requestPacket84);
        //qDebug() << "Request packet for ID=0x84 sent:" << requestPacket84.toHex();
    } else {
        qDebug() << "No hubo respuesta al handshake";
    }
}

void CapnographSerialThread::interpolacionLineal(quint16 realTimeCO2)
{
   // puntos de referencia (x0, y0) y (x1, y1)
   double x0 = 0.0;
   double y0 = 0.0;
   double x1 = 100.0;
   double y1 = 1000.0;

   //if (m_xValue >= 720){
   //    m_xValue = 0;
   //}

   // La variable yCO2 es el punto "x" a interpolar el valor "y"
   double x = yCO2 * 0.1; // Ajustar a la escala

   //fórmula de interpolación lineal
   m_iXval = std::fmod(m_xValue, 100) + 1;
   m_iYval = y0 + (x - x0) * (y1 - y0) / (x1 - x0);

   QVariantMap intPoint;
   intPoint["xValue"] = m_iXval;
   intPoint["yValue"] = m_iYval;

   //Mostrar el resultado de la interpolación
   //qDebug() << m_iXval << "------- " << m_iYval;
}



void CapnographSerialThread::startGenerating()
{
   m_timer.start(1000); // Generate values every second
}

void CapnographSerialThread::generateValues()
{

}

QVariantList CapnographSerialThread::getCurrentPoints() const
{
   QVariantList points;
   for (const QVariantMap &point : m_points) {
       points.append(point);
   }
   return points;
}

// no se ocupó la interpolación
QVariantList CapnographSerialThread::interpolatePoints(const QList<QMap<QString, QVariant>>& points, int numInterpolatedPoints) {

    for (int i = 0; i < points.size() - 1; ++i) {
        QMap<QString, QVariant> point1 = points[i];
        QMap<QString, QVariant> point2 = points[i + 1];

        //double x1 = point1["xValue"].toDouble();
        double y1 = point1["yValue"].toDouble();
        //double x2 = point2["xValue"].toDouble();
        double y2 = point2["yValue"].toDouble();

        for (int j = 0; j < numInterpolatedPoints; ++j) {
            QMap<QString, QVariant> point1 = points[i];
            QMap<QString, QVariant> point2 = points[i + 1];

            //double x1 = point1["xValue"].toDouble();
            double y1 = point1["yValue"].toDouble();
            //double x2 = point2["xValue"].toDouble();
            double y2 = point2["yValue"].toDouble();
            double t = static_cast<double>(j) / numInterpolatedPoints;
            //double xInterp = x1 + t * (x2 - x1);
            double yInterp = y1 + t * (y2 - y1);

            QMap<QString, QVariant> interpPoint;
            interpPoint["xValue"] = m_iXval;
            interpPoint["yValue"] = yInterp;
            interpolatedPoints.append(QVariant::fromValue(interpPoint));
        }
    }
    return interpolatedPoints;
}
/*
Canvas {
    id: canvas
    anchors.fill: parent
    onPaint: {
        var ctx = getContext("2d");
        ctx.clearRect(0, 0, width, height);

        ctx.strokeStyle = "blue";
        ctx.lineWidth = 2;
        ctx.beginPath();

        var scaleX = width / maxPoints;
        var scaleY = height / 100;

        if (dataPoints.length > 0) {
            for (var i = 0; i < dataPoints.length; i++) {
                var x = 62 + (i % maxPoints) * scaleX;
                var y = height - 5 - dataPoints[i] * scaleY;
                if (i === 0 || (i === currentIndex && replacing)) {
                    ctx.moveTo(x, y);
                } else if ((i === currentIndex - 1 && replacing) || (i === (currentIndex - 2 + maxPoints) % maxPoints && replacing)) {
                    // Creates a larger blank space by breaking the line further back
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
        }

        ctx.stroke();
    }
}
*/