Repository navigation
P0 consolidate: firmware-truth PTP lifecycle, firm SV START and native clipboard tests #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: fix/p0-ptp-copy-diagnostics-verified-start-20261021
Are you sure you want to change the base?
Changes from all commits
b4c4327
870ea88
efc90b2
b70074f
9169f89
cbd8aba
bd922da
c987bd5
cc1e6c0
af25366
0300fba
927da08
4871e8d
e34206c
6c97195
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||
| // Native GUI clipboard contract: exercise the SAME production adapter as Studio. | ||
| #include "StudioClipboard.hpp" | ||
|
|
||
| #include <QClipboard> | ||
| #include <QGuiApplication> | ||
| #include <QString> | ||
|
|
||
| int main(int argc, char** argv) { | ||
| QGuiApplication app(argc, argv); | ||
| StudioClipboard clipboard; | ||
| auto* osClipboard = QGuiApplication::clipboard(); | ||
| if (osClipboard == nullptr) return 2; | ||
|
|
||
| if (clipboard.copyText(QString{})) return 3; // no fabricated success | ||
| constexpr auto kHeader = "ARStack Studio field diagnostics"; | ||
| const QString evidence = QString::fromUtf8( | ||
| "ARStack Studio field diagnostics\n" | ||
| "Installed firmware: df73fea0b68e7f47\n" | ||
| "PTP START error: PTPROLE rejected: stop PTP before changing operating role\n" | ||
| "SVSYNC event: ETHERNET_LINK_LOSS\n"); | ||
| if (!evidence.startsWith(QString::fromLatin1(kHeader))) return 4; | ||
| if (!clipboard.copyText(evidence)) return 5; | ||
| if (osClipboard->text(QClipboard::Clipboard) != evidence) return 6; | ||
|
|
||
| const QString second = QString::fromUtf8( | ||
| "PTP diagnostic: SOURCE running\n" | ||
| "MAC phase ±2 us\n" | ||
| "SVSYNC ACQUIRED\n"); | ||
| if (!clipboard.copyText(second)) return 7; | ||
| if (osClipboard->text(QClipboard::Clipboard) != second) return 8; | ||
| if (osClipboard->text(QClipboard::Clipboard) == evidence) return 9; | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -814,6 +814,7 @@ bool DeviceController::startWithScenario(const QVariantMap& scenario) { | |
| scenarioProfileGeneration_ = profileGeneration_; | ||
| if (!sendCommandBatch(commands, false, true)) { resetScenarioStart(); return false; } | ||
| scenarioTimer_.start(); | ||
| emit scenarioStateChanged(); | ||
| signalFrequencyHz_ = hz; | ||
| return true; | ||
| } | ||
|
|
@@ -1436,9 +1437,22 @@ bool DeviceController::startPtpConfigured(const QString& requestedRole, | |
| void DeviceController::advancePtpStartup(const QString& line) { | ||
| if (!ptpStarting() || ptpStartStage_ == PtpStartStage::stop_unconfirmed) return; | ||
| if (ptpStartStage_ == PtpStartStage::awaiting_preflight) { | ||
| // PTP SHOW provides measured PTP2 role before status. Only adopt an | ||
| // already-active transmitter if THIS preflight saw the expected role; | ||
| // historical UI role cannot authorize mutation or claim an exact role. | ||
| const auto roleProof = kPtpMeasuredStateExpression.match(line); | ||
| if (roleProof.hasMatch()) | ||
| ptpRuntimeRoleSeen_ = roleProof.captured(1).toUpper() == pendingPtpRole_; | ||
| const auto actual = kPtpStatusExpression.match(line); | ||
| if (!actual.hasMatch()) return; | ||
| if (actual.captured(1).compare(QStringLiteral("RUNNING"), Qt::CaseInsensitive) == 0) { | ||
| if (ptpRuntimeRoleSeen_) { | ||
| const QString existingRole = pendingPtpRole_; | ||
| resetPtpStartup(); | ||
| ptpStatus_ = QStringLiteral("PTP %1 already active on board; external sync unqualified").arg(existingRole); | ||
| emit ptpStateChanged(); | ||
| return; // No redundant role/config/start, no surprise STOP. | ||
|
Comment on lines
+1449
to
+1454
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When reconnecting to an already-running board whose role matches the requested role but whose domain, transportSpecific, VLAN, or cadence differs, this branch resets the startup transaction and reports the runtime as adopted without ever comparing AGENTS.md reference: AGENTS.md:L11-L18 Useful? React with 👍 / 👎. |
||
| } | ||
| ptpLastStartFailure_ = QStringLiteral( | ||
| "Board PTP is already RUNNING. Click Stop PTP, wait for verified STOPPED, then retry Start."); | ||
| ptpStatus_ = ptpLastStartFailure_; | ||
|
|
@@ -1496,6 +1510,19 @@ void DeviceController::advancePtpStartup(const QString& line) { | |
| line.contains(QStringLiteral("PTP start rejected"), Qt::CaseInsensitive) || | ||
| line.contains(QStringLiteral("PTP unavailable"), Qt::CaseInsensitive); | ||
| if (rejected) { | ||
| if (line.contains(QStringLiteral("PTPROLE rejected: stop PTP before changing operating role"), | ||
| Qt::CaseInsensitive)) { | ||
| // A physical role rejection is positive evidence the timing runtime | ||
| // is active. Do not reset it to OFF, and never auto-stop a source | ||
| // that the current Studio transaction has not proven it owns. | ||
| ptpLastStartFailure_ = QStringLiteral("Board PTP is still RUNNING: ") + line; | ||
| resetPtpStartup(); | ||
| ptpRunning_ = true; | ||
| ptpStatus_ = QStringLiteral("PTP active on board; verify STOP before changing role"); | ||
| emit ptpStateChanged(); | ||
| setError(ptpLastStartFailure_); | ||
| return; | ||
| } | ||
| const bool rollback = ptpStartStage_ == PtpStartStage::awaiting_start || | ||
| ptpStartStage_ == PtpStartStage::awaiting_runtime; | ||
| const QString step = ptpStartPhase(); | ||
|
|
@@ -1693,7 +1720,10 @@ void DeviceController::resetScenarioStart() noexcept { | |
| scenarioSignalGeneration_ = 0; | ||
| scenarioBoot_.clear(); | ||
| scenarioProfileGeneration_.clear(); | ||
| if (wasPending) emit scenarioApplicationFinished(); | ||
| if (wasPending) { | ||
| emit scenarioStateChanged(); | ||
| emit scenarioApplicationFinished(); | ||
| } | ||
| } | ||
|
|
||
| void DeviceController::failScenarioStart(const QString& message) { | ||
|
|
@@ -1864,8 +1894,11 @@ void DeviceController::processLine(const QString& rawLine) { | |
| failScenarioStart(QStringLiteral("Unexpected SV START ACK identity/hash.")); | ||
| return; | ||
| } | ||
| resetScenarioStart(); | ||
| // Publish verified RUNNING before clearing the pending START phase. | ||
| // Otherwise QML can briefly see neither RUNNING nor STARTING and | ||
| // bounce the button back to START during a successful ACK handoff. | ||
| setRunning(true); | ||
| resetScenarioStart(); | ||
| return; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
pendingApplyis already false after a previous successful run, a subsequent CFG commit/readback/START rejection or timeout now only clearsscenarioApplying; because the broad error handler was removed and this connection only handles successful RUNNING evidence,pendingApplyremains false. After such a failure the status strip incorrectly says the setpoints are applied even when the transaction failed before commit, so the authoritative transaction failure path needs to mark the draft pending without reacting to unrelated device errors.Useful? React with 👍 / 👎.