From a38ea971a00dda082e9e499a8aa67312d17908b4 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sun, 2 Apr 2023 18:28:33 +1200 Subject: [PATCH] Import: Add option to sync time on dive computer download Add an option for users to sync the dive computer time with the PC time every time dives are downloaded. Obviously this will only work on dive computers that have time synchronisation support in libdivecomputer, for other computers a notice is logged. The selection for this option is persisted as a preference. Signed-off-by: Michael Keller --- CHANGELOG.md | 1 + Documentation/user-manual.txt | 8 +++++ cli-downloader.cpp | 1 + core/downloadfromdcthread.cpp | 11 +++++++ core/downloadfromdcthread.h | 2 ++ core/libdivecomputer.c | 30 +++++++++++++++++++ core/libdivecomputer.h | 1 + core/pref.c | 1 + core/pref.h | 1 + core/settings/qPrefDiveComputer.cpp | 4 +++ core/settings/qPrefDiveComputer.h | 10 +++++++ core/uemis-downloader.c | 3 ++ desktop-widgets/downloadfromdivecomputer.cpp | 5 ++++ desktop-widgets/downloadfromdivecomputer.h | 1 + desktop-widgets/downloadfromdivecomputer.ui | 10 +++++++ .../qml/DownloadFromDiveComputer.qml | 27 ++++++++++++++++- mobile-widgets/qmlinterface.cpp | 3 ++ mobile-widgets/qmlinterface.h | 12 ++++++++ 18 files changed, 130 insertions(+), 1 deletion(-) 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(); };