diff --git a/CHANGELOG.md b/CHANGELOG.md index 272871122..17deea5ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +import: add option to synchronise dive computer time when downloading dives core: fix bug when save sea water salinity given by DC desktop: add option to force firmware update on OSTC4 desktop: add column for dive notes to the dive list table diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 2f12c5d50..deef9350a 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -374,6 +374,13 @@ of the dive computer (at least for those not charging while connected via USB). - Do *not* check the checkboxes labelled _Save libdivecomputer logfile_ and _Save libdivecomputer dumpfile_. These are only used as diagnostic tools when there are problems with downloads(see below). + + - With some dive computers it is possible to adjust the clock on the dive + computer based on the PC clock. This can be very helpful when dealing with + daylight savings time changes, or when travelling between different time + zones. In order to synchronise the dive computer clock with the PC clock + every time dives are imported, check _Sync dive computer time_. + - Then select the _Download_ button. With communication established, you can see how the data are retrieved from the dive computer. @@ -398,6 +405,7 @@ of the dive computer (at least for those not charging while connected via USB). (Puck Pro)". Refer to the text in the box below. + **** *PROBLEMS WITH DATA DOWNLOAD FROM A DIVE COMPUTER?* [icon="images/icons/important.png"] diff --git a/cli-downloader.cpp b/cli-downloader.cpp index 3eef0b2d5..3e38a2e9c 100644 --- a/cli-downloader.cpp +++ b/cli-downloader.cpp @@ -29,6 +29,7 @@ void cliDownloader(const char *vendor, const char *product, const char *device) data->setForceDownload(false); data->setSaveLog(true); data->setSaveDump(false); + data->setSyncTime(false); diveImportedModel.startDownload(); diveImportedModel.waitForDownload(); diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 3128a36c5..7b5707372 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -227,6 +227,7 @@ DCDeviceData::DCDeviceData() #if defined(Q_OS_ANDROID) data.androidUsbDeviceDescriptor = nullptr; #endif + data.sync_time = false; } DCDeviceData *DCDeviceData::instance() @@ -291,6 +292,11 @@ int DCDeviceData::diveId() const return data.diveid; } +bool DCDeviceData::syncTime() const +{ + return data.sync_time; +} + void DCDeviceData::setVendor(const QString &vendor) { data.vendor = copy_qstring(vendor); @@ -350,6 +356,11 @@ void DCDeviceData::setDiveId(int diveId) data.diveid = diveId; } +void DCDeviceData::setSyncTime(bool syncTime) +{ + data.sync_time = syncTime; +} + void DCDeviceData::setSaveDump(bool save) { data.libdc_dump = save; diff --git a/core/downloadfromdcthread.h b/core/downloadfromdcthread.h index 871a79556..324c39cf5 100644 --- a/core/downloadfromdcthread.h +++ b/core/downloadfromdcthread.h @@ -32,6 +32,7 @@ public: bool forceDownload() const; bool saveLog() const; int diveId() const; + bool syncTime() const; /* this needs to be a pointer to make the C-API happy */ device_data_t *internalData(); @@ -54,6 +55,7 @@ public: #if defined(Q_OS_ANDROID) void setUsbDevice(const android_usb_serial_device_descriptor &usbDescriptor); #endif + void setSyncTime(bool syncTime); private: #if defined(Q_OS_ANDROID) struct android_usb_serial_device_descriptor androidUsbDescriptor; diff --git a/core/libdivecomputer.c b/core/libdivecomputer.c index f1fd8f526..c5031cb52 100644 --- a/core/libdivecomputer.c +++ b/core/libdivecomputer.c @@ -1413,6 +1413,14 @@ dc_status_t divecomputer_device_open(device_data_t *data) return DC_STATUS_UNSUPPORTED; } +static dc_status_t sync_divecomputer_time(dc_device_t *device) +{ + dc_datetime_t now; + dc_datetime_localtime(&now, dc_datetime_now()); + + return dc_device_timesync(device, &now); +} + const char *do_libdivecomputer_import(device_data_t *data) { dc_status_t rc; @@ -1463,6 +1471,28 @@ const char *do_libdivecomputer_import(device_data_t *data) dev_info(data, "Starting import ..."); err = do_device_import(data); /* TODO: Show the logfile to the user on error. */ + dev_info(data, "Import complete"); + + if (!err && data->sync_time) { + dev_info(data, "Syncing dive computer time ..."); + rc = sync_divecomputer_time(data->device); + + switch (rc) { + case DC_STATUS_SUCCESS: + dev_info(data, "Time sync complete"); + + break; + case DC_STATUS_UNSUPPORTED: + dev_info(data, "Time sync not supported by dive computer"); + + break; + default: + dev_info(data, "Time sync failed"); + + break; + } + } + dc_device_close(data->device); data->device = NULL; if (!data->log->dives->nr) diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index c93daaedd..30f0f9c76 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -46,6 +46,7 @@ typedef struct { bool libdc_log; bool libdc_dump; bool bluetooth_mode; + bool sync_time; FILE *libdc_logfile; struct divelog *log; void *androidUsbDeviceDescriptor; diff --git a/core/pref.c b/core/pref.c index 85a5ec243..42188ecb4 100644 --- a/core/pref.c +++ b/core/pref.c @@ -93,6 +93,7 @@ struct preferences default_prefs = { .extract_video_thumbnails = true, .extract_video_thumbnails_position = 20, // The first fifth seems like a reasonable place .three_m_based_grid = false, + .sync_dc_time = false, }; /* copy a preferences block, including making copies of all included strings */ diff --git a/core/pref.h b/core/pref.h index e0c10cda4..1738e20b8 100644 --- a/core/pref.h +++ b/core/pref.h @@ -96,6 +96,7 @@ struct preferences { dive_computer_prefs_t dive_computer2; dive_computer_prefs_t dive_computer3; dive_computer_prefs_t dive_computer4; + bool sync_dc_time; // ********** Display ************* bool display_invalid_dives; diff --git a/core/settings/qPrefDiveComputer.cpp b/core/settings/qPrefDiveComputer.cpp index 0ee74254a..57c060808 100644 --- a/core/settings/qPrefDiveComputer.cpp +++ b/core/settings/qPrefDiveComputer.cpp @@ -29,6 +29,8 @@ void qPrefDiveComputer::loadSync(bool doSync) DISK_DC(2) DISK_DC(3) DISK_DC(4) + + disk_sync_dc_time(doSync); } // these are the 'active' settings @@ -54,3 +56,5 @@ HANDLE_PREFERENCE_TXT_EXT_ALT(DiveComputer, "dive_computer_vendor1", vendor, div HANDLE_PREFERENCE_TXT_EXT_ALT(DiveComputer, "dive_computer_vendor2", vendor, dive_computer, 2) HANDLE_PREFERENCE_TXT_EXT_ALT(DiveComputer, "dive_computer_vendor3", vendor, dive_computer, 3) HANDLE_PREFERENCE_TXT_EXT_ALT(DiveComputer, "dive_computer_vendor4", vendor, dive_computer, 4) + +HANDLE_PREFERENCE_BOOL(DiveComputer, "sync_dive_computer_time", sync_dc_time); diff --git a/core/settings/qPrefDiveComputer.h b/core/settings/qPrefDiveComputer.h index e3cb4df11..644a547e9 100644 --- a/core/settings/qPrefDiveComputer.h +++ b/core/settings/qPrefDiveComputer.h @@ -35,6 +35,8 @@ class qPrefDiveComputer : public QObject { Q_PROPERTY(QString vendor3 READ vendor3 WRITE set_vendor3 NOTIFY vendor3Changed) Q_PROPERTY(QString vendor4 READ vendor4 WRITE set_vendor4 NOTIFY vendor4Changed) + Q_PROPERTY(bool sync_dc_time READ sync_dc_time WRITE set_sync_dc_time NOTIFY sync_dc_timeChanged) + public: static qPrefDiveComputer *instance(); @@ -49,6 +51,8 @@ public: IMPLEMENT5GETTERS(product) IMPLEMENT5GETTERS(vendor) + static bool sync_dc_time() { return prefs.sync_dc_time; } + public slots: static void set_device(const QString &device); static void set_device1(const QString &device); @@ -74,6 +78,8 @@ public slots: static void set_vendor3(const QString &vendor); static void set_vendor4(const QString &vendor); + static void set_sync_dc_time(bool value); + signals: void deviceChanged(const QString &device); void device1Changed(const QString &device); @@ -99,6 +105,8 @@ signals: void vendor3Changed(const QString &vendor); void vendor4Changed(const QString &vendor); + void sync_dc_timeChanged(bool value); + private: qPrefDiveComputer() {} @@ -127,6 +135,8 @@ private: static void disk_vendor2(bool doSync); static void disk_vendor3(bool doSync); static void disk_vendor4(bool doSync); + + static void disk_sync_dc_time(bool doSync); }; #endif diff --git a/core/uemis-downloader.c b/core/uemis-downloader.c index df9f9e3c1..95c635e80 100644 --- a/core/uemis-downloader.c +++ b/core/uemis-downloader.c @@ -1518,6 +1518,9 @@ const char *do_uemis_import(device_data_t *data) if (uemis_mem_status != UEMIS_MEM_OK) result = translate("gettextFromC", ERR_FS_ALMOST_FULL); + if (data->sync_time) + uemis_info(translate("gettextFromC", "Time sync not supported by dive computer")); + bail: (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, &result); if (!strcmp(param_buff[0], "error")) { diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 7636a5661..b2d2824d6 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -60,6 +60,7 @@ DownloadFromDCWidget::DownloadFromDCWidget(QWidget *parent) : QDialog(parent, QF ui.vendor->setModel(&vendorModel); ui.search->setEnabled(is_vendor_searchable(ui.vendor->currentText())); ui.product->setModel(&productModel); + ui.syncDiveComputerTime->setChecked(prefs.sync_dc_time); progress_bar_text = ""; @@ -72,6 +73,7 @@ DownloadFromDCWidget::DownloadFromDCWidget(QWidget *parent) : QDialog(parent, QF connect(ui.logToFile, SIGNAL(stateChanged(int)), this, SLOT(checkLogFile(int))); connect(ui.selectAllButton, SIGNAL(clicked()), diveImportedModel, SLOT(selectAll())); connect(ui.unselectAllButton, SIGNAL(clicked()), diveImportedModel, SLOT(selectNone())); + connect(ui.syncDiveComputerTime, &QAbstractButton::toggled, qPrefDiveComputer::instance(), &qPrefDiveComputer::set_sync_dc_time); connect(timer, SIGNAL(timeout()), this, SLOT(updateProgressBar())); connect(close, SIGNAL(activated()), this, SLOT(close())); connect(quit, SIGNAL(activated()), parent, SLOT(close())); @@ -419,6 +421,7 @@ void DownloadFromDCWidget::on_downloadCancelRetryButton_clicked() data->setForceDownload(ui.forceDownload->isChecked()); data->setSaveLog(ui.logToFile->isChecked()); data->setSaveDump(ui.dumpToFile->isChecked()); + data->setSyncTime(ui.syncDiveComputerTime->isChecked()); qPrefDiveComputer::set_vendor(data->vendor()); qPrefDiveComputer::set_product(data->product()); @@ -582,6 +585,7 @@ void DownloadFromDCWidget::markChildrenAsDisabled() ui.unselectAllButton->setEnabled(false); ui.bluetoothMode->setEnabled(false); ui.chooseBluetoothDevice->setEnabled(false); + ui.syncDiveComputerTime->setEnabled(false); } void DownloadFromDCWidget::markChildrenAsEnabled() @@ -605,6 +609,7 @@ void DownloadFromDCWidget::markChildrenAsEnabled() ui.bluetoothMode->setEnabled(true); ui.chooseBluetoothDevice->setEnabled(true); #endif + ui.syncDiveComputerTime->setEnabled(true); } #if defined(BT_SUPPORT) diff --git a/desktop-widgets/downloadfromdivecomputer.h b/desktop-widgets/downloadfromdivecomputer.h index 68763701c..24c956890 100644 --- a/desktop-widgets/downloadfromdivecomputer.h +++ b/desktop-widgets/downloadfromdivecomputer.h @@ -85,6 +85,7 @@ private: BtDeviceSelectionDialog *btDeviceSelectionDialog; BTDiscovery *btd; #endif + void setSyncDiveComputerTime(bool value); public: bool preferDownloaded(); diff --git a/desktop-widgets/downloadfromdivecomputer.ui b/desktop-widgets/downloadfromdivecomputer.ui index fee199399..e429009f8 100644 --- a/desktop-widgets/downloadfromdivecomputer.ui +++ b/desktop-widgets/downloadfromdivecomputer.ui @@ -185,6 +185,16 @@ + + + + Sync dive computer time + + + Adjust the time on the dive computer to match the time on the PC (if supported by the dive computer model). + + + diff --git a/mobile-widgets/qml/DownloadFromDiveComputer.qml b/mobile-widgets/qml/DownloadFromDiveComputer.qml index 3346ad5c0..163241a20 100644 --- a/mobile-widgets/qml/DownloadFromDiveComputer.qml +++ b/mobile-widgets/qml/DownloadFromDiveComputer.qml @@ -397,7 +397,7 @@ Kirigami.Page { } RowLayout { - id: downloadOptions + id: forceDownloadOption Layout.fillWidth: true Layout.topMargin: 0 spacing: Kirigami.Units.smallSpacing @@ -421,6 +421,31 @@ Kirigami.Page { } } + RowLayout { + id: syncTimeOption + Layout.fillWidth: true + Layout.topMargin: 0 + spacing: Kirigami.Units.smallSpacing + TemplateCheckBox { + id: syncTimeWithDiveComputer + checked: Backend.sync_dc_time + enabled: syncTimeLabel.visible + visible: enabled + height: syncTimeLabel.height - Kirigami.Units.smallSpacing; + width: height + onClicked: { + Backend.sync_dc_time = checked + } + } + TemplateLabel { + id: syncTimeLabel + text: qsTr("Sync dive computer time") + visible: comboVendor.currentIndex != -1 && comboProduct.currentIndex != -1 && + comboConnection.currentIndex != -1 + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + } + ListView { id: dlList Layout.topMargin: Kirigami.Units.smallSpacing * 4 diff --git a/mobile-widgets/qmlinterface.cpp b/mobile-widgets/qmlinterface.cpp index e70c73cb9..52ac9f194 100644 --- a/mobile-widgets/qmlinterface.cpp +++ b/mobile-widgets/qmlinterface.cpp @@ -79,6 +79,9 @@ QMLInterface::QMLInterface() this, &QMLInterface::verbatim_planChanged); connect(qPrefDivePlanner::instance(), &qPrefDivePlanner::display_variationsChanged, this, &QMLInterface::display_variationsChanged); + + connect(qPrefDiveComputer::instance(), &qPrefDiveComputer::sync_dc_timeChanged, + this, &QMLInterface::sync_dc_timeChanged); } void QMLInterface::setup(QQmlContext *ct) diff --git a/mobile-widgets/qmlinterface.h b/mobile-widgets/qmlinterface.h index efa34cdc0..76f182f15 100644 --- a/mobile-widgets/qmlinterface.h +++ b/mobile-widgets/qmlinterface.h @@ -2,10 +2,12 @@ #ifndef QMLINTERFACE_H #define QMLINTERFACE_H #include "core/qthelper.h" +#include "core/downloadfromdcthread.h" #include "core/settings/qPrefCloudStorage.h" #include "core/settings/qPrefUnit.h" #include "core/settings/qPrefDivePlanner.h" #include "core/settings/qPrefTechnicalDetails.h" +#include "core/settings/qPrefDiveComputer.h" #include "qt-models/diveplannermodel.h" #include "backend-shared/plannershared.h" @@ -78,6 +80,8 @@ class QMLInterface : public QObject { Q_PROPERTY(bool verbatim_plan READ verbatim_plan WRITE set_verbatim_plan NOTIFY verbatim_planChanged); Q_PROPERTY(bool display_variations READ display_variations WRITE set_display_variations NOTIFY display_variationsChanged); + Q_PROPERTY(bool sync_dc_time READ sync_dc_time WRITE set_sync_dc_time NOTIFY sync_dc_timeChanged); + public: // function to do the needed setup static void setup(QQmlContext *ct); @@ -212,6 +216,8 @@ public: bool verbatim_plan() { return prefs.verbatim_plan; } bool display_variations() { return prefs.display_variations; } + bool sync_dc_time() { return prefs.sync_dc_time; } + public slots: void set_cloud_verification_status(CLOUD_STATUS value) { qPrefCloudStorage::set_cloud_verification_status(value); } void set_duration_units(DURATION value) { qPrefUnits::set_duration_units((units::DURATION)value); } @@ -258,6 +264,10 @@ public slots: void set_display_transitions(bool value) { DivePlannerPointsModel::instance()->setDisplayTransitions(value); } void set_verbatim_plan(bool value) { DivePlannerPointsModel::instance()->setVerbatim(value); } void set_display_variations(bool value) { DivePlannerPointsModel::instance()->setDisplayVariations(value); } + void set_sync_dc_time(bool value) { + qPrefDiveComputer::set_sync_dc_time(value); + DCDeviceData::instance()->setSyncTime(value); + } QString firstDiveDate() { return get_first_dive_date_string(); } QString lastDiveDate() { return get_last_dive_date_string(); } @@ -307,6 +317,8 @@ signals: void display_transitionsChanged(bool value); void verbatim_planChanged(bool value); void display_variationsChanged(bool value); + + void sync_dc_timeChanged(bool value); private: QMLInterface(); };