VTeamWork 进程管理模块架构说明

Combined 子进程 | IPC 协议 | 事件通道 | 生命周期 | 错误处理 | Electron 集成

想系统学习 Node 的“进程/线程/事件循环/Worker/Cluster/Child Process”等知识? 点此进入《Node 进程与线程实战手册》

一、总体概览

  • 将引擎 DLL(UserExtend.dll)与分析 DLL(TeamWorkComputer.dll)合并到同一 OS 子进程(Combined)。
  • 主进程通过 CombinedManager 以 IPC 协议向子进程投递任务,串行处理,稳定可靠。
  • 服务层 VFService 维护“实例池”,按项目 key 管理默认引擎、句柄与占用状态。
  • 事件分频道:closedEngine_${'{key}'}workerCrashed_${'{key}'};分析回调走 uid 频道。
  • 不做自愈重启,由前端提示用户是否重启;更符合可控与可观测性。

二、核心组件关系(示意图)

主服务(Node) VFService 实例池:key → { defaultEngine, engineHandles, occupied } CombinedManager fork + IPC:send(taskName, parameter) 事件:closedEngine / callback / crashed SocketIO closedEngine_${'{key}'} / workerCrashed_${'{key}'} 分析回调:emit(uid, payload) Combined 子进程 flightsim.node + analysis.node 同进程共享句柄,避免跨进程失效 任务处理(串行队列) init → task → shutdown openFlightSimEngine / closeFlightSimEngine / loadPrj ... startProjectMonteCarloSim / MinDuFenXi / Optimization ...

蓝色箭头:IPC 请求/响应。左侧为主服务(VFService + CombinedManager + SocketIO),右侧为合并后的子进程。

三、IPC 协议

{ id: string, type: 'init' | 'task' | 'shutdown', payload?: any }

// init.payload: { enginePath, flightsimDllPath, analysisDllPath }
// task.payload: { taskName, parameter }
// events(无 id): { event: 'closedEngine', closedPID } | { event: 'callback', uid, data }
        

子进程严格串行处理,确保原生库非线程安全部分不被并发访问。

四、事件通道

  • 引擎关闭 closedEngine_${'{key}'}:负载 { closedPID }
  • 进程崩溃 workerCrashed_${'{key}'}:负载 { key, worker, ts }(可扩展 code/signal)。
  • 分析回调 uid 频道:负载 payload(字符串将尝试 JSON 解析)。

五、生命周期(时序)

  1. 服务器启动:initVFService(enginePath),按需预热常驻实例。
  2. 项目进入:acquireInstance(key),复用或创建实例并启动默认引擎。
  3. 使用期:
    • 引擎类:openFlightSimEngine / loadPrj / savePrj / runModel ...
    • 分析类:startProjectMonteCarloSim / startProjectMinDuFenXi / startProjectOptimization ...
  4. 引擎退出:触发 closedEngine_${'{key}'},VFService 回收占用并维护实例状态(不自启)。
  5. 子进程异常:workerCrashed_${'{key}'},不自愈,交由用户决定是否重建。
  6. 服务停止:onStop() 优雅关闭所有子进程。

六、错误处理与边界

  • Worker not initialized:通常表示子进程崩溃或尚未 init。
  • 跨进程句柄:已通过“同进程合并”规避(引擎/分析共享句柄)。
  • Socket 递归:严禁直接 emit Sequelize 实例,先 toJSON()
  • 不自愈重启:避免死循环与资源抖动,交给用户决策。

七、目录与关键文件

  • src/process/combinedManager.fork.ts:子进程管理器(fork+IPC)。
  • src/process/combined.runtime.fork.ts:合并运行时,队列串行调度。
  • src/service/vfnode.service.ts:实例池、事件广播与高阶接口。

八、接口速查

// 引擎类(部分)
openFlightSimEngine({ enginePath, showEngine }) → engineId
closeFlightSimEngine({ engineId }) → boolean
loadPrj({ engineId, prjPath }) → boolean
runModel({ engineId, modelId }) → any

// 分析类(部分)
startProjectMonteCarloSim({ engineIds, engineNames, parameter, uid })
startProjectMinDuFenXi({ ... }) / startProjectOptimization({ ... })

// VFService 常用
acquireInstance(key, ownerId?) → rec
openFlightSimEngine(key, show?) → engineId
closeFlightSimEngine({ key, engineId }) → ok
getDefaultEngineHandle({ key }) → engineId | null
        

九、Electron 集成建议

  • 使用 child_process.forkutilityProcess 拉起 Combined 子进程,严禁在主/渲染进程内加载原生 DLL。
  • .node.dll 放至 unpack/extraResources,运行时以绝对路径 setDllPath
  • 崩溃仅通知,不自启,提示用户是否重建。

十、最小示例

// 1) 直接使用 CombinedManager(Node 环境)
import { CombinedManager } from './dist/process/combinedManager.fork.js';
const mgr = new CombinedManager('C:/Path/To/VFlight.exe');
const engineId = await mgr.send('openFlightSimEngine', { enginePath: 'C:/Path/To/VFlight.exe', showEngine: false });
await mgr.send('loadPrj', { engineId, prjPath: 'C:/Path/To/solution.prjt' });

// 2) VFService(应用内)
await vfService.initVFService({ enginePath: 'C:/Path/To/VFlight.exe' });
await vfService.acquireInstance(123);
const eid = await vfService.openFlightSimEngine(123, false);
await vfService.loadPrj({ key: 123, engineId: eid, prjtPath: '.../solution.prjt' });
        

提示:示例路径需替换为部署环境实际路径。

十一、常见问答

  • Q:为何不自愈重启?
    A:避免崩溃-重启循环放大问题,由用户判断是否重启,过程透明可控。
  • Q:跨进程句柄为何会失效?
    A:DLL 内部结构通常保存在进程地址空间,只能在同进程内有效,已通过合并子进程规避。
  • Q:如何定位子进程加载入口?
    A:CombinedManager.getChildFile() 会按多候选路径探测,适配不同构建布局。

© 本页用于进程管理模块学习与上手速览。建议配合源码 src/processsrc/service/vfnode.service.ts 阅读。