From cca1a1760798344c865fa46d878333556cfb3ac6 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 1 Jul 2016 11:36:59 +0000 Subject: [PATCH] First attempt at a time based Tx watchdog The watchdog configuration is now a spin box of minutes with a special first value of disabled. WSJT-X counts down minutes on the minute, if the countdown reaches zero any attempt to transmit is aborted. The countdown is reset by any mouse press or key press event inside the main window or if the Tx message changes (auto sequencing). The status bar now shows the Tx watchdog countdown if it is active. This information is shown to the right of the progress bar text. The warning message boxes triggered within the GUI update routine now no longer block the GUI updates. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6840 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- Configuration.cpp | 12 +-- Configuration.hpp | 2 +- Configuration.ui | 215 ++++++++++++++++++++++++++-------------------- mainwindow.cpp | 205 +++++++++++++++++++++++++++++-------------- mainwindow.h | 13 +-- mainwindow.ui | 16 ---- 6 files changed, 280 insertions(+), 183 deletions(-) diff --git a/Configuration.cpp b/Configuration.cpp index 657e6d9b0..8c761b6f6 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -534,7 +534,7 @@ private: bool miles_; bool quick_call_; bool disable_TX_on_73_; - bool watchdog_; + int watchdog_; bool TX_messages_; bool enable_VHF_features_; bool decode_at_52s_; @@ -623,7 +623,7 @@ bool Configuration::clear_DX () const {return m_->clear_DX_;} bool Configuration::miles () const {return m_->miles_;} bool Configuration::quick_call () const {return m_->quick_call_;} bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;} -bool Configuration::watchdog () const {return m_->watchdog_;} +int Configuration::watchdog () const {return m_->watchdog_;} bool Configuration::TX_messages () const {return m_->TX_messages_;} bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features_;} bool Configuration::decode_at_52s () const {return m_->decode_at_52s_;} @@ -1054,7 +1054,7 @@ void Configuration::impl::initialize_models () ui_->miles_check_box->setChecked (miles_); ui_->quick_call_check_box->setChecked (quick_call_); ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_); - ui_->watchdog_check_box->setChecked (watchdog_); + ui_->tx_watchdog_spin_box->setValue (watchdog_); ui_->TX_messages_check_box->setChecked (TX_messages_); ui_->enable_VHF_features_check_box->setChecked(enable_VHF_features_); ui_->decode_at_52s_check_box->setChecked(decode_at_52s_); @@ -1282,7 +1282,7 @@ void Configuration::impl::read_settings () miles_ = settings_->value ("Miles", false).toBool (); quick_call_ = settings_->value ("QuickCall", false).toBool (); disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool (); - watchdog_ = settings_->value ("Runaway", false).toBool (); + watchdog_ = settings_->value ("TxWatchdog", 6).toInt (); TX_messages_ = settings_->value ("Tx2QSO", true).toBool (); enable_VHF_features_ = settings_->value("VHFUHF",false).toBool (); decode_at_52s_ = settings_->value("Decode52",false).toBool (); @@ -1376,7 +1376,7 @@ void Configuration::impl::write_settings () settings_->setValue ("Miles", miles_); settings_->setValue ("QuickCall", quick_call_); settings_->setValue ("73TxDisable", disable_TX_on_73_); - settings_->setValue ("Runaway", watchdog_); + settings_->setValue ("TxWatchdog", watchdog_); settings_->setValue ("Tx2QSO", TX_messages_); settings_->setValue ("CATForceDTR", rig_params_.force_dtr); settings_->setValue ("DTR", rig_params_.dtr_high); @@ -1777,7 +1777,7 @@ void Configuration::impl::accept () miles_ = ui_->miles_check_box->isChecked (); quick_call_ = ui_->quick_call_check_box->isChecked (); disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked (); - watchdog_ = ui_->watchdog_check_box->isChecked (); + watchdog_ = ui_->tx_watchdog_spin_box->value (); TX_messages_ = ui_->TX_messages_check_box->isChecked (); data_mode_ = static_cast (ui_->TX_mode_button_group->checkedId ()); save_directory_ = ui_->save_path_display_label->text (); diff --git a/Configuration.hpp b/Configuration.hpp index 4c0f99c44..b2cbae192 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -113,7 +113,7 @@ public: bool miles () const; bool quick_call () const; bool disable_TX_on_73 () const; - bool watchdog () const; + int watchdog () const; bool TX_messages () const; bool split_mode () const; bool enable_VHF_features () const; diff --git a/Configuration.ui b/Configuration.ui index 3d6379b7d..cfb7b886f 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -132,6 +132,16 @@ Display + + + + Include a separator line between periods in the band activity window. + + + &Blank line between decoding periods + + + @@ -145,13 +155,36 @@ - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - Include a separator line between periods in the band activity window. + Show distance to DX station in miles rather than kilometers. - &Blank line between decoding periods + Display dista&nce in miles + + + + + + + Show outgoing transmitted messages in the Rx frequency window. + + + &Tx messages to Rx frequency window @@ -192,39 +225,6 @@ - - - - Show distance to DX station in miles rather than kilometers. - - - Display dista&nce in miles - - - - - - - Show outgoing transmitted messages in the Rx frequency window. - - - &Tx messages to Rx frequency window - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -255,47 +255,6 @@ - - - - <html><head/><body><p>Check this if you wish to automatically return to the last monitored frequency when monitor is enabled, leave it unchecked if you wish to have the current rig frequency maintained.</p></body></html> - - - Monitor returns to last used frequency - - - - - - - Turns off automatic transmissions after sending a 73 or any other free -text message. - - - Di&sable Tx after sending 73 - - - - - - - Stop transmitting automatically after five periods. - - - Runaway Tx &watchdog - - - - - - - Automatic transmission mode. - - - Doubl&e-click on call sets Tx enable - - - @@ -309,6 +268,13 @@ text message. + + + + Decode at t = 52 s + + + @@ -368,6 +334,13 @@ quiet period when decoding is done. + + + + Single decode + + + @@ -378,17 +351,80 @@ quiet period when decoding is done. - - + + + + + + Tx watchdog: + + + tx_watchdog_spin_box + + + + + + + <html><head/><body><p>Number of minutes before unattended transmissions are aborted</p></body></html> + + + Disabled + + + minutes + + + + + + 6 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <html><head/><body><p>Check this if you wish to automatically return to the last monitored frequency when monitor is enabled, leave it unchecked if you wish to have the current rig frequency maintained.</p></body></html> + - Decode at t = 52 s + Monitor returns to last used frequency - - + + + + Automatic transmission mode. + - Single decode + Doubl&e-click on call sets Tx enable + + + + + + + Turns off automatic transmissions after sending a 73 or any other free +text message. + + + Di&sable Tx after sending 73 @@ -2376,10 +2412,7 @@ soundcard changes font_push_button decoded_text_font_push_button monitor_off_check_box - quick_call_check_box tx_QSY_check_box - disable_TX_on_73_check_box - watchdog_check_box decode_at_52s_check_box offset_Rx_freq_check_box CW_id_after_73_check_box @@ -2522,11 +2555,11 @@ soundcard changes + + + + - - - - diff --git a/mainwindow.cpp b/mainwindow.cpp index d9a7959ad..76fb0a535 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -143,6 +143,13 @@ namespace && ((type < 6 && msg_parts.contains ("73")) || (type == 6 && !msg_parts.filter ("73").isEmpty ())); } + + int ms_to_next_minute () + { + auto const& now = QDateTime::currentDateTime (); + auto const& time = now.time (); + return now.msecsTo (QDateTime {now.date (), QTime {time.hour (), time.minute (), 0}}.addSecs (60)); + } } //--------------------------------------------------- MainWindow constructor @@ -192,7 +199,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_inGain {0}, m_secID {0}, m_repeatMsg {0}, - m_watchdogLimit {6}, m_nSubMode {0}, m_nclearave {1}, m_pctx {0}, @@ -423,7 +429,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, // text message, send + empty text means send the current free // text message without change, !send + empty text means clear // the current free text message - qDebug () << "Free text UDP message - text:" << text << "send:" << send << "text empty:" << text.isEmpty (); if (0 == ui->tabWidget->currentIndex ()) { if (!text.isEmpty ()) { ui->tx5->setCurrentText (text); @@ -507,7 +512,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, }); setWindowTitle (program_title ()); - createStatusBar(); connect(&proc_jt9, &QProcess::readyReadStandardOutput, this, &MainWindow::readFromStdout); connect(&proc_jt9, static_cast (&QProcess::error), @@ -654,6 +658,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, readSettings(); //Restore user's setup params + createStatusBar(); + m_audioThread.start (m_audioThreadPriority); #ifdef WIN32 @@ -834,10 +840,42 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_wideGraph->setModeTx(m_modeTx); ui->sbSubmode->setValue(m_nSubMode); + connect (&minuteTimer, &QTimer::timeout, this, &MainWindow::on_the_minute); + minuteTimer.setSingleShot (true); + minuteTimer.start (ms_to_next_minute ()); + // this must be the last statement of constructor if (!m_valid) throw std::runtime_error {"Fatal initialization exception"}; } +void MainWindow::on_the_minute () +{ + if (minuteTimer.isSingleShot ()) + { + minuteTimer.setSingleShot (false); + minuteTimer.start (60 * 1000); // run free + } + else + { + auto const& ms = ms_to_next_minute (); + if (qAbs (60 * 1000 - ms) > 1000) // correct drift + { + minuteTimer.setSingleShot (true); + minuteTimer.start (ms); + } + } + + if (m_repeatMsg < m_config.watchdog ()) ++m_repeatMsg; + if (!m_mode.startsWith ("WSPR") && m_config.watchdog () != 0) + { + updateProgressBarFormat (true); + } + else + { + updateProgressBarFormat (false); + } +} + //--------------------------------------------------- MainWindow destructor MainWindow::~MainWindow() { @@ -1392,7 +1430,7 @@ void MainWindow::updateProgressBarFormat (bool wd_in_use) { if (wd_in_use) { - progressBar->setFormat (QString {"%v/%m WD:%1"}.arg (m_watchdogLimit - m_repeatMsg)); + progressBar->setFormat (QString {"%v/%m WD:%1m"}.arg (m_config.watchdog () - m_repeatMsg)); } else { @@ -1400,28 +1438,8 @@ void MainWindow::updateProgressBarFormat (bool wd_in_use) } } -void MainWindow::mousePressEvent (QMouseEvent * e) -{ - if (!m_mode.startsWith ("WSPR") && m_mode!="Echo" && m_config.watchdog ()) { - m_repeatMsg = 0; // reset Tx watchdog - updateProgressBarFormat (true); - } - else { - updateProgressBarFormat (false); - } - QMainWindow::mousePressEvent (e); -} - void MainWindow::keyPressEvent (QKeyEvent * e) { - if (!m_mode.startsWith ("WSPR") && m_mode!="Echo" && m_config.watchdog ()) { - m_repeatMsg = 0; // reset Tx watchdog - updateProgressBarFormat (true); - } - else { - updateProgressBarFormat (false); - } - int n; switch(e->key()) { @@ -1570,17 +1588,35 @@ void MainWindow::statusChanged() << ui->rptSpinBox->value() << ";" << m_modeTx << endl; f.close(); } else { - msgBox("Cannot open \"" + f.fileName () + "\" for writing:" + f.errorString ()); + msgBox (tr ("Cannot open \"%1\" for writing: %2").arg (f.fileName ()).arg (f.errorString ())); } } -bool MainWindow::eventFilter(QObject *object, QEvent *event) //eventFilter() +bool MainWindow::eventFilter (QObject * object, QEvent * event) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); - MainWindow::keyPressEvent(keyEvent); - return QObject::eventFilter(object, event); - } + switch (event->type()) + { + case QEvent::KeyPress: + // fall through + case QEvent::MouseButtonPress: + if (m_repeatMsg && !m_mode.startsWith ("WSPR") && m_config.watchdog () != 0) { + m_repeatMsg = 0; // reset Tx watchdog + updateProgressBarFormat (true); + } + break; + + case QEvent::ChildAdded: + // ensure our child widgets get added to our event filter + add_child_to_event_filter (static_cast (event)->child ()); + break; + + case QEvent::ChildRemoved: + // ensure our child widgets get d=removed from our event filter + remove_child_from_event_filter (static_cast (event)->child ()); + break; + + default: break; + } return QObject::eventFilter(object, event); } @@ -1592,7 +1628,6 @@ void MainWindow::createStatusBar() //createStatusBar tx_status_label->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusBar()->addWidget(tx_status_label); - mode_label->setAlignment(Qt::AlignHCenter); mode_label->setMinimumSize(QSize(80,18)); mode_label->setFrameStyle(QFrame::Panel | QFrame::Sunken); @@ -1610,7 +1645,7 @@ void MainWindow::createStatusBar() //createStatusBar statusBar()->addWidget(progressBar); progressBar->setMinimumSize (QSize {150, 18}); - updateProgressBarFormat (!m_mode.startsWith ("WSPR") && m_mode!="Echo" && m_config.watchdog ()); + updateProgressBarFormat (!m_mode.startsWith ("WSPR") && m_config.watchdog () != 0); } void MainWindow::subProcessFailed (QProcess * process, int exit_code, QProcess::ExitStatus status) @@ -1684,11 +1719,11 @@ void MainWindow::on_stopButton_clicked() //stopButton } } -void MainWindow::msgBox(QString t) //msgBox +void MainWindow::msgBox (QString const& text) { - msgBox0.setText(t); + msgBox0.setText (text); QApplication::alert (this); - msgBox0.exec(); + msgBox0.exec (); } void MainWindow::on_actionOnline_User_Guide_triggered() //Display manual @@ -2149,7 +2184,7 @@ void::MainWindow::fast_decode_done() out << message.mid(0,n-2) << endl; f.close(); } else { - msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ()); + msgBox (tr ("Cannot open \"%1\" for append: %2").arg (f.fileName ()).arg (f.errorString ())); } if(m_mode=="JT9" or m_mode=="JTMSK" or m_mode=="MSK144") { @@ -2236,7 +2271,7 @@ void MainWindow::readFromStdout() //readFromStdout out << t.mid(0,n-2) << endl; f.close(); } else { - msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ()); + msgBox (tr ("Cannot open \"%1\" for append: %2").arg (f.fileName ()).arg (f.errorString ())); } if (m_config.insert_blank () && m_blankLine) @@ -2485,13 +2520,23 @@ void MainWindow::guiUpdate() if (m_auto) auto_tx_mode (false); if(onAirFreq!=m_onAirFreq0) { m_onAirFreq0=onAirFreq; - QString t="Please choose another Tx frequency.\n"; - t+="WSJT-X will not knowingly transmit another\n"; - t+="mode in the WSPR sub-band on 30 m."; - msgBox(t); + auto const& message = tr ("Please choose another Tx frequency.\n" + "WSJT-X will not knowingly transmit another\n" + "mode in the WSPR sub-band on 30 m."); + QTimer::singleShot (0, [=] {msgBox (message);}); // don't block guiUpdate } } + if (!m_mode.startsWith ("WSPR") && m_config.watchdog() != 0 + && m_repeatMsg >= m_config. watchdog ()) { + m_bTxTime=false; + if (m_tune) stop_tuning (); + if (m_auto) auto_tx_mode (false); + QTimer::singleShot (0, [=] {msgBox ("Runaway Tx watchdog");}); // don't block guiUpdate + m_repeatMsg = 0; + updateProgressBarFormat (true); + } + float fTR=float((nsec%m_TRperiod))/m_TRperiod; // if(g_iptt==0 and ((m_bTxTime and fTR<0.4) or m_tune )) { if(g_iptt==0 and ((m_bTxTime and fTR<99) or m_tune )) { //### Allow late starts @@ -2622,7 +2667,9 @@ void MainWindow::guiUpdate() } else { - msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ()); + auto const& message = tr ("Cannot open \"%1\" for append: %2") + .arg (f.fileName ()).arg (f.errorString ()); + QTimer::singleShot (0, [=] {msgBox (message);}); // don't block guiUpdate } if (m_config.TX_messages ()) { @@ -2702,18 +2749,13 @@ void MainWindow::guiUpdate() if (g_iptt == 1 && m_iptt0 == 0) { auto const& current_message = QString::fromLatin1 (msgsent); - if(!m_mode.startsWith ("WSPR") && m_mode!="Echo" && m_config.watchdog ()) { - if (current_message == m_msgSent0) { - m_repeatMsg++; - } else { + if(!m_mode.startsWith ("WSPR") && m_config.watchdog () != 0) { + if (current_message != m_msgSent0) { m_repeatMsg=0; // in case we are auto sequencing m_msgSent0 = current_message; } updateProgressBarFormat (true); } - else { - updateProgressBarFormat (false); - } if(!m_tune) { QFile f {m_dataDir.absoluteFilePath ("ALL.TXT")}; @@ -2725,8 +2767,9 @@ void MainWindow::guiUpdate() << ": " << m_currentMessage << endl; f.close(); } else { - msgBox("Cannot open \"" + f.fileName () + "\" for append:" + - f.errorString ()); + auto const& message = tr ("Cannot open \"%1\" for append: %2") + .arg (f.fileName ()).arg(f.errorString ()); + QTimer::singleShot (0, [=] {msgBox (message);}); // don't block guiUpdate } } @@ -2844,7 +2887,8 @@ void MainWindow::startTx2() << m_currentMessage << " " + m_mode << endl; f.close(); } else { - msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ()); + msgBox (tr ("Cannot open \"%1\" for append: %2") + .arg (f.fileName ()).arg (f.errorString ())); } } } @@ -2870,17 +2914,6 @@ void MainWindow::stopTx2() on_stopTxButton_clicked(); m_nTx73=0; } - if (!m_mode.startsWith ("WSPR") && m_mode!="Echo" && m_config.watchdog()) { - if (m_repeatMsg >= m_watchdogLimit) { - on_stopTxButton_clicked(); - msgBox("Runaway Tx watchdog"); - m_repeatMsg = 0; - } - updateProgressBarFormat (true); - } - else { - updateProgressBarFormat (false); - } if(m_mode.startsWith ("WSPR") and m_ntr==-1 and !m_tuneup) { m_wideGraph->setWSPRtransmitted(); WSPR_scheduling (); @@ -4136,6 +4169,7 @@ void MainWindow::WSPR_config(bool b) auto_tx_label->setText (m_config.quick_call () ? "Auto-Tx-Enable Armed" : "Auto-Tx-Enable Disarmed"); m_bSimplex = false; } + auto_tx_label->setVisible (!b); enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook } @@ -5574,3 +5608,46 @@ void MainWindow::statusUpdate () const m_hisGrid); } } + +void MainWindow::childEvent (QChildEvent * e) +{ + if (e->child ()->isWidgetType ()) + { + switch (e->type ()) + { + case QEvent::ChildAdded: add_child_to_event_filter (e->child ()); break; + case QEvent::ChildRemoved: remove_child_from_event_filter (e->child ()); break; + default: break; + } + } + QMainWindow::childEvent (e); +} + +// add widget and any child widgets to our event filter so that we can +// take action on key press ad mouse press events anywhere in the main window +void MainWindow::add_child_to_event_filter (QObject * target) +{ + if (target && target->isWidgetType ()) + { + target->installEventFilter (this); + } + auto const& children = target->children (); + for (auto iter = children.begin (); iter != children.end (); ++iter) + { + add_child_to_event_filter (*iter); + } +} + +// recursively remove widget and any child widgets from our event filter +void MainWindow::remove_child_from_event_filter (QObject * target) +{ + auto const& children = target->children (); + for (auto iter = children.begin (); iter != children.end (); ++iter) + { + remove_child_from_event_filter (*iter); + } + if (target && target->isWidgetType ()) + { + target->removeEventFilter (this); + } +} diff --git a/mainwindow.h b/mainwindow.h index f2d04dc66..9822d3d94 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -113,9 +113,9 @@ public slots: protected: void keyPressEvent (QKeyEvent *) override; - void mousePressEvent (QMouseEvent *) override; - void closeEvent(QCloseEvent*) override; - bool eventFilter(QObject *object, QEvent *event) override; + void closeEvent(QCloseEvent *) override; + void childEvent(QChildEvent *) override; + bool eventFilter(QObject *, QEvent *) override; private slots: void on_tx1_editingFinished(); @@ -351,7 +351,6 @@ private: qint32 m_ncw; qint32 m_secID; qint32 m_repeatMsg; - qint32 m_watchdogLimit; qint32 m_nSubMode; qint32 m_nclearave; qint32 m_minSync; @@ -458,6 +457,7 @@ private: QTimer tuneATU_Timer; QTimer TxAgainTimer; QTimer RxQSYTimer; + QTimer minuteTimer; QString m_path; QString m_baseCall; @@ -514,7 +514,7 @@ private: void writeSettings(); void createStatusBar(); void updateStatusBar(); - void msgBox(QString t); + void msgBox(QString const&); void genStdMsgs(QString rpt); void clearDX (); void lookup(); @@ -558,6 +558,9 @@ private: void subProcessError (QProcess *, QProcess::ProcessError); void statusUpdate () const; void updateProgressBarFormat (bool wd_in_use); + void on_the_minute (); + void add_child_to_event_filter (QObject *); + void remove_child_from_event_filter (QObject *); }; extern int killbyname(const char* progName); diff --git a/mainwindow.ui b/mainwindow.ui index 63ab27659..3ca1ac77f 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -2,14 +2,6 @@ MainWindow - - - 0 - 0 - 896 - 565 - - WSJT-X by K1JT @@ -2284,14 +2276,6 @@ QPushButton[state="ok"] { - - - 0 - 0 - 896 - 21 - - File