Files
IOT_SIM/server/src/compile-service.ts
T
xuyueandClaude Opus 5.5 98be8331bc 第 0 阶段:教材项目 2 任务 1 在网页中原样运行
- runtime:仿真 WinForms 子集(Form、Button、Label、TextBox、ComboBox、
  GroupBox、Timer、MessageBox、界面线程消息循环、跨线程检查)、
  教材设备类库(DeviceCtrlHelper、LED 控件)、运行器(常驻编译服务 + 运行模式)
- server:Bun + Hono,WebSocket 会话,每次运行一个进程,消息转发
- web:Vue + Naive UI,WinForms 风格窗体渲染、MessageBox、实训台 LED 屏
- 端到端测试与 100 个程序并发的压力测试
- 教材逐任务摘要(docs/教材摘要)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:15:14 +08:00

73 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Subprocess } from 'bun'
import type { Diagnostic } from '@iot/shared'
import { RUNNER_DLL, readLines } from './runner-process'
export interface CompileResult {
ok: boolean
diagnostics: Diagnostic[]
ms: number
}
type Reply = ({ type: 'compiled' } & CompileResult) | { type: 'compileFailed'; message: string }
/**
* 常驻编译服务:全服务器共用一个 C# 编译进程(Roslyn 只加载一次),按请求编号对应回复。
* 进程意外退出时,挂起的请求全部失败,下一次请求自动重启。
*/
class CompileService {
private proc?: Subprocess<'pipe', 'pipe', 'inherit'>
private ready?: Promise<void>
private nextId = 1
private readonly pending = new Map<number, { resolve: (r: CompileResult) => void; reject: (e: Error) => void }>()
/** 编译工程目录;成功时程序集写到同一目录 */
async compile(dir: string): Promise<CompileResult> {
await this.start()
const id = this.nextId++
const result = new Promise<CompileResult>((resolve, reject) => this.pending.set(id, { resolve, reject }))
this.proc!.stdin.write(`${JSON.stringify({ id, dir })}\n`)
this.proc!.stdin.flush()
return result
}
stop() {
this.proc?.kill()
}
/** 服务器启动时调用,提前完成编译器的加载和预热 */
start() {
if (this.ready) return this.ready
const proc = Bun.spawn(['dotnet', RUNNER_DLL, 'compile-server'], {
stdin: 'pipe',
stdout: 'pipe',
stderr: 'inherit',
env: { ...process.env, DOTNET_CLI_TELEMETRY_OPTOUT: '1', DOTNET_NOLOGO: '1' },
})
this.proc = proc
this.ready = new Promise<void>((resolveReady) => {
void readLines(proc.stdout, (line) => {
const message = JSON.parse(line) as ({ type: 'ready' } | Reply) & { id?: number }
if (message.type === 'ready') {
resolveReady()
return
}
const waiter = this.pending.get(message.id!)
if (!waiter) return
this.pending.delete(message.id!)
if (message.type === 'compiled') waiter.resolve(message)
else waiter.reject(new Error(message.message))
})
})
proc.exited.then((code) => {
if (this.proc !== proc) return
this.proc = undefined
this.ready = undefined
for (const waiter of this.pending.values()) waiter.reject(new Error(`编译服务意外退出(${code})`))
this.pending.clear()
})
return this.ready
}
}
export const compileService = new CompileService()