Preserve manual tuning descriptions while parked

This commit is contained in:
firestar5683
2026-11-09 16:05:56 +00:00
parent e0f8b7a118
commit e2a3802722
3 changed files with 50 additions and 2 deletions
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict'
import { compile } from '../web/vendor/vue/vue.esm-browser.js'
import { FlmPage } from '../web/js/flm.js'
import { GalaxySettingRow } from '../web/js/galaxy-setting-row.js'
import { SURFACE_NAMES, PID_UNSUPPORTED, validTrainingResults, validTrackingResults, trackingView, surfaceRows, measurementText, validTrackingValues, trackingCoordinates, validTrackingPlot, trackingPlotView, sourceSeconds, tunerStatus } from '../web/js/flm-training-results.js'
const coordinates = amount => SURFACE_NAMES.map((name, i) => [name, i === 0 ? amount : 0])
@@ -292,3 +293,50 @@ ctx.now=3000
assert.equal(ctx.reportFresh,false)
assert.notEqual(ctx.state.title,'Tune saved for the next drive','Expired report cannot assert the current saved tune')
console.log('Actual parked Vue render exposes all five route candidates, distinguishes saving/application and expires stale status')
// Declared UI states only: never enable driving edits or contact a device.
// Render BOTH the parent and the actual setting-row template so a parent prop
// override cannot silently remove help while the slider remains editable.
const settingRender = compile(GalaxySettingRow.template, { onError: e => { throw e }, decodeEntities: v => v.replaceAll('&', '&') })
const vnodeRows = vnode => Array.isArray(vnode) ? vnode.flatMap(vnodeRows)
: vnode && typeof vnode === 'object' ? [vnode, ...vnodeRows(vnode.children)] : []
const manualContext = { ...FlmPage.data(), mode: 'local', view: 'tune', group: 'balance', unauthorized() {} }
for (const [name, fn] of Object.entries(FlmPage.methods)) manualContext[name] = fn.bind(manualContext)
for (const [name, get] of Object.entries(FlmPage.computed)) Object.defineProperty(manualContext, name, { get: () => get.call(manualContext) })
const manualHelp = 'Sets the extra planned steering effort in left curves, before tracking-error corrections. Higher adds more effort; lower adds less.'
for (const state of [{ parked: true }, { parked: false }, { parked: true }, { parked: true, error: 'Connection lost' }]) {
const calls = []
manualContext.manualFeed = { active: true, async previewValue(index, value) { calls.push([index, value]) } }
manualContext.manualState = { status: 'ready', pending: null, error: state.error || '', data: { rows: [{
coordinate: 'ff_gain_left', label: 'Steering gain', reason: state.parked ? manualHelp : manualHelp + ' Parked only.',
available: state.parked, action: true, value: 0.5, defaultValue: 0, resetAvailable: state.parked,
minimum: -25, maximum: 25, step: 0.25, unit: '%',
}] } }
const parentRows = vnodeRows(render.call(manualContext, manualContext, [])).filter(node => node.props?.row?.coordinate === 'ff_gain_left')
assert.equal(parentRows.length, 1)
const props = parentRows[0].props
assert.equal(props.row.reason, manualContext.manualState.data.rows[0].reason, 'Available manual sliders must retain their descriptions')
const defaults = Object.fromEntries(Object.entries(GalaxySettingRow.props)
.filter(([, prop]) => Object.hasOwn(prop, 'default')).map(([name, prop]) => [name, prop.default]))
const settingContext = { ...defaults, ...GalaxySettingRow.data(), ...props,
saveValue: props.saveValue || props['save-value'] }
assert.equal(typeof settingContext.saveValue, 'function')
for (const [name, fn] of Object.entries(GalaxySettingRow.methods)) settingContext[name] = fn.bind(settingContext)
for (const [name, get] of Object.entries(GalaxySettingRow.computed)) Object.defineProperty(settingContext, name, { get: () => get.call(settingContext) })
const rowTree = settingRender.call(settingContext, settingContext, [])
assert(text(rowTree).includes(manualHelp), 'Help must render when parked, driving, and after returning to park')
if (!state.parked) assert(text(rowTree).includes('Parked only.'), 'The driving restriction must remain visible')
const input = vnodeRows(rowTree).find(node => node.type === 'input' && node.props?.type === 'range')
const reset = vnodeRows(rowTree).find(node => node.type === 'button' && node.props?.['aria-label']?.startsWith('Reset '))
assert(input && reset)
const locked = !state.parked || !!state.error
assert.equal(!!input.props.disabled, locked, 'Descriptions must not change the driving/error slider lockout')
assert.equal(!!reset.props.disabled, locked, 'Descriptions must not change the reset lockout')
await settingContext.commit(1)
assert.equal(calls.length, locked ? 0 : 1, 'Locked slider events must not reach settings preview/save')
if (locked) {
assert.equal(await manualContext.saveManual(0, 1), false, 'The parent must also reject direct locked saves')
assert.equal(calls.length, 0)
} else assert.deepEqual(calls, [[0, 1]])
}
console.log('Actual FLM and setting-row Vue renders preserve parked/driving descriptions and reject locked saves')
+1 -1
View File
@@ -218,7 +218,7 @@ export const FlmPage = {
<GxNotice v-if="manualState.error" tone="danger">{{ manualState.error }}</GxNotice>
<p v-if="!manualState.data" class="gx-tuner__caption">{{ manualState.status === 'loading' ? 'Loading saved manual tune…' : 'Manual tuning unavailable. Reconnecting…' }}</p>
<p v-else-if="!manualRows.length" class="gx-tuner__caption">{{ manualState.data.rows[0]?.reason || 'Manual tuning is unavailable for this controller.' }}</p>
<template v-for="{ row, index } in manualRows" :key="row.coordinate"><GalaxySettingRow :row="{ ...row, label: parameterLabel(row.coordinate), reason: row.available ? '' : row.reason }" :index="index" :busy="manualBusy" :disabled="!!manualState.error" :save-value="saveManual" @reset-default="index => manualFeed.resetDefault(index)" /><p v-if="manualEvidence(row.coordinate)" class="gx-tuner__caption">{{ manualEvidence(row.coordinate) }} Manual override shown above is separate.</p></template>
<template v-for="{ row, index } in manualRows" :key="row.coordinate"><GalaxySettingRow :row="{ ...row, label: parameterLabel(row.coordinate) }" :index="index" :busy="manualBusy" :disabled="!!manualState.error" :save-value="saveManual" @reset-default="index => manualFeed.resetDefault(index)" /><p v-if="manualEvidence(row.coordinate)" class="gx-tuner__caption">{{ manualEvidence(row.coordinate) }} Manual override shown above is separate.</p></template>
<GalaxySettingRow v-for="{ row, index } in manualActions" :key="index" :row="row" :index="index" :busy="manualBusy" :disabled="!!manualState.error" :save-value="saveManual" @review="reviewManualReset" />
</section>
<h4>Auto Learn evidence</h4><p class="gx-tuner__caption">These reported results are separate from your manual settings.</p>
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "74fc584db109834ff7904a8de3d7abc8a1a2507b845699c3eb97d6ee7f3ec76e", "schema_version": 1}
{"protected_inventory_sha256": "8a273dffca0c7044587efb05fbc9e7f4b305f5ae0282e86edd39d0e93fdd8cf4", "schema_version": 1}