78d04f1seqlab: write and view pulseq MRI sequences in the browserJeremy Magland 1// Dev utility: run one of the src/examples scripts headlessly through the
2// same call shape the browser runner uses, time it, and parse the produced
3// .seq with the JS parser. Usage: node scripts/run-example.mjs <fid|gre|epi>
4import fs from 'node:fs'
5import path from 'node:path'
6import { fileURLToPath } from 'node:url'
7import {
8 executeCode,
9 VirtualFileSystem,
10 BrowserFileIOAdapter,
11 BrowserSystemAdapter,
12} from 'numbl'
13import { parseSeq } from '../src/seq/parseSeq.ts'
14import { reconstruct } from '../src/seq/reconstruct.ts'
16const name = process.argv[2] ?? 'fid'
17const here = path.dirname(fileURLToPath(import.meta.url))
18const repoRoot = path.join(here, '..')
19const mrRoot = path.join(repoRoot, 'src', 'engine', 'pulseq')
21const MD5_OVERRIDE = `function digest = md5(message, noBuiltIn)
22 digest = hash('MD5', char(message));
23end
24`
26const files = []
27for (const entry of fs.readdirSync(path.join(mrRoot, '+mr'), { recursive: true })) {
28 const rel = String(entry)
29 const abs = path.join(mrRoot, '+mr', rel)
30 if (!fs.statSync(abs).isFile() || !rel.endsWith('.m')) continue
31 const vfsPath = `+mr/${rel}`
32 files.push({
33 path: vfsPath,
34 content: vfsPath === '+mr/+aux/md5.m' ? MD5_OVERRIDE : fs.readFileSync(abs, 'utf8'),
35 })
36}
37const script = fs.readFileSync(path.join(repoRoot, 'src', 'examples', `${name}.m`), 'utf8')
38files.push({ path: 'main.m', content: script })
40const enc = new TextEncoder()
41const vfs = new VirtualFileSystem()
42for (const f of files) vfs.writeFile('/project/' + f.path, enc.encode(f.content))
43vfs.setCwd('/project')
44const workspaceFiles = files.map((f) => ({ name: f.path, source: f.content }))
46const t0 = Date.now()
47try {
48 executeCode(
49 'main;',
50 {
51 onOutput: (text) => process.stdout.write(text),
52 displayResults: true,
53 maxIterations: 1e9,
54 optimization: '1',
55 fileIO: new BrowserFileIOAdapter(vfs),
56 system: new BrowserSystemAdapter(vfs),
57 },
58 workspaceFiles,
59 'repl',
60 [],
61 )
62} catch (err) {
63 console.error('run failed:', err.message ?? err)
64 if (err.file) console.error(` at ${err.file}:${err.line}`)
65 process.exit(1)
66}
67console.log(`\nrun finished in ${((Date.now() - t0) / 1000).toFixed(1)} s`)
69const seqName = `${name}.seq`
70const text = new TextDecoder().decode(vfs.readFile('/project/' + seqName))
71const seq = parseSeq(text)
72const rec = reconstruct(seq)
73console.log(
74 `${seqName}: ${text.length} bytes, v${seq.version.major}.${seq.version.minor}.${seq.version.revision}, ` +
75 `${seq.blocks.length} blocks, ${rec.rfSpans.length} RF, ${rec.adcSpans.length} ADC, ` +
76 `duration ${rec.duration.toFixed(4)} s, signature ${seq.signature?.valid ? 'valid' : 'MISSING/INVALID'}`,
77)