diff --git a/Library/Enums/NodeType.cs b/Library/Enums/NodeType.cs index 6c5af06..37f7fbc 100644 --- a/Library/Enums/NodeType.cs +++ b/Library/Enums/NodeType.cs @@ -124,6 +124,11 @@ namespace Serein.Library /// [Description("base")] Script, + /// + /// C#脚本节点 + /// + [Description("base")] + NetScript, } } diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index d37ff55..bd2a1d8 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -65,17 +65,21 @@ namespace Serein.Library } this.DebugSetting.NodeModel = null; this.DebugSetting = null; - foreach (var pd in this.MethodDetails.ParameterDetailss) + if(this.MethodDetails.ParameterDetailss != null) { - pd.DataValue = null; - pd.Items = null; - pd.NodeModel = null; - pd.ExplicitType = null; - pd.DataType = null; - pd.Name = null; - pd.ArgDataSourceNodeGuid = null; - pd.InputType = ParameterValueInputType.Input; + foreach (var pd in this.MethodDetails.ParameterDetailss) + { + pd.DataValue = null; + pd.Items = null; + pd.NodeModel = null; + pd.ExplicitType = null; + pd.DataType = null; + pd.Name = null; + pd.ArgDataSourceNodeGuid = null; + pd.InputType = ParameterValueInputType.Input; + } } + this.MethodDetails.ParameterDetailss = null; this.MethodDetails.ActingInstance = null; this.MethodDetails.NodeModel = null; diff --git a/Library/Utils/SereinEnv.cs b/Library/Utils/SereinEnv.cs index f551f42..8ecc03c 100644 --- a/Library/Utils/SereinEnv.cs +++ b/Library/Utils/SereinEnv.cs @@ -9,7 +9,7 @@ using System.Text; using System.Threading.Tasks; using System.Xml.Linq; -namespace Serein.Library.Utils +namespace Serein.Library { public static class SereinEnv { diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index abfd5d6..35c6de0 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -5,6 +5,7 @@ using Serein.Library.Utils.SereinExpression; using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; using System.Reactive; +using System.Reflection; using System.Text; namespace Serein.NodeFlow.Env @@ -52,6 +53,7 @@ namespace Serein.NodeFlow.Env NodeMVVMManagement.RegisterModel(NodeControlType.ConditionRegion, typeof(CompositeConditionNode)); // 条件区域 NodeMVVMManagement.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点 NodeMVVMManagement.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点 + NodeMVVMManagement.RegisterModel(NodeControlType.NetScript, typeof(SingleNetScriptNode)); // 脚本节点 #endregion #region 注册基本服务类 @@ -647,9 +649,30 @@ namespace Serein.NodeFlow.Env { SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件:{ex}"); } + } + + /// + /// 加载本地程序集 + /// + /// + public void LoadLibrary(Assembly assembly) + { + try + { + (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibraryOfPath(assembly); + if (mdInfos.Count > 0) + { + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示 + } + } + catch (Exception ex) + { + SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件:{ex}"); + } } + /// /// 移除DLL /// diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 9e06786..4f8f127 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -76,7 +76,7 @@ namespace Serein.NodeFlow flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == NodeType.Flipflop && it.IsStart == false) .Select(it => (SingleFlipflopNode)it) - .Where(node => node is SingleFlipflopNode flipflopNode && flipflopNode.NotExitPreviousNode()) + .Where(node => node.DebugSetting.IsEnable && node is SingleFlipflopNode flipflopNode && flipflopNode.NotExitPreviousNode()) .ToList();// 获取需要再运行开始之前启动的触发器节点 runNodeMd = nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息 diff --git a/NodeFlow/Model/SingleGlobalDataNode.cs b/NodeFlow/Model/SingleGlobalDataNode.cs index 6860f90..56db45f 100644 --- a/NodeFlow/Model/SingleGlobalDataNode.cs +++ b/NodeFlow/Model/SingleGlobalDataNode.cs @@ -14,7 +14,7 @@ namespace Serein.NodeFlow.Model { - /// + /// /// Expression Operation - 表达式操作 /// [NodeProperty(ValuePath = NodeValuePath.Node)] @@ -51,7 +51,11 @@ namespace Serein.NodeFlow.Model /// private NodeModelBase? DataNode; - + /// + /// 有节点被放置 + /// + /// + /// public bool PlaceNode(NodeModelBase nodeModel) { if(DataNode is null) diff --git a/NodeFlow/Model/SingleNetScriptNode.cs b/NodeFlow/Model/SingleNetScriptNode.cs new file mode 100644 index 0000000..ad99eac --- /dev/null +++ b/NodeFlow/Model/SingleNetScriptNode.cs @@ -0,0 +1,122 @@ +using Serein.Library; +using Serein.Library.Api; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow.Model +{ + + [NodeProperty(ValuePath = NodeValuePath.Node)] + public partial class SingleNetScriptNode : NodeModelBase + { + /// + /// 脚本代码 + /// + [PropertyInfo(IsNotification = true)] + private string _script; + + /// + /// 功能提示 + /// + [PropertyInfo(IsNotification = true)] + private string _tips = "写一下提示吧"; + + /// + /// 依赖路径 + /// + [PropertyInfo(IsNotification = true)] + private List _libraryFilePaths; + + } + + public partial class SingleNetScriptNode + { + /// + /// 表达式节点是基础节点 + /// + public override bool IsBase => true; + + public SingleNetScriptNode(IFlowEnvironment environment) : base(environment) + { + this.Env = environment; + } + + + + + + public override void OnCreating() + { + //MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi)); + //if (method != null) + //{ + // ScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口 + //} + + //var md = MethodDetails; + //var pd = md.ParameterDetailss ??= new ParameterDetails[1]; + //md.ParamsArgIndex = 0; + //pd[0] = new ParameterDetails + //{ + // Index = 0, + // Name = "object", + // IsExplicitData = true, + // DataValue = string.Empty, + // DataType = typeof(object), + // ExplicitType = typeof(object), + // ArgDataSourceNodeGuid = string.Empty, + // ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, + // NodeModel = this, + // InputType = ParameterValueInputType.Input, + // Items = null, + // IsParams = true, + // Description = "脚本节点入参" + + //}; + + } + + /// + /// 导出脚本代码 + /// + /// + /// + public override NodeInfo SaveCustomData(NodeInfo nodeInfo) + { + dynamic data = new ExpandoObject(); + data.Script = this.Script ?? ""; + nodeInfo.CustomData = data; + return nodeInfo; + } + + /// + /// 加载自定义数据 + /// + /// + public override void LoadCustomData(NodeInfo nodeInfo) + { + this.Script = nodeInfo.CustomData?.Script ?? ""; + + // 更新变量名 + //for (int i = 0; i < Math.Min(this.MethodDetails.ParameterDetailss.Length, nodeInfo.ParameterData.Length); i++) + //{ + // this.MethodDetails.ParameterDetailss[i].Name = nodeInfo.ParameterData[i].ArgName; + //} + + + } + + + + + + + + + } +} diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/SingleScriptNode.cs index 239a6d6..fda4f1f 100644 --- a/NodeFlow/Model/SingleScriptNode.cs +++ b/NodeFlow/Model/SingleScriptNode.cs @@ -28,13 +28,11 @@ namespace Serein.NodeFlow.Model /// public partial class SingleScriptNode : NodeModelBase { - /// /// 脚本节点是基础节点 /// public override bool IsBase => true; - private IScriptFlowApi ScriptFlowApi { get; } private ASTNode mainNode; diff --git a/Library/Utils/DynamicCompiler.cs b/NodeFlow/Tool/DynamicCompiler.cs similarity index 75% rename from Library/Utils/DynamicCompiler.cs rename to NodeFlow/Tool/DynamicCompiler.cs index c835194..7b563e9 100644 --- a/Library/Utils/DynamicCompiler.cs +++ b/NodeFlow/Tool/DynamicCompiler.cs @@ -9,7 +9,7 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; -namespace Serein.Library.Utils +namespace Serein.NodeFlow.Tool { /// /// 动态编译 @@ -22,7 +22,7 @@ namespace Serein.Library.Utils { // 默认添加当前 AppDomain 加载的所有程序集 var defaultReferences = AppDomain.CurrentDomain.GetAssemblies() - .Where(a => !string.IsNullOrEmpty(a.Location)) // a.IsDynamic 动态程序集 + .Where(a => !string.IsNullOrEmpty(a.Location)) // a.IsDynamic 动态程序集 .Select(a => MetadataReference.CreateFromFile(a.Location)); @@ -64,17 +64,28 @@ namespace Serein.Library.Utils public Assembly Compile(string code, string assemblyName = null) { SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code); - if(assemblyName is null) + if (assemblyName is null) { assemblyName = Path.GetRandomFileName(); // 生成随机程序集名称 } + var temp_dir = Path.Combine(Directory.GetCurrentDirectory(), "temp"); + if (!Directory.Exists(temp_dir)) + { + Directory.CreateDirectory(temp_dir); + } + var savePath = Path.Combine(temp_dir, $"{assemblyName}.dll"); + + var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + + CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, new[] { syntaxTree }, _references, - new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + options + ); using (var ms = new MemoryStream()) @@ -90,11 +101,27 @@ namespace Serein.Library.Utils } return null; } - ms.Seek(0, SeekOrigin.Begin); - return Assembly.Load(ms.ToArray()); + var assembly = Assembly.Load(ms.ToArray()); + var t1 = assembly.Location; + var t = assembly.GetType().Assembly.Location; + + + + // 保存 + + compilation.Emit(savePath); + return assembly; } - + } + + public void Save() + { + + } + + + } } diff --git a/NodeFlow/Tool/FlowLibrary.cs b/NodeFlow/Tool/FlowLibrary.cs index 35c4bb9..56923af 100644 --- a/NodeFlow/Tool/FlowLibrary.cs +++ b/NodeFlow/Tool/FlowLibrary.cs @@ -26,19 +26,33 @@ namespace Serein.NodeFlow /// public class FlowLibrary { - private readonly Assembly assembly; - private readonly Action actionOfUnloadAssmbly; + private readonly Assembly _assembly; - public FlowLibrary(Assembly assembly, - Action actionOfUnloadAssmbly) + + + //private readonly Action actionOfUnloadAssmbly; + /*, Action actionOfUnloadAssmbly*/ + //this.actionOfUnloadAssmbly = actionOfUnloadAssmbly; + + public FlowLibrary(Assembly assembly) { - this.assembly = assembly; - this.actionOfUnloadAssmbly = actionOfUnloadAssmbly; + this._assembly = assembly; + this.FullName = Path.GetFileName(_assembly.Location); + + this.FilePath = _assembly.Location; } - public string FullName => assembly.GetName().FullName; + public FlowLibrary(Assembly assembly, + string filePath) + { + this._assembly = assembly; + this.FullName = Path.GetFileName(filePath); ; + this.FilePath = filePath; + } - public string Version => assembly.GetName().Version.ToString(); + public string FullName { get; private set; } + + public string FilePath { get; private set; } /// /// 加载程序集时创建的方法描述 @@ -68,7 +82,7 @@ namespace Serein.NodeFlow DelegateDetailss.Clear(); RegisterTypes.Clear(); MethodDetailss.Clear(); - actionOfUnloadAssmbly?.Invoke(); + //actionOfUnloadAssmbly?.Invoke(); } @@ -78,11 +92,12 @@ namespace Serein.NodeFlow /// public NodeLibraryInfo ToInfo() { + var assemblyName = _assembly.GetName().Name; return new NodeLibraryInfo { - AssemblyName = assembly.GetName().Name, - FileName = Path.GetFileName(assembly.Location), - FilePath = assembly.Location, + AssemblyName = assemblyName, + FileName = this.FullName, + FilePath = this.FilePath, }; @@ -94,8 +109,9 @@ namespace Serein.NodeFlow /// /// 程序集本身 /// - public bool LoadAssembly(Assembly assembly) + public bool LoadAssembly() { + Assembly assembly = this._assembly; #region 检查入参 // 加载DLL,创建 MethodDetails、实例作用对象、委托方法 diff --git a/NodeFlow/Tool/FlowLibraryManagement.cs b/NodeFlow/Tool/FlowLibraryManagement.cs index ab3cc66..e06f953 100644 --- a/NodeFlow/Tool/FlowLibraryManagement.cs +++ b/NodeFlow/Tool/FlowLibraryManagement.cs @@ -39,7 +39,36 @@ namespace Serein.NodeFlow.Tool /// public (NodeLibraryInfo, List) LoadLibraryOfPath(string libraryfilePath) { - return LoadDllNodeInfo(libraryfilePath); + + var dir = Path.GetDirectoryName(libraryfilePath); // 获取目录路径 + var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);// 每个类库下面至少需要有“Serein.Library.dll”类库依赖 + if (!Path.Exists(sereinFlowBaseLibraryPath)) + { + throw new Exception($"从文件加载DLL失败,目标文件夹不存在{SereinBaseLibrary}文件" ); + } + + var flowAlc = new FlowLibraryAssemblyContext(sereinFlowBaseLibraryPath, Path.GetFileName(libraryfilePath)); + var assembly = flowAlc.LoadFromAssemblyPath(libraryfilePath); // 加载指定路径的程序集 + var reulst = LoadDllNodeInfo(assembly); + if(reulst.Item1 is null || reulst.Item2.Count == 0) + { + flowAlc?.Unload(); // 卸载程序集 + flowAlc = null; + GC.Collect(); // 强制触发GC确保卸载成功 + GC.WaitForPendingFinalizers(); + throw new Exception("从文件加载DLL失败:"+ libraryfilePath); + } + return reulst; + } + + /// + /// 加载类库 + /// + /// + /// + public (NodeLibraryInfo, List) LoadLibraryOfPath(Assembly assembly) + { + return LoadDllNodeInfo(assembly); } /// @@ -206,84 +235,44 @@ namespace Serein.NodeFlow.Tool /// public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll"; - private (NodeLibraryInfo, List) LoadDllNodeInfo(string dllFilePath) + private (NodeLibraryInfo, List) LoadDllNodeInfo(Assembly assembly) { - var fileName = Path.GetFileName(dllFilePath); // 获取文件名 - if (SereinBaseLibrary.Equals(fileName)) + + if (assembly.FullName?.ToString().Equals(typeof(IFlowEnvironment).Assembly.FullName?.ToString()) == true) { - return LoadAssembly(typeof(IFlowEnvironment).Assembly, () => { - //SereinEnv.PrintInfo(InfoType.WRAN, "基础模块不能卸载"); - }); + // 加载基础依赖 + return LoadAssembly(typeof(IFlowEnvironment).Assembly); } else { - var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径 - var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary); - // 每个类库下面至少需要有“Serein.Library.dll”类库依赖 - - //AssemblyLoader assemblyLoader = new AssemblyLoader(dllFilePath); - - - var flowAlc = new FlowLibraryAssemblyContext(sereinFlowBaseLibraryPath, fileName); - Action actionUnload = () => + try { - flowAlc?.Unload(); // 卸载程序集 - flowAlc = null; - GC.Collect(); // 强制触发GC确保卸载成功 - GC.WaitForPendingFinalizers(); - }; - var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集 - var assembly_result = LoadAssembly(assembly, actionUnload); - return assembly_result; + var assembly_result = LoadAssembly(assembly); + return assembly_result; + } + catch (Exception) + { + return (null,[]); + } + } - /* var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径 - var sereinFlowLibraryPath = Path.Combine(dir, SereinLibraryDll); - // 每个类库下面至少需要有“Serein.Library.dll”类库依赖 - var flowAlc = new FlowLibraryAssemblyContext(sereinFlowLibraryPath, fileName); - Action actionUnload = () => - { - flowAlc?.Unload(); // 卸载程序集 - flowAlc = null; - GC.Collect(); // 强制触发GC确保卸载成功 - GC.WaitForPendingFinalizers(); - }; - var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集 - if (_myFlowLibrarys.ContainsKey(assembly.GetName().Name)) - { - actionUnload.Invoke(); - throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!"); - } - FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload); - if (flowLibrary.LoadAssembly(assembly)) - { - _myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary); - (NodeLibraryInfo, List) result = (flowLibrary.ToInfo(), - flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList()); - return result; - } - else - { - throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败"); - }*/ } - private (NodeLibraryInfo, List) LoadAssembly(Assembly assembly,Action actionUnload) + private (NodeLibraryInfo, List) LoadAssembly(Assembly assembly) { if (_myFlowLibrarys.ContainsKey(assembly.GetName().Name)) { - actionUnload.Invoke(); throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!"); } - FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload); - var loadResult = flowLibrary.LoadAssembly(assembly); // 加载程序集 + FlowLibrary flowLibrary = new FlowLibrary(assembly); + var loadResult = flowLibrary.LoadAssembly(); // 加载程序集 if (loadResult) { var assemblyName = assembly.GetName().Name; if (string.IsNullOrEmpty(assemblyName)) { - actionUnload.Invoke(); throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败,没有程序集名称"); } _myFlowLibrarys.TryAdd(assemblyName, flowLibrary); @@ -296,7 +285,6 @@ namespace Serein.NodeFlow.Tool } else { - actionUnload.Invoke(); throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败"); } } diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 63b0dbd..fffa778 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -19,7 +19,7 @@ public static class NodeMethodDetailsHelper /// public static IEnumerable GetMethodsToProcess(Type type) { - return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) .Where(m => m.GetCustomAttribute()?.Scan == true); } diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs index bcc7959..deeb37b 100644 --- a/Workbench/App.xaml.cs +++ b/Workbench/App.xaml.cs @@ -24,6 +24,7 @@ namespace Serein.Workbench filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\net8.0\PLCproject.dnf"; filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf"; filePath = @"F:\临时\project\project.dnf"; + filePath = @"F:\临时\flow\qrcode\project.dnf"; //filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\test.dnf"; string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 App.FlowProjectData = JsonConvert.DeserializeObject(content); diff --git a/Workbench/MainWindow.xaml b/Workbench/MainWindow.xaml index 87cef37..399f48c 100644 --- a/Workbench/MainWindow.xaml +++ b/Workbench/MainWindow.xaml @@ -46,11 +46,16 @@ + + + + + @@ -84,6 +89,7 @@ + diff --git a/Workbench/MainWindow.xaml.cs b/Workbench/MainWindow.xaml.cs index 4aa9323..58e0d71 100644 --- a/Workbench/MainWindow.xaml.cs +++ b/Workbench/MainWindow.xaml.cs @@ -5,6 +5,7 @@ using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; using Serein.NodeFlow; +using Serein.NodeFlow.Env; using Serein.NodeFlow.Tool; using Serein.Workbench.Extension; using Serein.Workbench.Node; @@ -12,6 +13,7 @@ using Serein.Workbench.Node.View; using Serein.Workbench.Node.ViewModel; using Serein.Workbench.Themes; using System.IO; +using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Windows; @@ -170,6 +172,7 @@ namespace Serein.Workbench NodeMVVMManagement.RegisterUI(NodeControlType.ConditionRegion, typeof(ConditionRegionControl), typeof(ConditionRegionNodeControlViewModel)); NodeMVVMManagement.RegisterUI(NodeControlType.GlobalData, typeof(GlobalDataControl), typeof(GlobalDataNodeControlViewModel)); NodeMVVMManagement.RegisterUI(NodeControlType.Script, typeof(ScriptNodeControl), typeof(ScriptNodeControlViewModel)); + NodeMVVMManagement.RegisterUI(NodeControlType.NetScript, typeof(NetScriptNodeControl), typeof(NetScriptNodeControlViewModel)); #endregion @@ -398,7 +401,12 @@ namespace Serein.Workbench { NodeLibraryInfo? library = projectData.Librarys[index]; string sourceFilePath = new Uri(library.FilePath).LocalPath; // 源文件夹 - string targetFilePath = System.IO.Path.Combine(librarySavePath, library.FileName); // 目标文件夹 + string targetDir = System.IO.Path.Combine(librarySavePath, library.AssemblyName); // 目标文件夹 + if (!Path.Exists(targetDir)) + { + Directory.CreateDirectory(targetDir); + } + string targetFilePath = System.IO.Path.Combine(targetDir, library.FileName); // 目标文件夹 try { @@ -432,9 +440,6 @@ namespace Serein.Workbench var tmpUri2 = new Uri(targetFilePath); var relativePath = saveProjectFileUri.MakeRelativeUri(tmpUri2).ToString(); // 转为类库的相对文件路径 - - - //string relativePath = System.IO.Path.GetRelativePath(savePath, targetPath); projectData.Librarys[index].FilePath = relativePath; } @@ -1417,6 +1422,7 @@ namespace Serein.Workbench Type when typeof(ExpOpNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpOp, Type when typeof(GlobalDataControl).IsAssignableFrom(droppedType) => NodeControlType.GlobalData, Type when typeof(ScriptNodeControl).IsAssignableFrom(droppedType) => NodeControlType.Script, + Type when typeof(NetScriptNodeControl).IsAssignableFrom(droppedType) => NodeControlType.NetScript, _ => NodeControlType.None, }; if (nodeControlType != NodeControlType.None) @@ -2669,6 +2675,55 @@ namespace Serein.Workbench #endregion + #region 顶部菜单栏 - 拓展 + /// + /// 动态编译 + /// + /// + /// + private async void OpenDynamicCompileEdit_Click(object sender, RoutedEventArgs e) + { + try + { + string script = @"using Serein.Library; +using Serein.Library.Api; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +[DynamicFlow(""[动态编译]"")] +public class FlowLibrary +{ + [NodeAction(NodeType.Action, AnotherName = ""输出"")] + public void Print(IDynamicContext context,string value = ""Hello World!"") + { + context.Env.WriteLine(InfoType.INFO, value); + } +}"; + + DynamicCompilerView dynamicCompilerView = new DynamicCompilerView(); + dynamicCompilerView.ScriptCode = script; + dynamicCompilerView.OnCompileComplete = (assembly) => + { + if(EnvDecorator.CurrentEnv is FlowEnvironment environment) + { + environment.LoadLibrary(assembly); + } + //EnvDecorator.LoadLibrary + }; + dynamicCompilerView.ShowDialog(); + } + catch (Exception ex) + { + SereinEnv.WriteLine(ex); + return; + } + } + #endregion #region 顶部菜单栏 - 远程管理 private async void ButtonStartRemoteServer_Click(object sender, RoutedEventArgs e) { diff --git a/Workbench/Node/View/NetScriptNodeControl.xaml b/Workbench/Node/View/NetScriptNodeControl.xaml new file mode 100644 index 0000000..10d3a8f --- /dev/null +++ b/Workbench/Node/View/NetScriptNodeControl.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Workbench/Node/View/NetScriptNodeControl.xaml.cs b/Workbench/Node/View/NetScriptNodeControl.xaml.cs new file mode 100644 index 0000000..8c314f6 --- /dev/null +++ b/Workbench/Node/View/NetScriptNodeControl.xaml.cs @@ -0,0 +1,58 @@ +using Serein.NodeFlow.Model; +using Serein.Workbench.Node.ViewModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Serein.Workbench.Node.View +{ + /// + /// NetScriptNodeControl.xaml 的交互逻辑 + /// + public partial class NetScriptNodeControl : NodeControlBase , INodeJunction + { + public NetScriptNodeControl() + { + base.ViewModel = new NetScriptNodeControlViewModel(new SingleNetScriptNode(null)); + base.ViewModel.IsEnabledOnView = false; + base.DataContext = ViewModel; + InitializeComponent(); + } + + public NetScriptNodeControl(NetScriptNodeControlViewModel viewModel) : base(viewModel) + { + DataContext = viewModel; + InitializeComponent(); + } + + /// + /// 入参控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl; + + /// + /// 下一个调用方法控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl; + + /// + /// 返回值控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.ReturnDataJunction => throw new Exception(); + + + public JunctionControlBase[] ArgDataJunction => []; + + } +} diff --git a/Workbench/Node/View/ScriptNodeControl.xaml b/Workbench/Node/View/ScriptNodeControl.xaml index ed26c9f..6cb222d 100644 --- a/Workbench/Node/View/ScriptNodeControl.xaml +++ b/Workbench/Node/View/ScriptNodeControl.xaml @@ -9,11 +9,8 @@ d:DataContext="{d:DesignInstance vm:ScriptNodeControlViewModel}" mc:Ignorable="d" MinWidth="50"> - + - diff --git a/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs b/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs new file mode 100644 index 0000000..5e090ef --- /dev/null +++ b/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs @@ -0,0 +1,99 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Serein.Library; +using Serein.NodeFlow; +using Serein.NodeFlow.Model; +using Serein.Workbench.Themes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Documents; +using System.Windows.Input; + +namespace Serein.Workbench.Node.ViewModel +{ + public class NetScriptNodeControlViewModel : NodeControlViewModelBase + { + private SingleNetScriptNode NodeModel => (SingleNetScriptNode)base.NodeModel; + + public string Tips + { + get => NodeModel.Tips; + set { NodeModel.Tips = value; OnPropertyChanged(); } + } + + public string Script + { + get => NodeModel.Script; + set { NodeModel.Script = value; OnPropertyChanged(); } + } + + + public NetScriptNodeControlViewModel(NodeModelBase nodeModel) : base(nodeModel) + { + Script = @"using Serein.Library; +using Serein.Library.Api; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +[DynamicFlow(""[动态编译]"")] +public class FlowLibrary +{ + [NodeAction(NodeType.Action, AnotherName = ""输出"")] + public void Print(IDynamicContext context,string value = ""Hello World!"") + { + context.Env.WriteLine(InfoType.INFO, value); + } +}"; + + CommandOpenScriptEdit = new RelayCommand(async o => + { + DynamicCompilerView dynamicCompilerView = new DynamicCompilerView(); + dynamicCompilerView.ScriptCode = this.Script ; + dynamicCompilerView.OnCompileComplete = OnCompileComplete; + dynamicCompilerView.ShowDialog(); + + //try + //{ + // var result = await NodeModel.ExecutingAsync(new Library.DynamicContext(nodeModel.Env)); + // nodeModel.Env.WriteLine(InfoType.INFO, result?.ToString()); + //} + //catch (Exception ex) + //{ + // nodeModel.Env.WriteLine(InfoType.ERROR, ex.ToString()); + //} + }); + } + + private static void OnCompileComplete(System.Reflection.Assembly assembly) + { + FlowLibrary flowLibrary = new FlowLibrary(assembly); + var loadResult = flowLibrary.LoadAssembly(); // 动态编译完成后加载程序集 + if (!loadResult) + { + return ; + } + + var md = flowLibrary.MethodDetailss.Values.FirstOrDefault(); + if (md is null) + { + return; + } + + + } + + /// + /// 打开编辑窗口 + /// + public ICommand CommandOpenScriptEdit { get; } + + + } +} diff --git a/Workbench/Serein.WorkBench.csproj b/Workbench/Serein.WorkBench.csproj index e4bc66e..0e1152d 100644 --- a/Workbench/Serein.WorkBench.csproj +++ b/Workbench/Serein.WorkBench.csproj @@ -59,10 +59,11 @@ + - + diff --git a/Workbench/Themes/DynamicCompilerView.xaml b/Workbench/Themes/DynamicCompilerView.xaml new file mode 100644 index 0000000..3047a0a --- /dev/null +++ b/Workbench/Themes/DynamicCompilerView.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + +