/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * 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();
}
}
*/