File size: 3,490 Bytes
b7e7f16 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | // Thin adapter around the shared `SF.createSolver()` controller.
export function createSolverController({
sf,
backend,
statusBar,
onPlan,
onAnalysis,
onMeta,
onLifecycle,
onError,
}) {
const solver = sf.createSolver({
backend,
statusBar,
onProgress(meta) {
publishMeta(meta);
},
onSolution(snapshot, meta) {
publishSnapshot(snapshot, meta);
},
onPaused(snapshot, meta) {
publishSnapshot(snapshot, meta);
},
onResumed(meta) {
publishMeta(meta);
},
onCancelled(snapshot, meta) {
publishSnapshot(snapshot, meta);
},
onComplete(snapshot, meta) {
publishSnapshot(snapshot, meta);
},
onFailure(_message, meta, snapshot, analysis) {
publishSnapshot(snapshot, meta);
if (analysis) onAnalysis(analysis);
},
onAnalysis(analysis) {
onAnalysis(analysis);
publishLifecycle();
},
onError(message) {
if (typeof onError === 'function') onError(message);
publishLifecycle();
},
});
return {
// Starts a new solve after cleaning up any previous terminal retained job.
async start(planProvider) {
if (solver.isRunning() || solver.getLifecycleState() === 'PAUSED') return;
await cleanupTerminalJob();
const plan = await planProvider();
await solver.start(plan);
publishMeta({
id: solver.getJobId(),
jobId: solver.getJobId(),
lifecycleState: solver.getLifecycleState(),
currentScore: null,
bestScore: null,
telemetry: null,
});
},
pause() {
return solver.pause().then(() => {
publishLifecycle();
});
},
resume() {
return solver.resume().then(() => {
publishLifecycle();
});
},
cancel() {
return solver.cancel().then(() => {
publishLifecycle();
});
},
analyzeSnapshot() {
return solver.analyzeSnapshot().then((analysis) => {
onAnalysis(analysis);
publishLifecycle();
return analysis;
});
},
getLifecycleState() {
return solver.getLifecycleState();
},
getJobId() {
return solver.getJobId();
},
getSnapshotRevision() {
return solver.getSnapshotRevision();
},
};
// Publishes the latest solution and lifecycle metadata back to the app shell.
function publishSnapshot(snapshot, meta) {
if (snapshot && snapshot.solution) onPlan(snapshot.solution);
publishMeta(meta);
}
// Publishes status metadata and refreshes the shell lifecycle markers.
function publishMeta(meta) {
onMeta(meta || null);
publishLifecycle();
}
// Keeps HTML data attributes in sync with the underlying solver lifecycle.
function publishLifecycle() {
onLifecycle({
jobId: solver.getJobId(),
snapshotRevision: solver.getSnapshotRevision(),
lifecycleState: solver.getLifecycleState(),
});
}
// Deletes old terminal jobs so "Solve" always starts from a clean retained slot.
function cleanupTerminalJob() {
const state = solver.getLifecycleState();
if (!solver.getJobId() || state === 'IDLE' || state === 'PAUSED' || solver.isRunning()) {
return Promise.resolve();
}
return solver.delete()
.then(() => {
onAnalysis(null);
onMeta(null);
publishLifecycle();
})
.catch((error) => {
if (typeof onError === 'function') onError(error);
throw error;
});
}
}
|