diff --git a/.gitignore b/.gitignore index e5c3f88..2e24693 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,3 @@ obj/ # 排除某些项目 -Net461DllTest/ \ No newline at end of file diff --git a/Library/Entity/ExplicitData.cs b/Library/Entity/ExplicitData.cs index e2aa190..ff97094 100644 --- a/Library/Entity/ExplicitData.cs +++ b/Library/Entity/ExplicitData.cs @@ -26,7 +26,7 @@ namespace Serein.Library.Entity ///// ///// 显式类型 ///// - //public Type ExplicitType { get; set; } + public Type ExplicitType { get; set; } ///// ///// 显示类型编号> @@ -55,7 +55,7 @@ namespace Serein.Library.Entity { Index = Index, IsExplicitData = IsExplicitData, - // ExplicitType = ExplicitType, + ExplicitType = ExplicitType, ExplicitTypeName = ExplicitTypeName, DataType = DataType, ParameterName = ParameterName, diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs index e75c061..7479c56 100644 --- a/Library/NodeAttribute.cs +++ b/Library/NodeAttribute.cs @@ -73,6 +73,30 @@ namespace Serein.Library.Attributes } } + /// + /// 枚举值转换器,要求枚举项标记的BindValueAttribute特性,与搭配的参数类型一致,否则参数不会传入 + /// + + [AttributeUsage(AttributeTargets.Parameter)] + public class EnumTypeConvertorAttribute : Attribute + { + public Type EnumType { get; } + + public EnumTypeConvertorAttribute(Type @enum) + { + if (@enum.IsEnum) + { + EnumType = @enum; + } + else + { + throw new ArgumentException("需要枚举类型"); + } + } + } + + + [AttributeUsage(AttributeTargets.Field)] public class PLCValueAttribute : Attribute { @@ -121,27 +145,4 @@ namespace Serein.Library.Attributes } } - - /// - /// 枚举值转换器 - /// - - //[AttributeUsage(AttributeTargets.Parameter)] - //public class EnumConvertorAttribute : Attribute - //{ - // public Type Enum { get; } - - // public EnumConvertorAttribute(Type @enum) - // { - // if (@enum.IsEnum) - // { - // Enum = @enum; - // } - // else - // { - // throw new ArgumentException("需要枚举类型"); - // } - // } - //} - } diff --git a/Library/Utils/EnumHelper.cs b/Library/Utils/EnumHelper.cs index 3743e34..0ef6161 100644 --- a/Library/Utils/EnumHelper.cs +++ b/Library/Utils/EnumHelper.cs @@ -16,6 +16,13 @@ namespace Serein.Library.Utils return attribute != null ? (TResult)valueSelector(attribute) : default; } + public static object GetBoundValue(Type enumType,object enumValue, Func valueSelector) + { + var fieldInfo = enumType.GetField(enumValue.ToString()); + var attribute = fieldInfo.GetCustomAttribute(); + + return attribute != null ? valueSelector(attribute) : default; + } //public static TResult GetBoundValue(TEnum enumValue, // Func valueSelector) diff --git a/Net461DllTest/Data/MyData.cs b/Net461DllTest/Data/MyData.cs new file mode 100644 index 0000000..5ad9543 --- /dev/null +++ b/Net461DllTest/Data/MyData.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net461DllTest.Data +{ + public class MyData + { + public int[] Values { get; set; } = new int[] { 1, 1, 4, 5, 1, 4, 1, 9, 9, 9 }; + public int Count { get; set; } + public string Tips { get; set; } + } +} diff --git a/Net461DllTest/Device/PlcDevice.cs b/Net461DllTest/Device/PlcDevice.cs new file mode 100644 index 0000000..a7eb7ad --- /dev/null +++ b/Net461DllTest/Device/PlcDevice.cs @@ -0,0 +1,48 @@ +using Net461DllTest.Data; +using Net461DllTest.Signal; +using Serein.Library.Attributes; +using Serein.Library.NodeFlow.Tool; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net461DllTest.Device +{ + + + public class PlcDevice : ChannelFlowTrigger + { + [AutoInjection] + public MyData MyData { get; set; } + + public PlcDevice() + { + PlcId = 114514 + 10000000 * new Random().Next(1, 9); + } + public int PlcId { get; set; } + + public void InitDevice(string ip, int port, string tips) + { + Write($"模拟设备初始化 :{Environment.NewLine}" + + $" ip :{ip}{Environment.NewLine}" + + $"port:{port}{Environment.NewLine}" + + $"tips:{tips}{Environment.NewLine}"); + } + + public void Write(T value) + { + Console.WriteLine($"{value}"); + } + public void Read() + { + Console.WriteLine($"读取数据:... "); + } + public void Disconnect() + { + Console.WriteLine($"断开连接..."); + } + } + +} diff --git a/Net461DllTest/Flow/LogicControl.cs b/Net461DllTest/Flow/LogicControl.cs new file mode 100644 index 0000000..0562ac4 --- /dev/null +++ b/Net461DllTest/Flow/LogicControl.cs @@ -0,0 +1,131 @@ +using Net461DllTest.Data; +using Net461DllTest.Device; +using Net461DllTest.Signal; +using Net461DllTest.Web; +using Serein.Library.Api; +using Serein.Library.Attributes; +using Serein.Library.Enums; +using Serein.Library.Ex; +using Serein.Library.Framework.NodeFlow; +using Serein.Library.NodeFlow.Tool; +using Serein.Library.Web; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net461DllTest.Flow +{ + [DynamicFlow] // 标记该类存在节点方法 + public class LogicControl + { + [AutoInjection] // 标记该属性为依赖项,需要注入 + public PlcDevice MyPlc { get; set; } + + + #region 初始化、初始化完成以及退出的事件 + [NodeAction(NodeType.Init)] // Init : 初始化事件,流程启动时执行 + public void Init(IDynamicContext context) + { + context.Env.IOC.Register(); // 注册Plc设备 + context.Env.IOC.Register(); // 注册数据类 + context.Env.IOC.Register(); // 注册Web服务 + // // 注册控制器 + context.Env.IOC.Run(router => { + router.RegisterController(typeof(ApiController)); + }); + } + + [NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作 + public void Loading(IDynamicContext context) + { + context.Env.IOC.Run((web) => + { + web.Start("http://*:8089/"); // 开启 Web 服务 + }); + } + + [NodeAction(NodeType.Exit)] // 流程结束时自动执行 + public void Exit(IDynamicContext context) + { + MyPlc.Disconnect(); + MyPlc.CancelAllTasks(); + } + + #endregion + + #region 触发器 + + [NodeAction(NodeType.Flipflop, "等待信号触发", ReturnType = typeof(int))] + public async Task WaitTask(OrderSignal order = OrderSignal.A) + { + try + { + TriggerData triggerData = await MyPlc.CreateChannelWithTimeoutAsync(order, TimeSpan.FromMinutes(5), 0); + if (triggerData.Type == TriggerType.Overtime) + { + throw new FlipflopException("超时取消"); + } + //int.TryParse(triggerData.Value.ToString(),out int result); + MyPlc.MyData.Count += (int)triggerData.Value; + return new FlipflopContext(FlipflopStateType.Succeed, MyPlc.MyData.Count); + } + catch (FlipflopException) + { + throw; + } + catch (Exception) + { + return new FlipflopContext(FlipflopStateType.Error); + } + + } + + #endregion + + #region 动作 + + [NodeAction(NodeType.Action, "初始化")] + public PlcDevice PlcInit(string ip = "192.168.1.1", + int port = 6688, + string tips = "测试") + { + MyPlc.InitDevice(ip, port, tips); + return MyPlc; + } + + + [NodeAction(NodeType.Action, "自增")] + public PlcDevice 自增(int number = 1) + { + MyPlc.MyData.Count += number; + return MyPlc; + } + + [NodeAction(NodeType.Action, "重置计数")] + public void 重置计数() + { + MyPlc.MyData.Count = 0; + } + + [NodeAction(NodeType.Action, "触发信号")] + public void 光电1信号触发(int data) + { + MyPlc.Write($"{MyPlc.PlcId.ToString("00000")} - 信号源[光电1] - 模拟写入 : {data}{Environment.NewLine}"); + } + + //[NodeAction(NodeType.Action, "触发光电2")] + //public void 光电2信号触发(int data) + //{ + // MyPlc.Write($"{MyPlc.PlcId.ToString("00000")} - 信号源[光电2] - 模拟写入 : {data}{Environment.NewLine}"); + //} + + //[NodeAction(NodeType.Action, "触发光电3")] + //public void 光电3信号触发(int data) + //{ + // MyPlc.Write($"{MyPlc.PlcId.ToString("00000")} - 信号源[光电3] - 模拟写入 : {data}{Environment.NewLine}"); + //} + #endregion + } +} diff --git a/Net461DllTest/Flow/ViewLogicControl.cs b/Net461DllTest/Flow/ViewLogicControl.cs new file mode 100644 index 0000000..ef15caa --- /dev/null +++ b/Net461DllTest/Flow/ViewLogicControl.cs @@ -0,0 +1,100 @@ +using Net461DllTest.Data; +using Net461DllTest.Device; +using Net461DllTest.View; +using Net461DllTest.ViewModel; +using Serein.Library.Api; +using Serein.Library.Attributes; +using Serein.Library.Enums; +using Serein.Library.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; + +namespace Net461DllTest.Flow +{ + + public class ViewManagement + { + + private List
forms = new List(); + public void OpenView(Form form) + { + form.FormClosing += (s, e) => + { + // 关闭窗体时执行一些关于逻辑层的操作 + }; + form.Show(); + forms.Add(form); + } + public void CloseView(Type formType) + { + var remoteForms = forms.Where(f => f.GetType() == formType).ToArray(); + foreach (Form f in remoteForms) + { + f.Close(); + f.Dispose(); + this.forms.Remove(f); + } + + + } + } + + + public enum FromId + { + None, + [BindValue(typeof(FromWorkBenchView))] + FromWorkBenchView, + [BindValue(typeof(TeseFormView))] + TeseFormView, + } + + [DynamicFlow] + public class ViewLogicControl + { + [AutoInjection] + public ViewManagement ViewManagement { get; set; } + + [NodeAction(NodeType.Init)] + public void Init(IDynamicContext context) + { + context.Env.IOC.Register(); + context.Env.IOC.Register(); + } + + + [NodeAction(NodeType.Action, "打开窗体(指定枚举值)")] + public void OpenForm(IDynamicContext context, FromId fromId = FromId.None) + { + var fromType = EnumHelper.GetBoundValue(fromId, attr => attr.Value); + if (fromType is null) return; + if (context.Env.IOC.Instantiate(fromType) is Form form) + { + ViewManagement.OpenView(form); + + } + } + + [NodeAction(NodeType.Action, "打开窗体(使用转换器)")] + public void OpenForm2([EnumTypeConvertor(typeof(FromId))] Form form) + { + ViewManagement.OpenView(form); + } + + + [NodeAction(NodeType.Action, "关闭窗体")] + public void CloseForm(IDynamicContext context, FromId fromId = FromId.None) + { + var fromType = EnumHelper.GetBoundValue(fromId, attr => attr.Value); + if (fromType is null) return; + ViewManagement.CloseView(fromType); + } + + + + + } +} diff --git a/Net461DllTest/Net461DllTest.csproj b/Net461DllTest/Net461DllTest.csproj new file mode 100644 index 0000000..227a6e5 --- /dev/null +++ b/Net461DllTest/Net461DllTest.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {E40EE629-1A38-4011-88E3-9AD036869987} + Library + Net461DllTest + Net461DllTest + v4.6.1 + 512 + true + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + Form + + + FromWorkBenchView.cs + + + + Form + + + TeseFormView.cs + + + + + + {73B272E8-222D-4D08-A030-F1E1DB70B9D1} + Serein.Library.Framework + + + {9FCE93C2-2278-46F3-96AB-CDAAFF27A55F} + Serein.Library + + + + + FromWorkBenchView.cs + + + TeseFormView.cs + + + + + + + + + + \ No newline at end of file diff --git a/Net461DllTest/Signal/OrderSignal.cs b/Net461DllTest/Signal/OrderSignal.cs new file mode 100644 index 0000000..e986f14 --- /dev/null +++ b/Net461DllTest/Signal/OrderSignal.cs @@ -0,0 +1,21 @@ +using Serein.Library.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static Serein.Library.Attributes.PLCValueAttribute; + +namespace Net461DllTest.Signal +{ + public enum OrderSignal + { + A, + B, + C, + D, + E, + F, + G + } +} diff --git a/Net461DllTest/Signal/Signal.cs b/Net461DllTest/Signal/Signal.cs new file mode 100644 index 0000000..4dd4c0b --- /dev/null +++ b/Net461DllTest/Signal/Signal.cs @@ -0,0 +1,13 @@ +using Net461DllTest.Flow; +using Serein.Library.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net461DllTest.Signal +{ + + +} diff --git a/Net461DllTest/View/FromWorkBenchView.Designer.cs b/Net461DllTest/View/FromWorkBenchView.Designer.cs new file mode 100644 index 0000000..2d14bf7 --- /dev/null +++ b/Net461DllTest/View/FromWorkBenchView.Designer.cs @@ -0,0 +1,96 @@ +namespace Net461DllTest +{ + partial class FromWorkBenchView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button2 = new System.Windows.Forms.Button(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(187, 22); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(98, 23); + this.button1.TabIndex = 0; + this.button1.Text = "查看状态"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(35, 24); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(137, 21); + this.textBox1.TabIndex = 1; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(178, 179); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(107, 23); + this.button2.TabIndex = 2; + this.button2.Text = "触发"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // listBox1 + // + this.listBox1.FormattingEnabled = true; + this.listBox1.ItemHeight = 12; + this.listBox1.Location = new System.Drawing.Point(35, 85); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(250, 88); + this.listBox1.TabIndex = 6; + // + // FromWorkBenchView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(341, 251); + this.Controls.Add(this.listBox1); + this.Controls.Add(this.button2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.button1); + this.Name = "FromWorkBenchView"; + this.Text = "Form1"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.ListBox listBox1; + } +} \ No newline at end of file diff --git a/Net461DllTest/View/FromWorkBenchView.cs b/Net461DllTest/View/FromWorkBenchView.cs new file mode 100644 index 0000000..92f204b --- /dev/null +++ b/Net461DllTest/View/FromWorkBenchView.cs @@ -0,0 +1,51 @@ +using Net461DllTest.Device; +using Net461DllTest.Signal; +using Net461DllTest.ViewModel; +using Serein.Library.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Net461DllTest +{ + public partial class FromWorkBenchView : Form + { + [AutoInjection] + public FromWorkBenchViewModel ViewModel { get; set; } + public FromWorkBenchView() + { + InitializeComponent(); + listBox1.Items.Clear(); + var enumValues = Enum.GetValues(typeof(OrderSignal)).Cast(); + foreach (var value in enumValues) + { + listBox1.Items.Add(value.ToString()); + } + } + + private void button1_Click(object sender, EventArgs e) + { + textBox1.Text = ViewModel.GetDeviceInfo(); + } + + private void button2_Click(object sender, EventArgs e) + { + string type = listBox1.SelectedItem.ToString(); + + if (Enum.TryParse(type, out OrderSignal signal) && Enum.IsDefined(typeof(OrderSignal), signal)) + { + Console.WriteLine($"Trigger : {type}"); + ViewModel.Trigger(signal); + } + + } + + + } +} diff --git a/Net461DllTest/View/FromWorkBenchView.resx b/Net461DllTest/View/FromWorkBenchView.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Net461DllTest/View/FromWorkBenchView.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Net461DllTest/View/TeseFormView.Designer.cs b/Net461DllTest/View/TeseFormView.Designer.cs new file mode 100644 index 0000000..9c580dd --- /dev/null +++ b/Net461DllTest/View/TeseFormView.Designer.cs @@ -0,0 +1,60 @@ +namespace Net461DllTest.View +{ + partial class TeseFormView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(167, 58); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "清空"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // TeseFormView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(254, 118); + this.Controls.Add(this.button1); + this.Name = "TeseFormView"; + this.Text = "TeseForm"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/Net461DllTest/View/TeseFormView.cs b/Net461DllTest/View/TeseFormView.cs new file mode 100644 index 0000000..7bf4128 --- /dev/null +++ b/Net461DllTest/View/TeseFormView.cs @@ -0,0 +1,33 @@ +using Net461DllTest.Data; +using Net461DllTest.Signal; +using Serein.Library.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using static System.Windows.Forms.VisualStyles.VisualStyleElement; + +namespace Net461DllTest.View +{ + public partial class TeseFormView : Form + { + [AutoInjection] + public MyData MyData { get; set; } + + public TeseFormView() + { + InitializeComponent(); + + } + + private void button1_Click(object sender, EventArgs e) + { + MyData.Count = 0; + } + } +} diff --git a/Net461DllTest/View/TeseFormView.resx b/Net461DllTest/View/TeseFormView.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Net461DllTest/View/TeseFormView.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Net461DllTest/ViewModel/FromWorkBenchViewModel.cs b/Net461DllTest/ViewModel/FromWorkBenchViewModel.cs new file mode 100644 index 0000000..337ea80 --- /dev/null +++ b/Net461DllTest/ViewModel/FromWorkBenchViewModel.cs @@ -0,0 +1,35 @@ +using Net461DllTest.Device; +using Net461DllTest.Signal; +using Serein.Library.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net461DllTest.ViewModel +{ + public class FromWorkBenchViewModel + { + [AutoInjection] + public PlcDevice Device { get; set; } + public string Name { get; set; } + + public string GetDeviceInfo() + { + if(Device is null) + { + return string.Empty; + } + return "PLC ID:" + Device.PlcId + " - " + Device.MyData.Count.ToString(); + } + + + public void Trigger(OrderSignal signal) + { + Device.TriggerSignal(signal, 0); + } + + + } +} diff --git a/Net461DllTest/Web/ApiController.cs b/Net461DllTest/Web/ApiController.cs new file mode 100644 index 0000000..bf92417 --- /dev/null +++ b/Net461DllTest/Web/ApiController.cs @@ -0,0 +1,32 @@ +using Net461DllTest.Device; +using Net461DllTest.Signal; +using Serein.Library.Attributes; +using Serein.Library.Web; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net461DllTest.Web +{ + [AutoHosting] + public class ApiController : ControllerBase + { + [AutoInjection] + public PlcDevice PlcDevice { get; set; } + + [WebApi(API.POST)] + public dynamic Trigger([Url] string type, int value) + { + if (Enum.TryParse(type, out OrderSignal signal) && Enum.IsDefined(typeof(OrderSignal), signal)) + { + Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} "); + PlcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号 + return new { state = "succeed" }; + } + return new { state = "fail" }; + } + } + +} diff --git a/Net461DllTest/packages.config b/Net461DllTest/packages.config new file mode 100644 index 0000000..142ece2 --- /dev/null +++ b/Net461DllTest/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index a3cdb30..4335ad6 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -1,15 +1,18 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Serein.Library.Api; +using Serein.Library.Attributes; using Serein.Library.Entity; using Serein.Library.Enums; using Serein.Library.Ex; +using Serein.Library.Utils; using Serein.NodeFlow.Tool.SereinExpression; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Net.Http.Headers; +using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -111,11 +114,11 @@ namespace Serein.NodeFlow.Base // 从栈中弹出一个节点作为当前节点进行处理 var currentNode = stack.Pop(); - // 设置方法执行的对象 - if (currentNode.MethodDetails?.ActingInstance is not null && currentNode.MethodDetails?.ActingInstanceType is not null) - { - currentNode.MethodDetails.ActingInstance = context.Env.IOC.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); - } + //// 设置方法执行的对象 + //if (currentNode.MethodDetails?.ActingInstance is not null && currentNode.MethodDetails?.ActingInstanceType is not null) + //{ + // currentNode.MethodDetails.ActingInstance = context.Env.IOC.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); + //} #region 执行相关 @@ -198,6 +201,7 @@ namespace Serein.NodeFlow.Base MethodDetails md = MethodDetails; var del = md.MethodDelegate.Clone(); + md.ActingInstance ??= context.Env.IOC.GetOrRegisterInstantiate(MethodDetails.ActingInstanceType); object instance = md.ActingInstance; var haveParameter = md.ExplicitDatas.Length > 0; @@ -270,9 +274,8 @@ namespace Serein.NodeFlow.Base var ed = md.ExplicitDatas[i]; // 方法入参描述 - if (ed.IsExplicitData) + if (ed.IsExplicitData) // 判断是否使用显示的输入参数 { - if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) { // 执行表达式从上一节点获取对象 @@ -289,11 +292,34 @@ namespace Serein.NodeFlow.Base inputParameter = flowData; // 使用上一节点的对象 } + + + //var attribute = ed.DataType.GetCustomAttribute(); + //if (attribute is not null && attribute.EnumType.IsEnum) // 获取枚举转换器中记录的枚举 + if ( ed.DataType != ed.ExplicitType) // 获取枚举转换器中记录的枚举 + { + if (ed.ExplicitType.IsEnum && Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项 + { + var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value); + if(type is Type enumBindType && enumBindType is not null) + { + var value = context.Env.IOC.Instantiate(enumBindType); + if(value is not null) + { + parameters[i] = value; + continue; + } + } + } + } + + try { parameters[i] = ed.DataType switch { //Type t when t == previousDataType => inputParameter, // 上下文 + Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举 Type t when t == typeof(IDynamicContext) => context, // 上下文 Type t when t == typeof(MethodDetails) => md, // 节点方法描述 Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类 @@ -316,9 +342,6 @@ namespace Serein.NodeFlow.Base Type t when t == typeof(nint) => inputParameter is null ? 0 : nint.Parse(inputParameter?.ToString()), Type t when t == typeof(nuint) => inputParameter is null ? 0 : nuint.Parse(inputParameter?.ToString()), - - - Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举 Type t when t.IsArray => (inputParameter as Array)?.Cast().ToList(), Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter, Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)), @@ -340,7 +363,6 @@ namespace Serein.NodeFlow.Base /// public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context,NodeModelBase nodeModel, object? newData = null) { - string guid = nodeModel.Guid; if(newData is not null) { @@ -348,79 +370,6 @@ namespace Serein.NodeFlow.Base await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点 nodeModel.FlowData = newData; // 替换数据 } - - - - //if(context.Env.CheckObjMonitorState(newData, out List exps)) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断? - //{ - // context.Env.MonitorObjectNotification(guid, newData); // 对象处于监视状态,通知UI更新数据显示 - // if (exps.Count > 0) - // { - // // 表达式环境下判断是否需要执行中断 - // bool isExpInterrupt = false; - // string? exp = ""; - // // 判断执行监视表达式,直到为 true 时退出 - // for (int i = 0; i < exps.Count && !isExpInterrupt; i++) - // { - // exp = exps[i]; - // isExpInterrupt = SereinConditionParser.To(newData, exp); - // } - - // if (isExpInterrupt) // 触发中断 - // { - // InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 - // if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass)) - // { - // context.Env.TriggerInterrupt(guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Obj); - // var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); - // await Console.Out.WriteLineAsync($"[{newData}]中断已{cancelType},开始执行后继分支"); - // } - // } - // } - - //} - - - //if (newData is not null && nodeModel.DebugSetting.InterruptExpressions.Count > 0) // 检查节点是否存在监视表达式 - //{ - // // 表达式环境下判断是否需要执行中断 - // bool isExpInterrupt = false; - // string? exp = ""; - // // 判断执行监视表达式,直到为 true 时退出 - // for (int i = 0; i < nodeModel.DebugSetting.InterruptExpressions.Count && !isExpInterrupt; i++) - // { - // exp = nodeModel.DebugSetting.InterruptExpressions[i]; - // isExpInterrupt = SereinConditionParser.To(newData, exp); - // } - - // if (isExpInterrupt) // 触发中断 - // { - // InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 - // if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass)) - // { - // context.Env.TriggerInterrupt(guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); - // var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); - // await Console.Out.WriteLineAsync($"[{nodeModel.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); - // } - - // } - //} - - - - //else if (nodeModel.DebugSetting.InterruptClass != InterruptClass.None) - //{ - // var cancelType = await nodeModel.DebugSetting.InterruptTask; - // await Console.Out.WriteLineAsync($"[{nodeModel.MethodDetails.MethodName}]中断已{(cancelType == CancelType.Manual ? "手动取消" : "自动取消")},开始执行后继分支"); - //} - - - - //if (nodeModel.DebugSetting.IsMonitorFlowData) - //{ - // // 节点是否监视了数据,如果是,调用环境接口触发其相关事件。 - // context.Env.FlowDataNotification(guid, newData); - //} } private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int type) diff --git a/NodeFlow/FlowEnvironment.cs b/NodeFlow/FlowEnvironment.cs index 2851f82..4c7410e 100644 --- a/NodeFlow/FlowEnvironment.cs +++ b/NodeFlow/FlowEnvironment.cs @@ -606,7 +606,6 @@ namespace Serein.NodeFlow /// public bool TryGetMethodDetails(string name, out MethodDetails? md) { - var isPass = false; if (!string.IsNullOrEmpty(name)) { md = MethodDetailss.FirstOrDefault(it => it.MethodName == name); @@ -617,8 +616,6 @@ namespace Serein.NodeFlow md = null; return false; } - - } /// diff --git a/NodeFlow/Tool/MethodDetailsHelper.cs b/NodeFlow/Tool/MethodDetailsHelper.cs index 0a6afad..f304788 100644 --- a/NodeFlow/Tool/MethodDetailsHelper.cs +++ b/NodeFlow/Tool/MethodDetailsHelper.cs @@ -2,6 +2,7 @@ using Serein.Library.Attributes; using Serein.Library.Core.NodeFlow; using Serein.Library.Entity; +using System; using System.Collections.Concurrent; using System.Reflection; @@ -99,26 +100,25 @@ public static class MethodDetailsHelperTmp return parameters.Select((it, index) => { Type paremType; - //var attribute = it.ParameterType.GetCustomAttribute(); - //if (attribute is not null && attribute.Enum.IsEnum) - //{ - // // 存在选择器 - // paremType = attribute.Enum; - //} - //else - //{ - // paremType = it.ParameterType; - //} - paremType = it.ParameterType; + var attribute = it.GetCustomAttribute(); + if (attribute is not null) + { + // 存在选择器 + paremType = attribute.EnumType; + } + else + { + paremType = it.ParameterType; + } string explicitTypeName = GetExplicitTypeName(paremType); var items = GetExplicitItems(paremType, explicitTypeName); if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型 return new ExplicitData { - IsExplicitData = it.HasDefaultValue, + IsExplicitData = attribute is null ? it.HasDefaultValue: true, Index = index, - // ExplicitType = it.ParameterType, ExplicitTypeName = explicitTypeName, + ExplicitType = paremType, DataType = it.ParameterType, ParameterName = it.Name, DataValue = it.HasDefaultValue ? it.DefaultValue.ToString() : "", diff --git a/README.md b/README.md index 4833faf..6015247 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ https://space.bilibili.com/33526379 # 如何加载我的DLL? -使用 **DynamicFlow** 特性标记你的类,可以参照 **MyDll** 与 **SereinWAT** 的实现。编译为 Dll文件 后,拖入到软件中即可。 +使用 **DynamicFlow** 特性标记你的类,可以参照 **Net461DllTest** 的实现。编译为 Dll文件 后,拖入到软件中即可。 # 如何让我的方法成为节点? 使用 **NodeAction** 特性标记你的方法。