Files
xuyueandClaude Opus 5.5 5e5d29b2aa
CI/CD / test (push) Canceled after 0s
CI/CD / deploy (push) Canceled after 0s
导出 EXE:实验页工具栏"导出 EXE"把学生的程序编译成一个 exe,拷到机房电脑上双击就能控制真的实训台。编译服务照教材工程编译学生写的原始代码(.NET Framework 4.5 引用程序集,构建时只复制教材工程引用的 13 个到 netfx/;真机版设备类库),不做平台的仿真改写和 API 限制,和 VS 里一样。教材 dll(DeviceCtrlLibrary 及 API 下 7 个、WinFormsDeviceControlLibrary、媒体播放器的两个 Interop)作为资源嵌进 exe,模块初始化器注册 AssemblyResolve 从 exe 里加载,Program.cs 不用改;Properties.Resources 的图片、声音嵌进 exe,代码里按文件名用到的素材(项目 7 的 ring.mp3)启动时放到 exe 旁边的 Resources;媒体播放器的 OcxState 用教材 resx 的默认状态;书上的 ledHelper.Close() 在教材类库里没有,补一个空的扩展方法。23 个实验都能导出(约 0.9 MB),mono + Xvfb 里实测能启动、控件和图片正常。接口 POST /api/export-exe,代码有错时 422,实验页弹框列出报错、点击跳到代码。教材 dll 有版权、GitHub 仓库公开,不进仓库:本机放 textbook-libs/,服务器放 ~/IOT_SIM/textbook-libs/ 由 compose 只读挂载(deploy/README.md),没放时提示原因。使用说明、设计方案、README 同步
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:10:07 +08:00

53 lines
3.1 KiB
TypeScript
Raw Permalink 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 { resolve } from 'node:path'
const env = process.env
/** 项目根目录。部署时服务端打包成单个文件,位置变了,用 APP_ROOT 指定 */
export const ROOT = env.APP_ROOT ?? resolve(import.meta.dir, '../..')
export const config = {
port: Number(env.PORT ?? 3000),
/** 运行时程序(编译服务和运行进程共用),部署时用 Release 版 */
runnerDll: resolve(ROOT, env.RUNNER_DLL ?? 'runtime/Sim.Runner/bin/Debug/net10.0/Sim.Runner.dll'),
/** 每次运行的临时工程目录放在这里 */
runsDir: resolve(ROOT, env.RUNS_DIR ?? '.runs'),
samplesDir: resolve(ROOT, env.SAMPLES_DIR ?? 'samples'),
/**
* 导出 EXE 用的教材设备类库(真机版 DeviceCtrlLibrary.dll 及其 API 目录下的 dll、WinFormsDeviceControlLibrary.dll、
* 媒体播放器的 AxInterop.WMPLib.dll / Interop.WMPLib.dll,放在同一个目录里)。有版权,不进仓库;没有时不能导出
*/
textbookLibsDir: resolve(ROOT, env.SIM_TEXTBOOK_LIBS ?? 'textbook-libs'),
/** 打包好的网页(web/dist);目录不存在时不提供网页,开发时由 Vite 提供 */
staticDir: resolve(ROOT, env.STATIC_DIR ?? 'web/dist'),
/** 学生程序的沙箱:bwrap(bubblewrap)或 none;默认装了 bwrap 就用 */
sandbox: (env.SIM_SANDBOX ?? (Bun.which('bwrap') ? 'bwrap' : 'none')) as 'bwrap' | 'none',
/** 学生程序托管堆上限(MB) */
heapLimitMb: Number(env.SIM_HEAP_LIMIT_MB ?? 128),
/**
* 一次运行最多用多少秒 CPU。正常的界面程序大部分时间在等事件,一小时也用不了几秒;
* 写出死循环的程序会一直占满一个核(服务器只有 2 核),到时间就被结束
*/
cpuLimitSeconds: Number(env.SIM_CPU_LIMIT_SECONDS ?? 120),
/** 全站同时运行的学生程序最多多少个(两个班约 100 人) */
maxRuns: Number(env.SIM_MAX_RUNS ?? 150),
/**
* 全站同时跑多少个评分检查项(提交评分和自测共用)。检查大部分时间在等学生程序的定时器,并发太低时全班排队:
* 2 核实测 45 人同时自测项目 8 任务 3,6 个时最后一人等 4 分钟(CPU 才 28%),16 个时 1.5 分钟(CPU 58%);
* 24 个时 CPU 接近 80%,定时器来不及触发,参考答案被误判不通过(test/classroom.ts)
*/
gradeConcurrency: Number(env.SIM_GRADE_CONCURRENCY ?? 16),
/** 一次运行最长多少分钟,到时自动结束 */
maxRunMinutes: Number(env.SIM_MAX_RUN_MINUTES ?? 60),
/** SQLite 数据库所在目录(账号、班级、学生作品);部署时放在数据卷里 */
dataDir: resolve(ROOT, env.DATA_DIR ?? 'data'),
/** 老师账号(只有一个)。开发时不设置密码就用 admin / admin */
teacherUsername: env.TEACHER_USERNAME ?? 'admin',
teacherPassword: env.TEACHER_PASSWORD ?? '',
timeZone: env.TZ ?? 'Asia/Shanghai',
/** AI 评阅(DeepSeek,OpenAI 格式接口);不设置密钥就不做 AI 评阅 */
aiApiKey: env.DEEPSEEK_API_KEY ?? '',
aiBaseUrl: env.AI_BASE_URL ?? 'https://api.deepseek.com',
aiModel: env.AI_MODEL ?? 'deepseek-flash',
}