// SPDX-License-Identifier: GPL-2.0 #include "configuredivecomputer.h" #include #include #include #include #include #include #include #include #include "core/file.h" #include "core/errorhelper.h" #include "core/version.h" ConfigureDiveComputer::ConfigureDiveComputer() : readThread(0), writeThread(0), resetThread(0), firmwareThread(0) { setState(INITIAL); } void ConfigureDiveComputer::connectThreadSignals(DeviceThread *thread) { connect(thread, &DeviceThread::finished, this, &ConfigureDiveComputer::readThreadFinished, Qt::QueuedConnection); connect(thread, &DeviceThread::error, this, &ConfigureDiveComputer::setError); connect(thread, &DeviceThread::progress, this, &ConfigureDiveComputer::progressEvent, Qt::QueuedConnection); } void ConfigureDiveComputer::readSettings(device_data_t *data) { setState(READING); if (readThread) readThread->deleteLater(); readThread = new ReadSettingsThread(this, data); connectThreadSignals(readThread); connect(readThread, &ReadSettingsThread::devicedetails, this, &ConfigureDiveComputer::deviceDetailsChanged); readThread->start(); } void ConfigureDiveComputer::saveDeviceDetails(const DeviceDetails &details, device_data_t *data) { setState(WRITING); if (writeThread) writeThread->deleteLater(); writeThread = new WriteSettingsThread(this, data); connectThreadSignals(writeThread); writeThread->setDeviceDetails(details); writeThread->start(); } static QString writeGasDetails(gas g) { return QStringList({ QString::number(g.oxygen), QString::number(g.helium), QString::number(g.type), QString::number(g.depth) }).join(QLatin1Char(',')); } bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data) { QString xml = ""; QString vendor = data->vendor; QString product = data->product; QXmlStreamWriter writer(&xml); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("DiveComputerSettingsBackup"); writer.writeStartElement("DiveComputer"); writer.writeTextElement("Vendor", vendor); writer.writeTextElement("Product", product); writer.writeEndElement(); writer.writeStartElement("Settings"); writer.writeTextElement("CustomText", details.customText); //Add gasses writer.writeTextElement("Gas1", writeGasDetails(details.gas1)); writer.writeTextElement("Gas2", writeGasDetails(details.gas2)); writer.writeTextElement("Gas3", writeGasDetails(details.gas3)); writer.writeTextElement("Gas4", writeGasDetails(details.gas4)); writer.writeTextElement("Gas5", writeGasDetails(details.gas5)); // //Add dil values writer.writeTextElement("Dil1", writeGasDetails(details.dil1)); writer.writeTextElement("Dil2", writeGasDetails(details.dil2)); writer.writeTextElement("Dil3", writeGasDetails(details.dil3)); writer.writeTextElement("Dil4", writeGasDetails(details.dil4)); writer.writeTextElement("Dil5", writeGasDetails(details.dil5)); //Add setpoint values QString sp1 = QString("%1,%2") .arg(QString::number(details.sp1.sp), QString::number(details.sp1.depth)); QString sp2 = QString("%1,%2") .arg(QString::number(details.sp2.sp), QString::number(details.sp2.depth)); QString sp3 = QString("%1,%2") .arg(QString::number(details.sp3.sp), QString::number(details.sp3.depth)); QString sp4 = QString("%1,%2") .arg(QString::number(details.sp4.sp), QString::number(details.sp4.depth)); QString sp5 = QString("%1,%2") .arg(QString::number(details.sp5.sp), QString::number(details.sp5.depth)); writer.writeTextElement("SetPoint1", sp1); writer.writeTextElement("SetPoint2", sp2); writer.writeTextElement("SetPoint3", sp3); writer.writeTextElement("SetPoint4", sp4); writer.writeTextElement("SetPoint5", sp5); //Other Settings writer.writeTextElement("DiveMode", QString::number(details.diveMode)); writer.writeTextElement("Saturation", QString::number(details.saturation)); writer.writeTextElement("Desaturation", QString::number(details.desaturation)); writer.writeTextElement("LastDeco", QString::number(details.lastDeco)); writer.writeTextElement("Brightness", QString::number(details.brightness)); writer.writeTextElement("Units", QString::number(details.units)); writer.writeTextElement("SamplingRate", QString::number(details.samplingRate)); writer.writeTextElement("Salinity", QString::number(details.salinity)); writer.writeTextElement("DiveModeColor", QString::number(details.diveModeColor)); writer.writeTextElement("Language", QString::number(details.language)); writer.writeTextElement("DateFormat", QString::number(details.dateFormat)); writer.writeTextElement("CompassGain", QString::number(details.compassGain)); writer.writeTextElement("SafetyStop", QString::number(details.safetyStop)); writer.writeTextElement("GfHigh", QString::number(details.gfHigh)); writer.writeTextElement("GfLow", QString::number(details.gfLow)); writer.writeTextElement("PressureSensorOffset", QString::number(details.pressureSensorOffset)); writer.writeTextElement("PpO2Min", QString::number(details.ppO2Min)); writer.writeTextElement("PpO2Max", QString::number(details.ppO2Max)); writer.writeTextElement("FutureTTS", QString::number(details.futureTTS)); writer.writeTextElement("CcrMode", QString::number(details.ccrMode)); writer.writeTextElement("DecoType", QString::number(details.decoType)); writer.writeTextElement("AGFSelectable", QString::number(details.aGFSelectable)); writer.writeTextElement("AGFHigh", QString::number(details.aGFHigh)); writer.writeTextElement("AGFLow", QString::number(details.aGFLow)); writer.writeTextElement("CalibrationGas", QString::number(details.calibrationGas)); writer.writeTextElement("FlipScreen", QString::number(details.flipScreen)); writer.writeTextElement("SetPointFallback", QString::number(details.setPointFallback)); writer.writeTextElement("LeftButtonSensitivity", QString::number(details.leftButtonSensitivity)); writer.writeTextElement("RightButtonSensitivity", QString::number(details.rightButtonSensitivity)); writer.writeTextElement("BottomGasConsumption", QString::number(details.bottomGasConsumption)); writer.writeTextElement("DecoGasConsumption", QString::number(details.decoGasConsumption)); writer.writeTextElement("ModWarning", QString::number(details.modWarning)); writer.writeTextElement("DynamicAscendRate", QString::number(details.dynamicAscendRate)); writer.writeTextElement("GraphicalSpeedIndicator", QString::number(details.graphicalSpeedIndicator)); writer.writeTextElement("AlwaysShowppO2", QString::number(details.alwaysShowppO2)); // Suunto vyper settings. writer.writeTextElement("Altitude", QString::number(details.altitude)); writer.writeTextElement("PersonalSafety", QString::number(details.personalSafety)); writer.writeTextElement("TimeFormat", QString::number(details.timeFormat)); writer.writeStartElement("Light"); writer.writeAttribute("enabled", QString::number(details.lightEnabled)); writer.writeCharacters(QString::number(details.light)); writer.writeEndElement(); writer.writeStartElement("AlarmTime"); writer.writeAttribute("enabled", QString::number(details.alarmTimeEnabled)); writer.writeCharacters(QString::number(details.alarmTime)); writer.writeEndElement(); writer.writeStartElement("AlarmDepth"); writer.writeAttribute("enabled", QString::number(details.alarmDepthEnabled)); writer.writeCharacters(QString::number(details.alarmDepth)); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndDocument(); QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { lastError = tr("Could not save the backup file %1. Error Message: %2") .arg(fileName, file.errorString()); return false; } //file open successful. write data and save. QTextStream out(&file); out << xml; file.close(); return true; } bool ConfigureDiveComputer::restoreXMLBackup(const QString &fileName, DeviceDetails &details) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { lastError = tr("Could not open backup file: %1").arg(file.errorString()); return false; } QString xml = file.readAll(); QXmlStreamReader reader(xml); while (!reader.atEnd()) { if (reader.isStartElement()) { QString settingName = reader.name().toString(); QXmlStreamAttributes attributes = reader.attributes(); reader.readNext(); QString keyString = reader.text().toString(); if (settingName == "CustomText") details.customText = keyString; if (settingName == "Gas1") { QStringList gasData = keyString.split(","); gas gas1; gas1.oxygen = gasData.at(0).toInt(); gas1.helium = gasData.at(1).toInt(); gas1.type = gasData.at(2).toInt(); gas1.depth = gasData.at(3).toInt(); details.gas1 = gas1; } if (settingName == "Gas2") { QStringList gasData = keyString.split(","); gas gas2; gas2.oxygen = gasData.at(0).toInt(); gas2.helium = gasData.at(1).toInt(); gas2.type = gasData.at(2).toInt(); gas2.depth = gasData.at(3).toInt(); details.gas2 = gas2; } if (settingName == "Gas3") { QStringList gasData = keyString.split(","); gas gas3; gas3.oxygen = gasData.at(0).toInt(); gas3.helium = gasData.at(1).toInt(); gas3.type = gasData.at(2).toInt(); gas3.depth = gasData.at(3).toInt(); details.gas3 = gas3; } if (settingName == "Gas4") { QStringList gasData = keyString.split(","); gas gas4; gas4.oxygen = gasData.at(0).toInt(); gas4.helium = gasData.at(1).toInt(); gas4.type = gasData.at(2).toInt(); gas4.depth = gasData.at(3).toInt(); details.gas4 = gas4; } if (settingName == "Gas5") { QStringList gasData = keyString.split(","); gas gas5; gas5.oxygen = gasData.at(0).toInt(); gas5.helium = gasData.at(1).toInt(); gas5.type = gasData.at(2).toInt(); gas5.depth = gasData.at(3).toInt(); details.gas5 = gas5; } if (settingName == "Dil1") { QStringList dilData = keyString.split(","); gas dil1; dil1.oxygen = dilData.at(0).toInt(); dil1.helium = dilData.at(1).toInt(); dil1.type = dilData.at(2).toInt(); dil1.depth = dilData.at(3).toInt(); details.dil1 = dil1; } if (settingName == "Dil2") { QStringList dilData = keyString.split(","); gas dil2; dil2.oxygen = dilData.at(0).toInt(); dil2.helium = dilData.at(1).toInt(); dil2.type = dilData.at(2).toInt(); dil2.depth = dilData.at(3).toInt(); details.dil1 = dil2; } if (settingName == "Dil3") { QStringList dilData = keyString.split(","); gas dil3; dil3.oxygen = dilData.at(0).toInt(); dil3.helium = dilData.at(1).toInt(); dil3.type = dilData.at(2).toInt(); dil3.depth = dilData.at(3).toInt(); details.dil3 = dil3; } if (settingName == "Dil4") { QStringList dilData = keyString.split(","); gas dil4; dil4.oxygen = dilData.at(0).toInt(); dil4.helium = dilData.at(1).toInt(); dil4.type = dilData.at(2).toInt(); dil4.depth = dilData.at(3).toInt(); details.dil4 = dil4; } if (settingName == "Dil5") { QStringList dilData = keyString.split(","); gas dil5; dil5.oxygen = dilData.at(0).toInt(); dil5.helium = dilData.at(1).toInt(); dil5.type = dilData.at(2).toInt(); dil5.depth = dilData.at(3).toInt(); details.dil5 = dil5; } if (settingName == "SetPoint1") { QStringList spData = keyString.split(","); setpoint sp1; sp1.sp = spData.at(0).toInt(); sp1.depth = spData.at(1).toInt(); details.sp1 = sp1; } if (settingName == "SetPoint2") { QStringList spData = keyString.split(","); setpoint sp2; sp2.sp = spData.at(0).toInt(); sp2.depth = spData.at(1).toInt(); details.sp2 = sp2; } if (settingName == "SetPoint3") { QStringList spData = keyString.split(","); setpoint sp3; sp3.sp = spData.at(0).toInt(); sp3.depth = spData.at(1).toInt(); details.sp3 = sp3; } if (settingName == "SetPoint4") { QStringList spData = keyString.split(","); setpoint sp4; sp4.sp = spData.at(0).toInt(); sp4.depth = spData.at(1).toInt(); details.sp4 = sp4; } if (settingName == "SetPoint5") { QStringList spData = keyString.split(","); setpoint sp5; sp5.sp = spData.at(0).toInt(); sp5.depth = spData.at(1).toInt(); details.sp5 = sp5; } if (settingName == "Saturation") details.saturation = keyString.toInt(); if (settingName == "Desaturation") details.desaturation = keyString.toInt(); if (settingName == "DiveMode") details.diveMode = keyString.toInt(); if (settingName == "LastDeco") details.lastDeco = keyString.toInt(); if (settingName == "Brightness") details.brightness = keyString.toInt(); if (settingName == "Units") details.units = keyString.toInt(); if (settingName == "SamplingRate") details.samplingRate = keyString.toInt(); if (settingName == "Salinity") details.salinity = keyString.toInt(); if (settingName == "DiveModeColour") details.diveModeColor = keyString.toInt(); if (settingName == "Language") details.language = keyString.toInt(); if (settingName == "DateFormat") details.dateFormat = keyString.toInt(); if (settingName == "CompassGain") details.compassGain = keyString.toInt(); if (settingName == "SafetyStop") details.safetyStop = keyString.toInt(); if (settingName == "GfHigh") details.gfHigh = keyString.toInt(); if (settingName == "GfLow") details.gfLow = keyString.toInt(); if (settingName == "PressureSensorOffset") details.pressureSensorOffset = keyString.toInt(); if (settingName == "PpO2Min") details.ppO2Min = keyString.toInt(); if (settingName == "PpO2Max") details.ppO2Max = keyString.toInt(); if (settingName == "FutureTTS") details.futureTTS = keyString.toInt(); if (settingName == "CcrMode") details.ccrMode = keyString.toInt(); if (settingName == "DecoType") details.decoType = keyString.toInt(); if (settingName == "AGFSelectable") details.aGFSelectable = keyString.toInt(); if (settingName == "AGFHigh") details.aGFHigh = keyString.toInt(); if (settingName == "AGFLow") details.aGFLow = keyString.toInt(); if (settingName == "CalibrationGas") details.calibrationGas = keyString.toInt(); if (settingName == "FlipScreen") details.flipScreen = keyString.toInt(); if (settingName == "SetPointFallback") details.setPointFallback = keyString.toInt(); if (settingName == "LeftButtonSensitivity") details.leftButtonSensitivity = keyString.toInt(); if (settingName == "RightButtonSensitivity") details.rightButtonSensitivity = keyString.toInt(); if (settingName == "BottomGasConsumption") details.bottomGasConsumption = keyString.toInt(); if (settingName == "DecoGasConsumption") details.decoGasConsumption = keyString.toInt(); if (settingName == "ModWarning") details.modWarning = keyString.toInt(); if (settingName == "DynamicAscendRate") details.dynamicAscendRate = keyString.toInt(); if (settingName == "GraphicalSpeedIndicator") details.graphicalSpeedIndicator = keyString.toInt(); if (settingName == "AlwaysShowppO2") details.alwaysShowppO2 = keyString.toInt(); if (settingName == "Altitude") details.altitude = keyString.toInt(); if (settingName == "PersonalSafety") details.personalSafety = keyString.toInt(); if (settingName == "TimeFormat") details.timeFormat = keyString.toInt(); if (settingName == "Light") { if (attributes.hasAttribute("enabled")) details.lightEnabled = attributes.value("enabled").toString().toInt(); details.light = keyString.toInt(); } if (settingName == "AlarmDepth") { if (attributes.hasAttribute("enabled")) details.alarmDepthEnabled = attributes.value("enabled").toString().toInt(); details.alarmDepth = keyString.toInt(); } if (settingName == "AlarmTime") { if (attributes.hasAttribute("enabled")) details.alarmTimeEnabled = attributes.value("enabled").toString().toInt(); details.alarmTime = keyString.toInt(); } } reader.readNext(); } return true; } void ConfigureDiveComputer::startFirmwareUpdate(const QString &fileName, device_data_t *data, bool forceUpdate) { setState(FWUPDATE); if (firmwareThread) firmwareThread->deleteLater(); firmwareThread = new FirmwareUpdateThread(this, data, fileName, forceUpdate); connectThreadSignals(firmwareThread); firmwareThread->start(); } void ConfigureDiveComputer::resetSettings(device_data_t *data) { setState(RESETTING); if (resetThread) resetThread->deleteLater(); resetThread = new ResetSettingsThread(this, data); connectThreadSignals(resetThread); resetThread->start(); } void ConfigureDiveComputer::progressEvent(int percent) { emit progress(percent); } void ConfigureDiveComputer::setState(ConfigureDiveComputer::states newState) { currentState = newState; emit stateChanged(currentState); } void ConfigureDiveComputer::setError(QString err) { lastError = err; emit error(std::move(err)); } void ConfigureDiveComputer::readThreadFinished() { setState(DONE); if (lastError.isEmpty()) { //No error emit message(tr("Dive computer details read successfully")); } } void ConfigureDiveComputer::writeThreadFinished() { setState(DONE); if (lastError.isEmpty()) { //No error emit message(tr("Setting successfully written to device")); } } void ConfigureDiveComputer::firmwareThreadFinished() { setState(DONE); if (lastError.isEmpty()) { //No error emit message(tr("Device firmware successfully updated")); } } void ConfigureDiveComputer::resetThreadFinished() { setState(DONE); if (lastError.isEmpty()) { //No error emit message(tr("Device settings successfully reset")); } } QString ConfigureDiveComputer::dc_open(device_data_t *data) { FILE *fp = NULL; dc_status_t rc; if (data->libdc_log && !logfile_name.empty()) fp = subsurface_fopen(logfile_name.c_str(), "w"); data->libdc_logfile = fp; rc = dc_context_new(&data->context); if (rc != DC_STATUS_SUCCESS) { return tr("Unable to create libdivecomputer context"); } if (fp) { dc_context_set_loglevel(data->context, DC_LOGLEVEL_ALL); dc_context_set_logfunc(data->context, logfunc, fp); fprintf(data->libdc_logfile, "Subsurface: v%s, ", subsurface_git_version()); fprintf(data->libdc_logfile, "built with libdivecomputer v%s\n", dc_version(NULL)); } rc = divecomputer_device_open(data); if (rc != DC_STATUS_SUCCESS) { report_error("%s", errmsg(rc)); } else { rc = dc_device_open(&data->device, data->context, data->descriptor, data->iostream); } if (rc != DC_STATUS_SUCCESS) { return tr("Could not a establish connection to the dive computer."); } setState(OPEN); return NULL; } void ConfigureDiveComputer::dc_close(device_data_t *data) { if (data->device) dc_device_close(data->device); data->device = NULL; if (data->context) dc_context_free(data->context); data->context = NULL; dc_iostream_close(data->iostream); data->iostream = NULL; if (data->libdc_logfile) fclose(data->libdc_logfile); setState(INITIAL); }