using System;
using System.Collections.Generic;
using System.Threading;
using TeamAAS.FlowEditor.Models;
using TeamAAS.FlowEditor.Plugins;
using Plugins.Script.Models;
using Plugins.Script.Views;
using System.Diagnostics;
using Plugins.Script.Core;
namespace Plugins.Script
{
///
/// C# 脚本工具节点(稳定宿主):双击弹出 CodeForge 完整代码编辑器(自由写命名空间/多个类、智能补全、
/// 编译诊断波浪线、一键编译运行、添加 DLL 引用)。用户代码是一个完整插件类
/// (约定:public class MyScript : BasePlugin<ScriptToolModel>,PluginRun/DeclareOutputs 已预写好,想咋改咋改)。
///
/// 运行时本宿主把 Model/Context/Registry 注入编译出的脚本实例并委托执行——脚本因此拥有普通插件的全部能力,
/// 而节点自身是稳定类型(可写入 .aas、可被 PluginLoader 发现),规避了动态类型无法持久化的问题
/// (与 VisionMaster 的 C#脚本 插件同一思路:稳定节点 + 编译用户脚本类 + 反射委托)。
///
[Serializable]
[Plugin("C#脚本", PluginCategory.逻辑判断, typeof(ScriptToolModel), typeof(ScriptToolView),
"CodeBraces", Description = "用 CodeForge 完整编辑器编写 C# 脚本节点:脚本类继承 BasePlugin,可写多个类/添加 DLL 引用/智能补全/编译诊断/一键运行;拥有 GetValue/Log/Service/Model 等普通插件全部能力")]
public class ScriptToolPlugin : BasePlugin
{
[NonSerialized] private BasePlugin _script;
[NonSerialized] private string _scriptCode;
[NonSerialized] private List _scriptErrors = new List();
/// 取得(并按需重编译)已注入 Model/Context/Registry 的脚本插件实例;编译失败返回 null。
private BasePlugin GetScript()
{
var code = Model != null ? (Model.Code ?? "") : "";
if (_script != null && _scriptCode == code) return _script;
List errs, warns;
var inst = ScriptCompiler.CreateScript(code, Model, Context, Registry, out errs, out warns);
_scriptErrors = errs ?? new List();
_scriptCode = code;
if (inst != null)
{
_script = inst;
try { inst.InitPlugin(); } catch { /* 脚本 InitPlugin 失败不阻断,PluginRun 会再暴露 */ }
}
else _script = null;
return _script;
}
/// 每次执行前刷新注入(Registry 可能在初始化后才由框架注入)。
private void Inject(BasePlugin s)
{
if (s == null) return;
s.GetModel = Model;
s.Context = Context;
s.Registry = Registry;
}
public override bool InitPlugin()
{
// 首次拖入节点即走这里(InitRun)。CodeForge 引擎首建 + 全量引用 + Roslyn 编译耗时数秒,
// 同步做会冻结 UI —— 一律转后台编译并立即返回;输出在编译完成后的下一次声明/运行时生效。
var code = Model != null ? (Model.Code ?? "") : "";
System.Threading.Tasks.Task.Run(() =>
{
try
{
List errs, warns;
ScriptCompiler.CompileEntry(code, out errs, out warns);
}
catch { /* 后台预热/编译失败不阻断创建,PluginRun 时会再暴露 */ }
});
return true;
}
public override List DeclareOutputs()
{
// 只用已缓存的编译结果(绝不在这里同步编译——本方法会在创建节点的 UI 线程被调用);
// 后台编译完成后,下次打开属性/保存/运行即按脚本声明输出。
var code = Model != null ? (Model.Code ?? "") : "";
var s = ScriptCompiler.TryCreateCached(code, Model, Context, Registry);
if (s != null)
{
Inject(s);
try { return s.DeclareOutputs(); }
catch { /* 脚本声明输出失败则回退空 */ }
}
return new List();
}
public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary results)
{
results = new Dictionary();
var s = GetScript();
if (s == null)
{
results["Error"] = "脚本编译失败:" + string.Join(" | ", (_scriptErrors ?? new List()).ToArray());
Log(1, "脚本节点失败: " + results["Error"], TeamAAS.LogLevel.Error);
return NodeRunStatus.Failed;
}
Inject(s);
try
{
Dictionary r;
var status = s.PluginRun(token, out r);
if (r != null) results = r;
return status;
}
catch (Exception ex)
{
results["Error"] = ex.Message;
Log(1, "脚本运行异常: " + ex.Message, TeamAAS.LogLevel.Error);
return NodeRunStatus.Failed;
}
}
}
}