脚本执行环境概览

提供统一的脚本运行时:无论在编辑器中测试还是在仿真引擎内执行,都共享完全相同的上下文、打包方式与错误处理流程。 运行时基于 Node.js vm 沙箱与原生 C++ 扩展,可同时加载 JavaScript 与 C++ 两种脚本语言。

  • 同步执行模型:脚本以同步方式运行,顶层可直接 return,无需使用 async/await
  • 自动包装:系统会将脚本包裹在 IIFE 中,统一捕获异常、记录日志并回传执行结果。
  • 统一上下文:脚本可通过注入的 elementIdblackboardmessageapi 等对象读写仿真状态。
  • 一致的错误输出:语法、引用、类型、超时等错误会附带行列信息与堆栈,便于定位。

内置上下文对象

  • elementId / elementType / elementText:当前图元的标识、类型与展示名称。
  • parameters:过程触发时传入的参数字典。
  • simulationTime / sessionId:当前仿真时间与会话标识,可用于记录日志。
  • blackboard:跨元素共享的数据黑板,支持 setgetremovepushdump 等操作。
  • message:仿真消息总线,提供 sendconsumepeeklistclear 等方法。
  • api:语义化 API 调用封装,包含 checkConditionexecuteActiongetSimState 等同步方法。
  • Math / Date / JSON:常用 JS 内建对象,直接可用。

脚本编辑器

打开脚本编辑器

  1. 选中需要编写逻辑的对象或过程。
  2. 在右侧属性面板切换到 “脚本” 页签。
  3. 点击 “编辑脚本” 按钮,进入统一脚本编辑器。

常用功能

  • 语言切换:在编辑器右上角选择 JavaScript 或 C++,并同步保存到项目。
  • 立即测试:点击 “测试脚本”,使用与仿真一致的执行链路进行验证。
  • 脚本导入:支持从本地文件导入脚本,同时保留语法高亮与行号。
  • 冲突保护:若图元已绑定 API 服务,编辑器会提示并阻止直接改写脚本。

JavaScript 脚本指南

执行规则

  • 同步返回:条件脚本必须返回布尔值;过程脚本可根据需要返回任意类型或不返回。
  • 无需 async:所有内置 API 均为同步封装,请勿编写 async/awaitPromise.then 链。
  • 谨慎循环:脚本在主线程执行,避免长时间阻塞;如需多步逻辑,可借助黑板记录状态并拆分流程。
  • 错误处理:推荐使用 try...catch 捕获外部调用异常,并记录日志。

条件脚本示例

const missionReady = blackboard.get('airTask.common.ready', false);
const fuel = blackboard.get('aircraft.fuel', 100);
const checkResult = api.checkCondition('consim.mission.canEngage', {
    unitId: elementId,
    threshold: 40,
    operator: '>='
});

const shouldEngage = missionReady && checkResult.result === true && fuel >= 40;
console.log('交战条件评估:', { missionReady, fuel, shouldEngage });
return shouldEngage;

过程脚本示例

try {
    const targetId = blackboard.get('engagement.currentTarget');
    if (!targetId) {
        message.send('mission.log', { level: 'warn', text: '没有目标,保持巡逻' });
        return;
    }

    const fireResult = api.executeAction('engageTarget', {
        unitId: elementId,
        targetId
    });

    if (fireResult.success) {
        blackboard.set('engagement.lastShotAt', Date.now());
        message.send('mission.log', { level: 'info', targetId, status: 'fire' });
    } else {
        console.warn('执行交战失败:', fireResult.error);
    }
} catch (error) {
    console.error('过程脚本异常:', error.message);
    throw error; // 让引擎记录详细堆栈
}

黑板与消息接口

blackboard.set('patrol.routeId', 'ROUTE-01');
const latest = blackboard.get('sensor.temperature', 24.6);
const pending = blackboard.get('engagement.queue', []);
if (pending.length > 0) {
    message.send('mission.queue', { event: 'task-ready', tasks: pending.length });
    const messageInfo = message.consume('mission.queue');
    console.log('队列广播:', messageInfo);
}
建议:通过黑板共享状态,通过消息总线触发跨节点协作,不再依赖创建/销毁对象模拟流程。

C++ 脚本指南

当逻辑对性能或类型安全有更高要求时,可切换脚本语言为 C++。系统会自动调用原生编译器(默认 clang++/C++17)生成动态库并缓存。

代码结构

#include "cwopm_script_api.h"
#include <any>
#include <map>
#include <string>

extern "C" bool condition_func(cwopm::ScriptContext* ctx) {
    const double latest = ctx->blackboard->get<double>("cpp_demo.temperature", 0.0);
    const double threshold = ctx->blackboard->get<double>("cpp_demo.health.threshold", 28.0);
    const bool isHot = latest >= threshold;

    ctx->blackboard->set("cpp_demo.condition.latest", latest);
    ctx->blackboard->set("cpp_demo.condition.threshold", threshold);
    ctx->blackboard->set("cpp_demo.condition.isHot", isHot);
    return isHot;
}

extern "C" void action_func(cwopm::ScriptContext* ctx) {
    const bool isHot = ctx->blackboard->get<bool>("cpp_demo.condition.isHot", false);

    std::map<std::string, std::any> payload;
    payload["event"] = std::string("condition-evaluated");
    payload["result"] = isHot;
    ctx->message->send("cpp.demo.condition", payload);

    const std::string hotId = ctx->getStateIdByName("cpp-demo-condition-state", "高温预警");
    const std::string safeId = ctx->getStateIdByName("cpp-demo-condition-state", "安全");
    if (!hotId.empty() && !safeId.empty()) {
        ctx->setObjectState("cpp-demo-condition-state", isHot ? hotId : safeId);
    }
}
  • 入口函数:condition_funcaction_func 可任选其一实现,未实现的函数会被忽略。
  • 上下文访问:ScriptContext 暴露 blackboardmessagesetObjectState 等接口,与 JavaScript 版本对齐。
  • 自动编译:脚本保存后由引擎自动编译、装载并缓存,后续修改会触发热重载。
  • 调试建议:编译失败时,编辑器会返回编译器输出和错误位置,请优先处理语法或链接错误。

可用接口速查

blackboard(数据黑板)

  • set(key, value) / get(key, defaultValue?) / remove(key) / clear()
  • push(key, value) / pop(key) / merge(key, object)
  • query(pattern) / keys(pattern) / dump() / stats()

message(消息总线)

  • send(channel, payload, options?) :广播消息并自动附带发送者信息。
  • consume(channel, filter?) / peek(channel):读取或窥视消息。
  • list(pattern) / count(pattern) / clear(pattern) / clearAll()

api(语义化 API)

  • checkCondition(conditionId, params?, options?):同步条件评估,返回包含 result 字段的对象。
  • executeAction(actionType, params?, options?):同步动作执行,可返回 { success, data, error }
  • getSimState(query?, options?):同步获取仿真快照,支持筛选维度。
  • getConfig(apiName) / listEndpoints(apiName) / getHistory(limit) / clearHistory()
  • setObjectState(objectId, stateId) / getObjectState(objectId) / listStates(objectId)
同步调用提醒:底层通过 curl 同步 HTTP 请求实现,请合理设置阈值与超时,避免长时间阻塞主线程。

调试与错误处理

  • 编辑器直接测试:测试按钮走与仿真一致的执行链路,输出中包含标准化 successresultoutputerrorStack 等字段。
  • 日志查看:console.log / warn / error 输出会显示在测试日志与仿真运行面板中,便于复现。
  • 错误定位:语法、引用、类型、超时等错误均携带“第 X 行,第 Y 列”信息,可直接回到对应位置修复。
  • 黑板快照:测试结果中包含脚本结束时的黑板数据快照,适合排查状态未写入或覆盖问题。
常见问题:脚本中仍使用 await 或忘记返回布尔值,会导致条件结果为 None。请按照“脚本编写规范 - 统一机制”要求修正。

最佳实践

明确返回值

  • 条件脚本始终返回 true/false
  • 过程脚本遇到异常时抛出或返回明确状态,避免静默失败。

充分利用黑板

  • 黑板键值采用命名空间前缀(如 airTask.*)避免冲突。
  • 通过黑板拆分长流程,各子过程仅关注单一职责。

审慎调用外部 API

  • checkCondition/executeAction 设置合理阈值与参数,避免重复请求。
  • 记录调用结果,方便在仿真日志中回溯。

复用示例项目

  • 参考项目库中的 “空中任务总控行为模型” 与 “C++ Demo” 获取脚本模板。
  • 提交脚本前,先使用编辑器批量测试,确保所有节点通过。

下一步

完成脚本基础配置后,您可以继续:

  • 仿真运行 中执行脚本,验证行为与日志。
  • 阅读 API 集成,掌握同步 API 调用的更多案例。
  • 结合项目中的 C++ 示例与《脚本编写规范-统一机制.md》完善团队脚本规范。