上传了新的示例工程

This commit is contained in:
fengjiayi
2024-09-27 10:30:19 +08:00
parent 54c35ce445
commit f3a90df452
24 changed files with 1062 additions and 127 deletions

1
.gitignore vendored
View File

@@ -23,4 +23,3 @@ obj/
# 排除某些项目
Net461DllTest/

View File

@@ -26,7 +26,7 @@ namespace Serein.Library.Entity
///// <summary>
///// 显式类型
///// </summary>
//public Type ExplicitType { get; set; }
public Type ExplicitType { get; set; }
///// <summary>
///// 显示类型编号>
@@ -55,7 +55,7 @@ namespace Serein.Library.Entity
{
Index = Index,
IsExplicitData = IsExplicitData,
// ExplicitType = ExplicitType,
ExplicitType = ExplicitType,
ExplicitTypeName = ExplicitTypeName,
DataType = DataType,
ParameterName = ParameterName,

View File

@@ -73,6 +73,30 @@ namespace Serein.Library.Attributes
}
}
/// <summary>
/// 枚举值转换器要求枚举项标记的BindValueAttribute特性与搭配的参数类型一致否则参数不会传入
/// </summary>
[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
}
}
/// <summary>
/// 枚举值转换器
/// </summary>
//[AttributeUsage(AttributeTargets.Parameter)]
//public class EnumConvertorAttribute : Attribute
//{
// public Type Enum { get; }
// public EnumConvertorAttribute(Type @enum)
// {
// if (@enum.IsEnum)
// {
// Enum = @enum;
// }
// else
// {
// throw new ArgumentException("需要枚举类型");
// }
// }
//}
}

View File

@@ -16,6 +16,13 @@ namespace Serein.Library.Utils
return attribute != null ? (TResult)valueSelector(attribute) : default;
}
public static object GetBoundValue(Type enumType,object enumValue, Func<BindValueAttribute, object> valueSelector)
{
var fieldInfo = enumType.GetField(enumValue.ToString());
var attribute = fieldInfo.GetCustomAttribute<BindValueAttribute>();
return attribute != null ? valueSelector(attribute) : default;
}
//public static TResult GetBoundValue<TEnum, TAttribute, TResult>(TEnum enumValue,
// Func<TAttribute, TResult> valueSelector)

View File

@@ -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; }
}
}

View File

@@ -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<OrderSignal>
{
[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>(T value)
{
Console.WriteLine($"{value}");
}
public void Read<T>()
{
Console.WriteLine($"读取数据:... ");
}
public void Disconnect()
{
Console.WriteLine($"断开连接...");
}
}
}

View File

@@ -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<PlcDevice>(); // 注册Plc设备
context.Env.IOC.Register<MyData>(); // 注册数据类
context.Env.IOC.Register<WebServer>(); // 注册Web服务
// // 注册控制器
context.Env.IOC.Run<IRouter>(router => {
router.RegisterController(typeof(ApiController));
});
}
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
public void Loading(IDynamicContext context)
{
context.Env.IOC.Run<WebServer>((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<IFlipflopContext> 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
}
}

View File

@@ -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<Form> forms = new List<Form>();
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<ViewManagement>();
context.Env.IOC.Register<FromWorkBenchViewModel>();
}
[NodeAction(NodeType.Action, "打开窗体(指定枚举值)")]
public void OpenForm(IDynamicContext context, FromId fromId = FromId.None)
{
var fromType = EnumHelper.GetBoundValue<FromId, Type>(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, Type>(fromId, attr => attr.Value);
if (fromType is null) return;
ViewManagement.CloseView(fromType);
}
}
}

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E40EE629-1A38-4011-88E3-9AD036869987}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Net461DllTest</RootNamespace>
<AssemblyName>Net461DllTest</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Data\MyData.cs" />
<Compile Include="Device\PlcDevice.cs" />
<Compile Include="Flow\LogicControl.cs" />
<Compile Include="Flow\ViewLogicControl.cs" />
<Compile Include="Signal\OrderSignal.cs" />
<Compile Include="ViewModel\FromWorkBenchViewModel.cs" />
<Compile Include="View\FromWorkBenchView.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\FromWorkBenchView.Designer.cs">
<DependentUpon>FromWorkBenchView.cs</DependentUpon>
</Compile>
<Compile Include="Signal\Signal.cs" />
<Compile Include="View\TeseFormView.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\TeseFormView.Designer.cs">
<DependentUpon>TeseFormView.cs</DependentUpon>
</Compile>
<Compile Include="Web\ApiController.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\DynamicControl\SereinFlow\Library.Framework\Serein.Library.Framework.csproj">
<Project>{73B272E8-222D-4D08-A030-F1E1DB70B9D1}</Project>
<Name>Serein.Library.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\DynamicControl\SereinFlow\Library\Serein.Library.csproj">
<Project>{9FCE93C2-2278-46F3-96AB-CDAAFF27A55F}</Project>
<Name>Serein.Library</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="View\FromWorkBenchView.resx">
<DependentUpon>FromWorkBenchView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\TeseFormView.resx">
<DependentUpon>TeseFormView.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -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
}
}

View File

@@ -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
{
}

View File

@@ -0,0 +1,96 @@
namespace Net461DllTest
{
partial class FromWorkBenchView
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View File

@@ -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<OrderSignal>();
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);
}
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,60 @@
namespace Net461DllTest.View
{
partial class TeseFormView
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -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);
}
}
}

View File

@@ -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" };
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
</packages>

View File

@@ -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<EnumTypeConvertorAttribute>();
//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<object>().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
/// <param name="newData"></param>
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<string> 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)

View File

@@ -606,7 +606,6 @@ namespace Serein.NodeFlow
/// </summary>
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;
}
}
/// <summary>

View File

@@ -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<EnumConvertorAttribute>();
//if (attribute is not null && attribute.Enum.IsEnum)
//{
// // 存在选择器
// paremType = attribute.Enum;
//}
//else
//{
// paremType = it.ParameterType;
//}
paremType = it.ParameterType;
var attribute = it.GetCustomAttribute<EnumTypeConvertorAttribute>();
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() : "",

View File

@@ -12,7 +12,7 @@ https://space.bilibili.com/33526379
# 如何加载我的DLL
使用 **DynamicFlow** 特性标记你的类,可以参照 **MyDll****SereinWAT** 的实现。编译为 Dll文件 后,拖入到软件中即可。
使用 **DynamicFlow** 特性标记你的类,可以参照 **Net461DllTest** 的实现。编译为 Dll文件 后,拖入到软件中即可。
# 如何让我的方法成为节点?
使用 **NodeAction** 特性标记你的方法。