gui-ts: prevent stale run overwrites and expose model mismatch
Guard async solve updates with run tokens and surface FDM/FEA card length mismatch in Results so users understand when overlays are intentionally reduced. Made-with: Cursor
This commit is contained in:
@@ -48,6 +48,7 @@ export function App() {
|
|||||||
const [surfaceCardQaError, setSurfaceCardQaError] = useState<string | null>(null);
|
const [surfaceCardQaError, setSurfaceCardQaError] = useState<string | null>(null);
|
||||||
const [validatingSurfaceCard, setValidatingSurfaceCard] = useState(false);
|
const [validatingSurfaceCard, setValidatingSurfaceCard] = useState(false);
|
||||||
const hydrated = useRef(false);
|
const hydrated = useRef(false);
|
||||||
|
const latestRunToken = useRef(0);
|
||||||
const engineeringChecks = useMemo(() => runEngineeringChecks(store.state), [store.state]);
|
const engineeringChecks = useMemo(() => runEngineeringChecks(store.state), [store.state]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -96,6 +97,8 @@ export function App() {
|
|||||||
}, [parsedSurfaceCard]);
|
}, [parsedSurfaceCard]);
|
||||||
|
|
||||||
const handleRun = useCallback(async () => {
|
const handleRun = useCallback(async () => {
|
||||||
|
const runToken = latestRunToken.current + 1;
|
||||||
|
latestRunToken.current = runToken;
|
||||||
if (engineeringChecks.hasBlockingError) {
|
if (engineeringChecks.hasBlockingError) {
|
||||||
setError("Please fix blocking engineering checks before running the solver.");
|
setError("Please fix blocking engineering checks before running the solver.");
|
||||||
setStatusMessage("Blocked by engineering checks");
|
setStatusMessage("Blocked by engineering checks");
|
||||||
@@ -133,6 +136,9 @@ export function App() {
|
|||||||
fourierHarmonics: runSettings.outputOptions.fourierHarmonics
|
fourierHarmonics: runSettings.outputOptions.fourierHarmonics
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (latestRunToken.current !== runToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setResult(resp);
|
setResult(resp);
|
||||||
const dt = (performance.now() - t0) / 1000;
|
const dt = (performance.now() - t0) / 1000;
|
||||||
setElapsed(dt);
|
setElapsed(dt);
|
||||||
@@ -140,11 +146,16 @@ export function App() {
|
|||||||
setStatusMessage(`Done in ${dt.toFixed(1)}s`);
|
setStatusMessage(`Done in ${dt.toFixed(1)}s`);
|
||||||
setActiveTab("tab-results");
|
setActiveTab("tab-results");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (latestRunToken.current !== runToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setError(e instanceof Error ? e.message : String(e));
|
setError(e instanceof Error ? e.message : String(e));
|
||||||
setStatusMessage("Error");
|
setStatusMessage("Error");
|
||||||
} finally {
|
} finally {
|
||||||
|
if (latestRunToken.current === runToken) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}, [engineeringChecks.hasBlockingError, parsedSurfaceCard, runSettings, store.state]);
|
}, [engineeringChecks.hasBlockingError, parsedSurfaceCard, runSettings, store.state]);
|
||||||
|
|
||||||
const handleExportXml = useCallback(() => {
|
const handleExportXml = useCallback(() => {
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ export function ResultsTab({
|
|||||||
}
|
}
|
||||||
return [fdmSeries.position, fdmSeries.polished, fdmSeries.downhole] as AlignedData;
|
return [fdmSeries.position, fdmSeries.polished, fdmSeries.downhole] as AlignedData;
|
||||||
}, [fdm, fea]);
|
}, [fdm, fea]);
|
||||||
|
const hasCardLengthMismatch = useMemo(() => {
|
||||||
|
const fdmSeries = toSeries(fdm ?? undefined);
|
||||||
|
const feaSeries = toSeries(fea ?? undefined);
|
||||||
|
if (!fdmSeries || !feaSeries) return false;
|
||||||
|
return fdmSeries.position.length !== feaSeries.position.length;
|
||||||
|
}, [fdm, fea]);
|
||||||
|
|
||||||
const dynacardOptions = useMemo<Options>(() => {
|
const dynacardOptions = useMemo<Options>(() => {
|
||||||
const seriesCount = fea ? 5 : 3;
|
const seriesCount = fea ? 5 : 3;
|
||||||
@@ -364,6 +370,11 @@ export function ResultsTab({
|
|||||||
</Fieldset>
|
</Fieldset>
|
||||||
|
|
||||||
<Fieldset legend="Dynamometer Card">
|
<Fieldset legend="Dynamometer Card">
|
||||||
|
{hasCardLengthMismatch && (
|
||||||
|
<div className="callout">
|
||||||
|
FDM and FEA card lengths differ; chart overlay is limited to FDM series for this run.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{dynacardData ? (
|
{dynacardData ? (
|
||||||
<UPlotChart data={dynacardData} options={dynacardOptions} height={340} />
|
<UPlotChart data={dynacardData} options={dynacardOptions} height={340} />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user