Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/gui/ConnectionPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,9 @@ void ConnectionPanel::setConnected(bool connected)
clearPendingIcomCredentials();
}

if (connected)
m_startupProbe = false;

m_connected = connected;
m_disconnectBtn->setVisible(connected);
updateActionState();
Expand Down Expand Up @@ -2493,6 +2496,7 @@ void ConnectionPanel::onManualConnectClicked()
return;

m_manualConnectPending = true;
m_startupProbe = false;
setManualMessage(QStringLiteral("Checking %1…").arg(ip));
probeRadio(ip);
}
Expand All @@ -2503,12 +2507,33 @@ void ConnectionPanel::onManualAdvancedToggled(bool checked)
m_manualAdvancedWidget->setVisible(checked);
}

void ConnectionPanel::reportStartupProbeFailure(const QString& reason)
{
// A startup probe is the one probe with nobody reading the manual page.
// MainWindow has covered the window with "Looking for your radio…", and it
// suppressed the no-saved-radio dialog popup precisely BECAUSE a radio is
// saved — so setManualMessage() alone writes the reason onto a page behind a
// dialog that will never open. The operator is left with a spinner and no
// route back to the connection UI. Hand the reason up instead.
if (!m_startupProbe)
return;
m_startupProbe = false;
emit startupConnectUnavailable(reason);
}

void ConnectionPanel::probeRadio(const QString& ip, bool restoreSavedFamily)
{
const QString trimmedIp = ip.trimmed();
if (trimmedIp.isEmpty())
return;

// Latched, not assigned: the Icom keychain read below re-enters probeRadio()
// without restoreSavedFamily, and that second pass is still the same startup
// attempt. Cleared when a bail is reported, on a proven connect, and by
// onManualConnectClicked().
if (restoreSavedFamily)
m_startupProbe = true;

// Interactive and automation probes keep the family currently selected by
// the operator. Startup is the exception: it has no current operator
// choice, so restoreSavedFamily asks the retained route which wire protocol
Expand All @@ -2532,6 +2557,8 @@ void ConnectionPanel::probeRadio(const QString& ip, bool restoreSavedFamily)
updateManualAdvancedVisibility();
setManualMessage("Choose a live source path before trying again.", true);
m_manualConnectPending = false;
reportStartupProbeFailure(
QStringLiteral("The saved source path for this radio is unavailable."));
return;
}

Expand Down Expand Up @@ -2573,6 +2600,8 @@ void ConnectionPanel::probeRadio(const QString& ip, bool restoreSavedFamily)
"radio. Check Network Control is ON in the radio's menu, then "
"enter the same credentials here."),
true);
reportStartupProbeFailure(
QStringLiteral("This Icom needs its network user name and password."));
return;
}
if (pass.isEmpty()) {
Expand Down Expand Up @@ -2603,6 +2632,8 @@ void ConnectionPanel::probeRadio(const QString& ip, bool restoreSavedFamily)
"set on the radio, then connect once to remember it."),
true);
panel->m_manualConnectPending = false;
panel->reportStartupProbeFailure(
QStringLiteral("No saved password for this Icom."));
return;
}
if (panel->m_manualIcomPassEdit
Expand Down Expand Up @@ -2721,6 +2752,8 @@ void ConnectionPanel::probeRadio(const QString& ip, bool restoreSavedFamily)
QStringLiteral("Could not resolve \"%1\". Check the name, or enter the "
"radio's IP address instead.").arg(trimmedIp),
true);
reportStartupProbeFailure(
QStringLiteral("Could not resolve \"%1\".").arg(trimmedIp));
return;
}
resolved = hostInfo.addresses().first();
Expand Down Expand Up @@ -2765,7 +2798,11 @@ void ConnectionPanel::probeRadio(const QString& ip, bool restoreSavedFamily)
"radio is powered, idle, and reachable on UDP port 1024.")
.arg(trimmedIp),
true);
reportStartupProbeFailure(
QStringLiteral("No Hermes-Lite 2 answered at %1.").arg(trimmedIp));
} else if (probe == Hl2ProbeResult::NotAttempted) {
// probeHermesLite2() has already reported its own reason, upward
// included; only the button needs restoring here.
resetManualConnectButton();
}
return;
Expand Down Expand Up @@ -2834,11 +2871,15 @@ ConnectionPanel::Hl2ProbeResult ConnectionPanel::probeHermesLite2(
QStringLiteral("AetherSDR could not use that VPN source path. "
"Try Auto or choose another path."),
true);
reportStartupProbeFailure(
QStringLiteral("The saved source path for this radio is unavailable."));
} else {
setManualMessage(
QStringLiteral("Could not open a UDP socket to probe for a "
"Hermes-Lite 2: %1").arg(hpsdr.errorString()),
true);
reportStartupProbeFailure(
QStringLiteral("Could not open a UDP socket to reach the Hermes-Lite 2."));
}
return Hl2ProbeResult::NotAttempted;
}
Expand Down Expand Up @@ -2876,6 +2917,8 @@ ConnectionPanel::Hl2ProbeResult ConnectionPanel::probeHermesLite2(
: QStringLiteral("“%1” has no IPv4 address, and a Hermes-Lite 2 "
"is reachable over IPv4 only.").arg(ip),
true);
reportStartupProbeFailure(
QStringLiteral("Could not resolve “%1” to an IPv4 address.").arg(ip));
return Hl2ProbeResult::NotAttempted;
}
}
Expand All @@ -2891,6 +2934,8 @@ ConnectionPanel::Hl2ProbeResult ConnectionPanel::probeHermesLite2(
QStringLiteral("Could not send a discovery request to %1: %2")
.arg(dest.toString(), hpsdr.errorString()),
true);
reportStartupProbeFailure(
QStringLiteral("Could not send a discovery request to %1.").arg(dest.toString()));
return Hl2ProbeResult::NotAttempted;
}

Expand Down Expand Up @@ -2951,6 +2996,9 @@ ConnectionPanel::Hl2ProbeResult ConnectionPanel::probeHermesLite2(
QStringLiteral("The Hermes-Lite 2 at %1 is already in use by another client "
"and can't be shared.").arg(ip),
true);
reportStartupProbeFailure(
QStringLiteral("The Hermes-Lite 2 at %1 is in use by another client.")
.arg(ip));
return Hl2ProbeResult::Answered;
}

Expand Down Expand Up @@ -2987,6 +3035,8 @@ void ConnectionPanel::probeFlexRadio(const QString& trimmedIp, const RadioBindSe
m_manualConnectPending = false;
m_manualConnectBtn->setText("Connect by IP");
updateActionState();
reportStartupProbeFailure(
QStringLiteral("The saved source path for this radio is unavailable."));
return;
}

Expand All @@ -3004,6 +3054,8 @@ void ConnectionPanel::probeFlexRadio(const QString& trimmedIp, const RadioBindSe
"address and try Advanced only if your VPN exposes multiple adapters.")
.arg(trimmedIp),
true);
reportStartupProbeFailure(
QStringLiteral("No radio responded at %1.").arg(trimmedIp));
}
});

Expand Down Expand Up @@ -3161,6 +3213,8 @@ void ConnectionPanel::probeFlexRadio(const QString& trimmedIp, const RadioBindSe
m_manualConnectPending = false;
m_manualConnectBtn->setText("Connect by IP");
updateActionState();
reportStartupProbeFailure(
QStringLiteral("Could not reach %1: %2").arg(trimmedIp, sock->errorString()));
});
}

Expand Down
18 changes: 18 additions & 0 deletions src/gui/ConnectionPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ public slots:
void retryDiscoveryRequested();
void networkDiagnosticsRequested();
void smartLinkLoginRequested(const QString& email, const QString& password);
// A startup auto-connect gave up before it could reach the radio. Carries
// the operator-facing reason, which would otherwise be stranded on the
// manual page (see reportStartupProbeFailure).
void startupConnectUnavailable(const QString& reason);

private slots:
void onConnectionModeClicked(int id);
Expand Down Expand Up @@ -184,6 +188,13 @@ private slots:
};
Hl2ProbeResult probeHermesLite2(const QString& ip, const RadioBindSettings& bindSettings);
void probeFlexRadio(const QString& ip, const RadioBindSettings& bindSettings);
// Reports a startup-probe bail upward as startupConnectUnavailable. Every
// probeRadio() exit that does not end in a connect request calls this —
// including the ones inside probeHermesLite2() and probeFlexRadio() — so
// that a saved radio which cannot be reached, for whatever reason, hands
// the window back. No-op for interactive and automation probes, whose
// operator can already read setManualMessage.
void reportStartupProbeFailure(const QString& reason);
void resetManualConnectButton();
// Re-activate the body layout after a page change. The overlap this used to
// guard against — the Advanced section expanding, or the result line
Expand Down Expand Up @@ -298,6 +309,13 @@ private slots:
QPushButton* m_manualConnectBtn{nullptr};
QString m_manualProfileIp;
bool m_manualConnectPending{false};
// Set for the duration of a startup probe (probeRadio's restoreSavedFamily
// call), which is the one probe with no operator watching the manual page.
// Cleared in three distinct places, none redundant: by
// reportStartupProbeFailure() once a bail has been handed up, by
// setConnected(true) when the probe led to a real connection, and by
// onManualConnectClicked() when the operator takes over mid-flight.
bool m_startupProbe{false};
QCheckBox* m_autoConnectCheck{nullptr};
QCheckBox* m_showDemoCheck{nullptr}; // RFC #4288: offer the demo entry

Expand Down
15 changes: 15 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2477,6 +2477,21 @@ MainWindow::MainWindow(QWidget* parent)
}
});

// A startup auto-connect that gives up must hand the window back. The
// no-saved-radio popup above is suppressed whenever LastConnectedRadioSerial
// is set, so without this a saved radio that cannot be reached leaves the
// "Looking for your radio…" overlay up forever with no offered way into the
// connection dialog — the operator has to know the Settings menu item exists.
connect(m_connPanel, &ConnectionPanel::startupConnectUnavailable,
this, [this](const QString& reason) {
if (m_userDisconnected || m_radioModel.isConnected()) return;
setPanadapterConnectionAnimation(false);
m_connPanel->setStatusText(reason);
// show, not toggle: toggleConnectionDialog() would hide a dialog the
// operator had already opened while the probe was still in flight.
showConnectionDialog();
});

// Probe saved routed radio on startup
{
auto& s = AppSettings::instance();
Expand Down
Loading
Loading