脚本编写
了解统一脚本执行环境,使用 JavaScript 或 C++ 精准控制仿真逻辑
脚本执行环境概览
智绘 提供统一的脚本运行时:无论在编辑器中测试还是在仿真引擎内执行,都共享完全相同的上下文、打包方式与错误处理流程。 运行时基于 Node.js vm 沙箱与原生 C++ 扩展,可同时加载 JavaScript 与 C++ 两种脚本语言。
- 同步执行模型:脚本以同步方式运行,顶层可直接
return,无需使用async/await。 - 自动包装:系统会将脚本包裹在 IIFE 中,统一捕获异常、记录日志并回传执行结果。
- 统一上下文:脚本可通过注入的
elementId、blackboard、message、api等对象读写仿真状态。 - 一致的错误输出:语法、引用、类型、超时等错误会附带行列信息与堆栈,便于定位。
内置上下文对象
- elementId / elementType / elementText:当前图元的标识、类型与展示名称。
- parameters:过程触发时传入的参数字典。
- simulationTime / sessionId:当前仿真时间与会话标识,可用于记录日志。
- blackboard:跨元素共享的数据黑板,支持
set、get、remove、push、dump等操作。 - message:仿真消息总线,提供
send、consume、peek、list、clear等方法。 - api:语义化 API 调用封装,包含
checkCondition、executeAction、getSimState等同步方法。 - Math / Date / JSON:常用 JS 内建对象,直接可用。
脚本编辑器
打开脚本编辑器
- 选中需要编写逻辑的对象或过程。
- 在右侧属性面板切换到 “脚本” 页签。
- 点击 “编辑脚本” 按钮,进入统一脚本编辑器。
常用功能
- 语言切换:在编辑器右上角选择 JavaScript 或 C++,并同步保存到项目。
- 立即测试:点击 “测试脚本”,使用与仿真一致的执行链路进行验证。
- 脚本导入:支持从本地文件导入脚本,同时保留语法高亮与行号。
- 冲突保护:若图元已绑定 API 服务,编辑器会提示并阻止直接改写脚本。
JavaScript 脚本指南
执行规则
- 同步返回:条件脚本必须返回布尔值;过程脚本可根据需要返回任意类型或不返回。
- 无需 async:所有内置 API 均为同步封装,请勿编写
async/await或Promise.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_func与action_func可任选其一实现,未实现的函数会被忽略。 - 上下文访问:
ScriptContext暴露blackboard、message、setObjectState等接口,与 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 请求实现,请合理设置阈值与超时,避免长时间阻塞主线程。
调试与错误处理
- 编辑器直接测试:测试按钮走与仿真一致的执行链路,输出中包含标准化
success、result、output、errorStack等字段。 - 日志查看:
console.log / warn / error输出会显示在测试日志与仿真运行面板中,便于复现。 - 错误定位:语法、引用、类型、超时等错误均携带“第 X 行,第 Y 列”信息,可直接回到对应位置修复。
- 黑板快照:测试结果中包含脚本结束时的黑板数据快照,适合排查状态未写入或覆盖问题。
常见问题:脚本中仍使用
await 或忘记返回布尔值,会导致条件结果为 None。请按照“脚本编写规范 - 统一机制”要求修正。
最佳实践
明确返回值
- 条件脚本始终返回
true/false。 - 过程脚本遇到异常时抛出或返回明确状态,避免静默失败。
充分利用黑板
- 黑板键值采用命名空间前缀(如
airTask.*)避免冲突。 - 通过黑板拆分长流程,各子过程仅关注单一职责。
审慎调用外部 API
- 为
checkCondition/executeAction设置合理阈值与参数,避免重复请求。 - 记录调用结果,方便在仿真日志中回溯。
复用示例项目
- 参考项目库中的 “空中任务总控行为模型” 与 “C++ Demo” 获取脚本模板。
- 提交脚本前,先使用编辑器批量测试,确保所有节点通过。
下一步
完成脚本基础配置后,您可以继续: