diff --git a/openpilot/starpilot/galaxy/tests/test_flm.mjs b/openpilot/starpilot/galaxy/tests/test_flm.mjs index 4e45be00c1..9763c8d8bd 100644 --- a/openpilot/starpilot/galaxy/tests/test_flm.mjs +++ b/openpilot/starpilot/galaxy/tests/test_flm.mjs @@ -58,13 +58,27 @@ assert.doesNotMatch(FlmPage.template, /v-html|Analyze selected|GM Trial|Choose F assert.match(FlmPage.template, /Currently applied/) assert.match(FlmPage.template, /PID unsupported/) const app = readFileSync(new URL("../web/js/app.js", import.meta.url), "utf8") -assert(app.includes("['/tuning', '/tuning/plots'].includes(route.path)")) +assert(app.includes("['/tuning', '/tuning/plots', '/tuning/flm'].includes(route.path)")) +const analysis = app.match(/
/)?.[0] +assert(analysis, 'Existing analysis route and old bookmarks share one page') +assert.match(analysis, / { throw error }, decodeEntities: value => value.replaceAll("&", "&") }) assert.doesNotMatch(app, /Recorded-drive analysis/) const settings = readFileSync(new URL("../web/js/settings.js", import.meta.url), "utf8") assert.doesNotMatch(settings, /FLMTrainingResults|View FLM training results/) const catalog = JSON.parse(readFileSync(new URL("../web/data/catalog.json", import.meta.url), "utf8")) assert(catalog.tools.some(tool => tool.path === "/tuning/plots")) -assert(catalog.tools.some(tool => tool.path === "/tuning/flm")) +assert(!catalog.tools.some(tool => tool.path === "/tuning/flm")) +assert.equal(catalog.tools.filter(tool => tool.path.startsWith('/tuning')).length, 1) +const plots = readFileSync(new URL('../web/js/plots.js', import.meta.url), 'utf8') +assert.doesNotMatch(plots, /href="#\/tuning\/flm"/) +const driving = readFileSync(new URL('../web/js/driving.js', import.meta.url), 'utf8') +assert.match(driving, /go\('\/tuning\/plots'\)/) +assert.doesNotMatch(driving, /\/tuning\/flm/) +assert.match(FlmPage.template, /
]*>

Live steering response/) +assert.doesNotMatch(FlmPage.template, /href="#\/tuning\/plots"/) const route = readFileSync(new URL("../web/js/route-workspace.js", import.meta.url), "utf8") assert.doesNotMatch(route, /Analyze driving logs/) @@ -77,7 +91,17 @@ function instance() { } const originalFetch = globalThis.fetch, originalDocument = globalThis.document try { - globalThis.document = { hidden: false, removeEventListener() {} } + globalThis.document = { hidden: false, addEventListener() {}, removeEventListener() {} } + for (const embedded of [true, false]) { + const page = { ...FlmPage.data(), embedded, mode: 'local', unauthorized() {}, load() {} } + let plotStarts = 0 + FlmPage.created.call(page) + page.feed.start = () => { plotStarts++ } + page.feed.stop = () => {} + FlmPage.mounted.call(page) + assert.equal(plotStarts, embedded ? 0 : 1, 'Embedded results reuse the existing live plots') + FlmPage.beforeUnmount.call(page) + } const calls = [] globalThis.fetch = async (url, options) => { calls.push({ url, options }); return { ok: true, json: async () => value } } const vm = instance(); await vm.load() diff --git a/openpilot/starpilot/galaxy/tests/test_preview_contract.py b/openpilot/starpilot/galaxy/tests/test_preview_contract.py index 30517b5bc0..31983a0fc4 100644 --- a/openpilot/starpilot/galaxy/tests/test_preview_contract.py +++ b/openpilot/starpilot/galaxy/tests/test_preview_contract.py @@ -19,7 +19,7 @@ class PreviewContractTest(unittest.TestCase): self.assertEqual([tool["path"] for tool in catalog["tools"] if tool["availability"] == "partial-preview"], ["/logs"]) self.assertEqual([tool["path"] for tool in catalog["tools"] if tool["availability"] == "local-only"], ["/bluetooth", "/cameras", "/device-preferences", "/galaxy", "/manage_models", "/model_laboratory", - "/navigation", "/system", "/theme_maker", "/tuning/plots", "/tuning/flm", "/vehicle", "/android-auto"]) + "/navigation", "/system", "/theme_maker", "/tuning/plots", "/vehicle", "/android-auto"]) self.assertTrue(all(tool["availability"] in {"unavailable", "partial-preview", "local-only"} for tool in catalog["tools"])) self.assertTrue(all(tool["name"] and tool["description"] and tool["icon"] for tool in catalog["tools"])) diff --git a/openpilot/starpilot/galaxy/web/data/catalog.json b/openpilot/starpilot/galaxy/web/data/catalog.json index bf6e30d767..0b139e6430 100644 --- a/openpilot/starpilot/galaxy/web/data/catalog.json +++ b/openpilot/starpilot/galaxy/web/data/catalog.json @@ -75,14 +75,7 @@ "name": "Plots & Analysis", "path": "/tuning/plots", "icon": "bi-sign-turn-right", - "description": "Live steering and acceleration plots", - "availability": "local-only" - }, - { - "name": "Auto Learn Results", - "path": "/tuning/flm", - "icon": "bi-sliders", - "description": "StarPilot learned steering corrections and fitting progress", + "description": "Live control plots and StarPilot Auto Learn results", "availability": "local-only" }, { @@ -96,7 +89,7 @@ "name": "Android Auto", "path": "/android-auto", "icon": "bi-phone", - "description": "Show StarPilot on your car’s screen", + "description": "Show StarPilot on your car\u2019s screen", "availability": "local-only", "visibility": "authenticated" } diff --git a/openpilot/starpilot/galaxy/web/js/app.js b/openpilot/starpilot/galaxy/web/js/app.js index 0698e53abf..3b53628a83 100644 --- a/openpilot/starpilot/galaxy/web/js/app.js +++ b/openpilot/starpilot/galaxy/web/js/app.js @@ -250,8 +250,10 @@ createApp({ - - +
+ + +
This capability is unavailable in this build. No operation was attempted.

diff --git a/openpilot/starpilot/galaxy/web/js/driving.js b/openpilot/starpilot/galaxy/web/js/driving.js index 950225d25b..74802abadb 100644 --- a/openpilot/starpilot/galaxy/web/js/driving.js +++ b/openpilot/starpilot/galaxy/web/js/driving.js @@ -51,7 +51,7 @@ export const DrivingPage = {
-
+ `, } diff --git a/openpilot/starpilot/galaxy/web/js/flm.js b/openpilot/starpilot/galaxy/web/js/flm.js index 9c5b79536c..4c5a9f1b5f 100644 --- a/openpilot/starpilot/galaxy/web/js/flm.js +++ b/openpilot/starpilot/galaxy/web/js/flm.js @@ -8,10 +8,11 @@ import { validTrainingResults, trainingSummary, readable, detailText, surfaceRow export const FlmPage = { name: "FlmPage", components: { GxState, GxNotice, PlotGraph }, - props: { mode: { type: String, required: true }, unauthorized: { type: Function, required: true } }, + props: { mode: { type: String, required: true }, unauthorized: { type: Function, required: true }, embedded: { type: Boolean, default: false } }, data: () => ({ report: null, error: "", loading: false, request: null, receivedAt: 0, now: 0, status: "idle", data: null, history: [], plotError: "", showPlots: true }), created() { + if (this.embedded) this.showPlots = false this.feed = new PlotsFeed({ publish: update => { const { error, ...values } = update Object.assign(this.$data, values, { plotError: error }) @@ -26,7 +27,7 @@ export const FlmPage = { else { this.load(); if (this.showPlots && this.mode === "local") this.feed.start() } } document.addEventListener("visibilitychange", this.visibility) - if (this.mode === "local") { this.poller.start(); this.load(); if (!document.hidden) this.feed.start() } + if (this.mode === "local") { this.poller.start(); this.load(); if (this.showPlots && !document.hidden) this.feed.start() } }, beforeUnmount() { this.activePage = false; clearTimeout(this.freshnessTimer); this.poller?.stop(); this.request?.abort(); this.request = null; this.feed.stop() @@ -79,9 +80,9 @@ export const FlmPage = { }, }, template: `
-

StarPilot Auto Learn

+

StarPilot Auto Learn

StarPilot Auto Learn

Learned steering corrections, collection progress and current application.

- Live plots
+
Auto Learn results require authenticated local Galaxy access.