diff --git a/ImageCapture/FolderImageNode.xaml b/ImageCapture/FolderImageNode.xaml
new file mode 100644
index 0000000..e5db5c4
--- /dev/null
+++ b/ImageCapture/FolderImageNode.xaml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ImageCapture/FolderImageNode.xaml.cs b/ImageCapture/FolderImageNode.xaml.cs
new file mode 100644
index 0000000..c77ddbc
--- /dev/null
+++ b/ImageCapture/FolderImageNode.xaml.cs
@@ -0,0 +1,29 @@
+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;
+using VisionFrame.Base;
+
+namespace ImageCapture
+{
+ ///
+ /// FolderImageNode.xaml 的交互逻辑
+ ///
+ public partial class FolderImageNode : NodeBase
+ {
+ public FolderImageNode()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ImageCapture/FolderImageNodeModel.cs b/ImageCapture/FolderImageNodeModel.cs
new file mode 100644
index 0000000..69b11ed
--- /dev/null
+++ b/ImageCapture/FolderImageNodeModel.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VisionFrame.Base;
+
+namespace ImageCapture
+{
+ ///
+ /// 通信目录进行图像加载
+ ///
+ public class FolderImageNodeModel : NodeModelBase
+ {
+ public FolderImageNodeModel()
+ {
+ this.NodeName = "图像加载(F)";
+
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "图像目录",
+ ArgType = "String",
+ Direction = "输入",
+ ValueMode = 2
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "图像Handle",
+ ArgType = "IntPtr",
+ Direction = "输出"
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "测试",
+ ArgType = "IntPtr",
+ Direction = "输出",
+ ValueMode = 1
+ });
+ }
+ int index = 0;
+ public override void Execute(IFlowContext flowContext)
+ {
+ // 对接输入参数
+ if (this.Arguments[0].ArgValue == null)
+ throw new Exception("图像目录参数未配置");
+ if (this.Arguments[1].ArgValue == null)
+ throw new Exception("图像Handle参数未配置");
+
+ string folder = this.Arguments[0].ArgValue.ToString();
+
+ if (!Directory.Exists(folder))
+ throw new Exception("图像目录未找到");
+
+ string[] files = Directory.GetFiles(folder, "*.bmp");
+ if (files.Length == 0)
+ throw new Exception("未发现图像文件");
+
+ IntPtr handle = new Bitmap(files[index]).GetHbitmap();
+
+
+ // 对接输入参数 --- FlowTab中的参数名称
+ //this.Arguments[1].ArgValue
+
+ var am = flowContext.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[1].ArgValue.ToString());
+
+ if (am.ArgType != this.Arguments[1].ArgType)
+ throw new Exception("参数配置异常!类型不匹配");
+
+ am.Value = handle;
+
+ index++;
+ if (index >= files.Length)
+ index = 0;
+
+ }
+ }
+}
diff --git a/ImageCapture/HalconImageNode.xaml b/ImageCapture/HalconImageNode.xaml
new file mode 100644
index 0000000..dc84089
--- /dev/null
+++ b/ImageCapture/HalconImageNode.xaml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ImageCapture/HalconImageNode.xaml.cs b/ImageCapture/HalconImageNode.xaml.cs
new file mode 100644
index 0000000..6e9fd16
--- /dev/null
+++ b/ImageCapture/HalconImageNode.xaml.cs
@@ -0,0 +1,30 @@
+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;
+using VisionFrame.Base;
+
+namespace ImageCapture
+{
+ ///
+ /// HalconImageNode.xaml 的交互逻辑
+ ///
+ public partial class HalconImageNode : NodeBase
+ {
+ public HalconImageNode()
+ {
+ InitializeComponent();
+ }
+
+ }
+}
diff --git a/ImageCapture/HalconImageNodeModel.cs b/ImageCapture/HalconImageNodeModel.cs
new file mode 100644
index 0000000..e6c90ce
--- /dev/null
+++ b/ImageCapture/HalconImageNodeModel.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VisionFrame.Base;
+
+namespace ImageCapture
+{
+ public class HalconImageNodeModel : NodeModelBase
+ {
+ public override void Execute(IFlowContext flowContext)
+ {
+ base.Execute(flowContext);
+ }
+ }
+}
diff --git a/ImageCapture/ImageCapture.csproj b/ImageCapture/ImageCapture.csproj
new file mode 100644
index 0000000..28443d2
--- /dev/null
+++ b/ImageCapture/ImageCapture.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net9.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ImageProcess/ImageProcess.csproj b/ImageProcess/ImageProcess.csproj
new file mode 100644
index 0000000..1b5acc2
--- /dev/null
+++ b/ImageProcess/ImageProcess.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net9.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ImageProcess/ImageShowNode.xaml b/ImageProcess/ImageShowNode.xaml
new file mode 100644
index 0000000..0e02118
--- /dev/null
+++ b/ImageProcess/ImageShowNode.xaml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ImageProcess/ImageShowNode.xaml.cs b/ImageProcess/ImageShowNode.xaml.cs
new file mode 100644
index 0000000..14e8162
--- /dev/null
+++ b/ImageProcess/ImageShowNode.xaml.cs
@@ -0,0 +1,48 @@
+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;
+using VisionFrame.Base;
+
+namespace ImageProcess
+{
+ ///
+ /// ImageShowNode.xaml 的交互逻辑
+ ///
+ public partial class ImageShowNode : NodeBase
+ {
+ public ImageShowNode()
+ {
+ InitializeComponent();
+ }
+
+ //public ICommand AnchorDownCommand { get; set; }
+ //public ICommand AnchorUpCommand { get; set; }
+
+ private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ this.AnchorDownCommand?.Execute(
+ (this.DataContext as NodeModelBase).NodeId + ";" + (sender as Ellipse).Tag.ToString()
+ );
+ e.Handled = true;
+ }
+
+ private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ this.AnchorUpCommand?.Execute(
+ (this.DataContext as NodeModelBase).NodeId + ";" + (sender as Ellipse).Tag.ToString()
+ );
+ e.Handled = true;
+ }
+ }
+}
diff --git a/ImageProcess/ImageShowNodeModel.cs b/ImageProcess/ImageShowNodeModel.cs
new file mode 100644
index 0000000..a80bc80
--- /dev/null
+++ b/ImageProcess/ImageShowNodeModel.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VisionFrame.Base;
+
+namespace ImageProcess
+{
+ public class ImageShowNodeModel : NodeModelBase
+ {
+ public ImageShowNodeModel()
+ {
+ this.NodeName = "图像显示";
+
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "图像Handle",
+ ArgType = "IntPtr",
+ Direction = "输入",
+ ValueMode = 0
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "图像宽",
+ ArgType = "Int",
+ Direction = "输出",
+ ValueMode = 0
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "图像高",
+ ArgType = "Int",
+ Direction = "输出",
+ ValueMode = 0
+ });
+ }
+
+ public override void Execute(IFlowContext flowContext)
+ {
+ if (this.Arguments[0].ArgValue == null)
+ throw new Exception("图像Handle参数未配置");
+
+ var am = flowContext.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[0].ArgValue.ToString());
+
+ if (am.ArgType != this.Arguments[0].ArgType)
+ throw new Exception("参数配置异常!类型不匹配");
+
+ if (IntPtr.TryParse(am.Value.ToString(), out IntPtr img_handler))
+ {
+ //
+ //Bitmap bitmap = Bitmap.FromHbitmap(img_handler);
+
+
+ //bitmap.Width
+ //bitmap.Height
+
+ flowContext.ShowImage(img_handler);
+ }
+ else
+ throw new Exception("图像Handle数据无效");
+ }
+ }
+}
diff --git a/LogicControl/AddNode.xaml b/LogicControl/AddNode.xaml
new file mode 100644
index 0000000..b421b10
--- /dev/null
+++ b/LogicControl/AddNode.xaml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LogicControl/AddNode.xaml.cs b/LogicControl/AddNode.xaml.cs
new file mode 100644
index 0000000..fb5fc79
--- /dev/null
+++ b/LogicControl/AddNode.xaml.cs
@@ -0,0 +1,29 @@
+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;
+using VisionFrame.Base;
+
+namespace LogicControl
+{
+ ///
+ /// AddNode.xaml 的交互逻辑
+ ///
+ public partial class AddNode : NodeBase
+ {
+ public AddNode()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/LogicControl/AddNodeModel.cs b/LogicControl/AddNodeModel.cs
new file mode 100644
index 0000000..a9a705d
--- /dev/null
+++ b/LogicControl/AddNodeModel.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VisionFrame.Base;
+
+namespace LogicControl
+{
+ public class AddNodeModel : NodeModelBase
+ {
+ public AddNodeModel()
+ {
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "加数",
+ ArgType = "Any",
+ Direction = "输入"
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "累加值",
+ ArgType = "Any",
+ Direction = "输入",
+ ValueMode = 1
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "结果",
+ ArgType = "Any",
+ Direction = "输出"
+ });
+ }
+
+ public override void Execute(IFlowContext flowContext)
+ {
+ if (this.Arguments[0].ArgValue == null)
+ throw new Exception("参数未配置");
+ if (this.Arguments[1].ArgValue == null)
+ throw new Exception("参数未配置");
+ if (this.Arguments[2].ArgValue == null)
+ throw new Exception("参数未配置");
+
+
+ // 手动输入/下拉选择的差别处理
+ string org_value = this.Arguments[0].ArgValue.ToString();
+ if (this.Arguments[0].ValueMode == 0)
+ {
+ var am = flowContext.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[0].ArgValue.ToString());
+ if (am != null)
+ org_value = (am.Value ?? 0).ToString();
+ }
+ // 累加值
+ string add_value = this.Arguments[1].ArgValue.ToString();
+ if (this.Arguments[1].ValueMode == 0)
+ {
+ var am = flowContext.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[1].ArgValue.ToString());
+ if (am != null)
+ add_value = (am.Value ?? 0).ToString();
+ }
+
+ // 加法结果
+ string compare = org_value + "+" + add_value;
+ object result = new DataTable().Compute(compare, "");
+
+ var rm = flowContext.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[2].ArgValue.ToString());
+ if (rm != null)
+ rm.Value = result;
+ }
+ }
+}
diff --git a/LogicControl/DecisionNode.xaml b/LogicControl/DecisionNode.xaml
new file mode 100644
index 0000000..6990ace
--- /dev/null
+++ b/LogicControl/DecisionNode.xaml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LogicControl/DecisionNode.xaml.cs b/LogicControl/DecisionNode.xaml.cs
new file mode 100644
index 0000000..531a70f
--- /dev/null
+++ b/LogicControl/DecisionNode.xaml.cs
@@ -0,0 +1,29 @@
+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;
+using VisionFrame.Base;
+
+namespace LogicControl
+{
+ ///
+ /// DecisionNode.xaml 的交互逻辑
+ ///
+ public partial class DecisionNode : NodeBase
+ {
+ public DecisionNode()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/LogicControl/DecisionNodeModel.cs b/LogicControl/DecisionNodeModel.cs
new file mode 100644
index 0000000..fa13ab2
--- /dev/null
+++ b/LogicControl/DecisionNodeModel.cs
@@ -0,0 +1,68 @@
+
+using System.Data;
+using VisionFrame.Base;
+
+namespace LogicControl
+{
+ public class DecisionNodeModel : DecisionNodeModelBase
+ {
+ public DecisionNodeModel()
+ {
+ // ijֵ == ʲôֵ > < >= <= != ϵ
+ //
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "жĿ",
+ ArgType = "Any",
+ Direction = ""
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "жϷʽ",
+ ArgType = "String",
+ Direction = "",
+ ValueMode = 3
+ });
+ this.Arguments.Add(new VisionFrame.Base.Models.NodeArgModel
+ {
+ ArgName = "жֵ",
+ ArgType = "Any",
+ Direction = "",
+ ValueMode = 1
+ });
+ }
+ public override bool Decision(IFlowContext context)
+ {
+ //int temp = 1;
+ //return new DataTable().Compute("1<=2", "").ToString() == "True";
+
+ if (this.Arguments[0].ArgValue == null)
+ throw new Exception("жĿδ");
+ if (this.Arguments[1].ArgValue == null)
+ throw new Exception("жϷʽδ");
+ if (this.Arguments[2].ArgValue == null)
+ throw new Exception("жֵδ");
+
+ string org_value = "";
+ var am = context.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[0].ArgValue.ToString());
+ if (am != null)
+ org_value = am.Value.ToString();
+
+ string value = this.Arguments[2].ArgValue.ToString();
+ if (this.Arguments[2].ValueMode == 0)
+ {
+ am = context.ArgumentList
+ .FirstOrDefault(a => a.ArgName == this.Arguments[0].ArgValue.ToString());
+ if (am != null)
+ value = am.Value.ToString();
+ }
+
+ string compare = org_value + this.Arguments[1].ArgValue.ToString() + value;
+ // "0x5235443==32432"
+ // "1==2"
+ return new DataTable().Compute(compare, "").ToString() == "True";
+ }
+ }
+
+}
diff --git a/LogicControl/LogicControl.csproj b/LogicControl/LogicControl.csproj
new file mode 100644
index 0000000..5ce9143
--- /dev/null
+++ b/LogicControl/LogicControl.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net9.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VisionFrame.Base/Converter/BoolToBrushConverter.cs b/VisionFrame.Base/Converter/BoolToBrushConverter.cs
new file mode 100644
index 0000000..a54696f
--- /dev/null
+++ b/VisionFrame.Base/Converter/BoolToBrushConverter.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using System.Windows.Media;
+
+namespace VisionFrame.Base.Converter
+{
+ public class BoolToBrushConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (bool.Parse(value.ToString()))
+ return Brushes.LawnGreen;
+ else return Brushes.ForestGreen;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+ }
+}
diff --git a/VisionFrame.Base/Converter/LocationConverter.cs b/VisionFrame.Base/Converter/LocationConverter.cs
new file mode 100644
index 0000000..93029a8
--- /dev/null
+++ b/VisionFrame.Base/Converter/LocationConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+
+namespace VisionFrame.Base.Converter
+{
+ public class LocationConverter : IMultiValueConverter
+ {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ double.TryParse(values[0].ToString(), out double w);
+ double.TryParse(values[1].ToString(), out double h);
+
+ double w1 = w * 2;
+ double h1 = h * 2;
+
+ return new Thickness((w - w1) / 2, (h - h1) / 2, 0, 0);
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+ }
+}
diff --git a/VisionFrame.Base/Converter/NodeJsonConverter.cs b/VisionFrame.Base/Converter/NodeJsonConverter.cs
new file mode 100644
index 0000000..5cfbf64
--- /dev/null
+++ b/VisionFrame.Base/Converter/NodeJsonConverter.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Base.Converter
+{
+ public class NodeJsonConverter : JsonConverter
+ {
+ class TypeInfoTemp
+ {
+ public string TypeInfo { get; set; }
+ }
+ // 字符串到对象实例的转换过程
+ public override NodeModelBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ string json = reader.GetString();// 包含程序信息
+
+ TypeInfoTemp ti = System.Text.Json.JsonSerializer.Deserialize(json);
+ string[] type_info = ti.TypeInfo.Split(";");
+ Assembly assembly = Assembly.GetEntryAssembly();// 取当前执行的程序集
+ if (assembly.GetName().Name != type_info[0])
+ {
+ assembly = Assembly.LoadFile(Environment.CurrentDirectory + "/Activities/" + type_info[0] + ".dll");
+ }
+ // 最终要执的节点Model
+ Type type = assembly.GetType(type_info[1]);
+
+ NodeModelBase node = (NodeModelBase)System.Text.Json.JsonSerializer.Deserialize(json, type);
+ return node;
+ }
+
+ // 对象到Json字符串的转换过程
+ public override void Write(Utf8JsonWriter writer, NodeModelBase value, JsonSerializerOptions options)
+ {
+ // value--->Json
+ // writer--写
+ // 准备类型的记录
+ string type = value.GetType().FullName;
+ string assembly = value.GetType().Assembly.GetName().Name;
+ value.TypeInfo = assembly + ";" + type;
+
+ // 如果直接放value 会循环触发这个方法
+ // 用一个动态变量接收,
+ dynamic temp = value;
+ string json = System.Text.Json.JsonSerializer.Serialize(temp);
+ writer.WriteStringValue(json);
+ }
+ }
+}
diff --git a/VisionFrame.Base/Converter/SizeConverter.cs b/VisionFrame.Base/Converter/SizeConverter.cs
new file mode 100644
index 0000000..26a6981
--- /dev/null
+++ b/VisionFrame.Base/Converter/SizeConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace VisionFrame.Base.Converter
+{
+ public class SizeConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ double r = 2;
+ if (parameter != null)
+ {
+ double.TryParse(parameter.ToString(), out r);
+ }
+ // 实际的宽高值
+ // 将这个值乘2返回出去
+ if (value != null && double.TryParse(value.ToString(), out double new_value))
+ return new_value * r;
+
+ return value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+ }
+}
diff --git a/VisionFrame.Base/Converter/StringToNodeConverter.cs b/VisionFrame.Base/Converter/StringToNodeConverter.cs
new file mode 100644
index 0000000..61ab587
--- /dev/null
+++ b/VisionFrame.Base/Converter/StringToNodeConverter.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace VisionFrame.Base.Converter
+{
+ public class StringToNodeConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ string[] _info = value.ToString().Split(";");
+
+ Assembly assembly = Assembly.GetEntryAssembly();
+ Type type = assembly.GetType(_info[0]);
+ if (_info.Length == 2)
+ {
+ assembly =
+ Assembly.LoadFile(Environment.CurrentDirectory + "/Activities/" + _info[0] + ".dll");
+ if (assembly == null) return null;
+
+ type = assembly.GetType(_info[1]);
+ if (type == null) return null;
+ }
+
+ object instance = Activator.CreateInstance(type);
+
+ Binding binding = new Binding("DataContext.AnchorDownCommand");
+ binding.RelativeSource = new RelativeSource() { AncestorType = typeof(ItemsControl) };
+ (instance as NodeBase).SetBinding(NodeBase.AnchorDownCommandProperty, binding);
+
+
+ binding = new Binding("DataContext.AnchorUpCommand");
+ binding.RelativeSource = new RelativeSource() { AncestorType = typeof(ItemsControl) };
+ (instance as NodeBase).SetBinding(NodeBase.AnchorUpCommandProperty, binding);
+
+ return instance;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+ }
+}
diff --git a/VisionFrame.Base/DecisionNodeModelBase.cs b/VisionFrame.Base/DecisionNodeModelBase.cs
new file mode 100644
index 0000000..62746a2
--- /dev/null
+++ b/VisionFrame.Base/DecisionNodeModelBase.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Base
+{
+ public class DecisionNodeModelBase : NodeModelBase
+ {
+ public virtual bool Decision(IFlowContext context) { return false; }
+ }
+}
diff --git a/VisionFrame.Base/IFlowContext.cs b/VisionFrame.Base/IFlowContext.cs
new file mode 100644
index 0000000..de42e28
--- /dev/null
+++ b/VisionFrame.Base/IFlowContext.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VisionFrame.Base.Models;
+
+namespace VisionFrame.Base
+{
+ public interface IFlowContext
+ {
+ public ObservableCollection ArgumentList { get; set; }
+
+ void ShowImage(IntPtr imagePtr);
+ }
+}
diff --git a/VisionFrame.Base/Models/FlowArgModel.cs b/VisionFrame.Base/Models/FlowArgModel.cs
new file mode 100644
index 0000000..493c0a7
--- /dev/null
+++ b/VisionFrame.Base/Models/FlowArgModel.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Base.Models
+{
+ public class FlowArgModel
+ {
+ public string ArgName { get; set; }
+ public string ArgType { get; set; }
+ public object Value { get; set; }
+ }
+}
diff --git a/VisionFrame.Base/Models/NodeArgModel.cs b/VisionFrame.Base/Models/NodeArgModel.cs
new file mode 100644
index 0000000..dbb3698
--- /dev/null
+++ b/VisionFrame.Base/Models/NodeArgModel.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Base.Models
+{
+ public class NodeArgModel : INotifyPropertyChanged
+ {
+ public string ArgName { get; set; }
+ public string ArgType { get; set; }
+ public string Direction { get; set; }
+
+ private object _argValue;
+
+ public object ArgValue
+ {
+ get { return _argValue; }
+ set
+ {
+ _argValue = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ArgValue"));
+ }
+ }
+
+
+
+ // 数据的获取方式
+ // 0:选择流程参数,下拉列表
+ // 1:输入框
+ // 2:选择目录
+ public int ValueMode { get; set; } = 0;
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+ }
+}
diff --git a/VisionFrame.Base/NodeBase.cs b/VisionFrame.Base/NodeBase.cs
new file mode 100644
index 0000000..8d7bb85
--- /dev/null
+++ b/VisionFrame.Base/NodeBase.cs
@@ -0,0 +1,55 @@
+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.Input;
+using System.Windows.Shapes;
+
+namespace VisionFrame.Base
+{
+ public class NodeBase : UserControl
+ {
+ //public ICommand AnchorDownCommand { get; set; }
+ //public ICommand AnchorUpCommand { get; set; }
+
+ public ICommand AnchorDownCommand
+ {
+ get { return (ICommand)GetValue(AnchorDownCommandProperty); }
+ set { SetValue(AnchorDownCommandProperty, value); }
+ }
+ public static readonly DependencyProperty AnchorDownCommandProperty =
+ DependencyProperty.Register("AnchorDownCommand",
+ typeof(ICommand), typeof(NodeBase), new PropertyMetadata(null));
+
+
+ public ICommand AnchorUpCommand
+ {
+ get { return (ICommand)GetValue(AnchorUpCommandProperty); }
+ set { SetValue(AnchorUpCommandProperty, value); }
+ }
+ public static readonly DependencyProperty AnchorUpCommandProperty =
+ DependencyProperty.Register("AnchorUpCommand",
+ typeof(ICommand), typeof(NodeBase), new PropertyMetadata(null));
+
+
+
+ public void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ this.AnchorDownCommand?.Execute(
+ (this.DataContext as NodeModelBase).NodeId + ";" + (sender as Ellipse).Tag.ToString()
+ );
+ e.Handled = true;
+ }
+
+ public void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ this.AnchorUpCommand?.Execute(
+ (this.DataContext as NodeModelBase).NodeId + ";" + (sender as Ellipse).Tag.ToString()
+ );
+ e.Handled = true;
+ }
+ }
+}
diff --git a/VisionFrame.Base/NodeModelBase.cs b/VisionFrame.Base/NodeModelBase.cs
new file mode 100644
index 0000000..b6ffed6
--- /dev/null
+++ b/VisionFrame.Base/NodeModelBase.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using VisionFrame.Base.Converter;
+using VisionFrame.Base.Models;
+
+namespace VisionFrame.Base
+{
+ // 解决序列化时多态对象的类型区分
+ //[JsonPolymorphic(UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
+ //[JsonDerivedType(typeof(DecisionNodeModelBase), typeDiscriminator: "decision")]
+
+ // 对这类对象进么Json序列的时候 会执行Converter中的相关方法
+ [JsonConverter(typeof(NodeJsonConverter))]
+ public class NodeModelBase : INotifyPropertyChanged
+ {
+ public string TypeInfo { get; set; }
+ public List FlowLevel { get; set; } = new List();
+ // 每个节点实例 唯一编号
+ public string NodeId { get; set; }
+ public NodeModelBase()
+ {
+ NodeId = Guid.NewGuid().ToString();
+ }
+
+ public string NodeName { get; set; }
+
+ public string TargetNodeObject { get; set; }
+
+ public bool IsStart { get; set; } = false;
+
+ public bool IsDecision { get; set; } = false;
+
+ private double _x;
+
+ public double X
+ {
+ get { return _x; }
+ set
+ {
+ _x = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(X)));
+ }
+ }
+ private double _y;
+
+ public double Y
+ {
+ get { return _y; }
+ set
+ {
+ _y = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Y)));
+ }
+ }
+
+ public double W { get; set; }
+ public double H { get; set; }
+
+
+ private bool _isSelected = false;
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public bool IsSelected
+ {
+ get { return _isSelected; }
+ set
+ {
+ _isSelected = value;
+
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
+ }
+ }
+ private bool _isShowProperties;
+
+ public bool IsShowProperties
+ {
+ get { return _isShowProperties; }
+ set
+ {
+ if (!IsSelected) return;
+
+ _isShowProperties = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsShowProperties)));
+ }
+ }
+
+
+ private bool _isRunning;
+
+ public bool IsRunning
+ {
+ get { return _isRunning; }
+ set
+ {
+ _isRunning = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRunning)));
+ }
+ }
+
+ private long _duration;
+
+ public long Duration
+ {
+ get { return _duration; }
+ set
+ {
+ _duration = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Duration)));
+ }
+ }
+
+
+ public List Arguments { get; set; } = new List();
+
+ public bool ShowAnchorT { get; set; } = true;
+ public bool ShowAnchorB { get; set; } = true;
+ public bool ShowAnchorL { get; set; } = true;
+ public bool ShowAnchorR { get; set; } = true;
+
+ public void SetAnchorShow(string anchor, bool show)
+ {
+ PropertyInfo pi = this.GetType().GetProperty("ShowAnchor" + anchor);
+ if (pi != null)
+ {
+ pi.SetValue(this, show);
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ShowAnchor" + anchor));
+ }
+ }
+
+
+
+ //public List<>
+ public virtual void Execute(IFlowContext flowContext) { }
+ }
+}
diff --git a/VisionFrame.Base/TemplateSelector/NodeArgTemplateSelector.cs b/VisionFrame.Base/TemplateSelector/NodeArgTemplateSelector.cs
new file mode 100644
index 0000000..a6d53ab
--- /dev/null
+++ b/VisionFrame.Base/TemplateSelector/NodeArgTemplateSelector.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using VisionFrame.Base.Models;
+
+namespace VisionFrame.Base.TemplateSelector
+{
+ public class NodeArgTemplateSelector : DataTemplateSelector
+ {
+ public DataTemplate DropdownTempalte { get; set; }
+ public DataTemplate TextBoxTempalte { get; set; }
+ public DataTemplate SelectFolderTempalte { get; set; }
+ public DataTemplate CompareOpratorTempalte { get; set; }
+
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ var model = item as NodeArgModel;
+ if (model.ValueMode == 0)
+ return DropdownTempalte;
+ else if (model.ValueMode == 1)
+ return TextBoxTempalte;
+ else if (model.ValueMode == 2)
+ return SelectFolderTempalte;
+ else if (model.ValueMode == 3)
+ return CompareOpratorTempalte;
+
+ return null;
+ }
+ }
+}
diff --git a/VisionFrame.Base/VisionFrame.Base.csproj b/VisionFrame.Base/VisionFrame.Base.csproj
new file mode 100644
index 0000000..ac07c5b
--- /dev/null
+++ b/VisionFrame.Base/VisionFrame.Base.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net9.0-windows
+ enable
+ true
+ enable
+
+
+
diff --git a/VisionFrame.sln b/VisionFrame.sln
new file mode 100644
index 0000000..d0480bd
--- /dev/null
+++ b/VisionFrame.sln
@@ -0,0 +1,51 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35527.113
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionFrame", "VisionFrame\VisionFrame.csproj", "{35EB08A7-6C9B-495F-97B1-031CF800D3F6}"
+ ProjectSection(ProjectDependencies) = postProject
+ {103F3E6D-0F03-4AD7-A971-6296FB243500} = {103F3E6D-0F03-4AD7-A971-6296FB243500}
+ {193F9B5C-0452-44B9-9F36-C75D44A52735} = {193F9B5C-0452-44B9-9F36-C75D44A52735}
+ {19BEA527-EDC6-4A18-A361-017EE14784C1} = {19BEA527-EDC6-4A18-A361-017EE14784C1}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageCapture", "ImageCapture\ImageCapture.csproj", "{19BEA527-EDC6-4A18-A361-017EE14784C1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionFrame.Base", "VisionFrame.Base\VisionFrame.Base.csproj", "{11D768D7-9668-4916-835A-1CCAA7F38AED}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcess", "ImageProcess\ImageProcess.csproj", "{193F9B5C-0452-44B9-9F36-C75D44A52735}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LogicControl", "LogicControl\LogicControl.csproj", "{103F3E6D-0F03-4AD7-A971-6296FB243500}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {35EB08A7-6C9B-495F-97B1-031CF800D3F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {35EB08A7-6C9B-495F-97B1-031CF800D3F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {35EB08A7-6C9B-495F-97B1-031CF800D3F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {35EB08A7-6C9B-495F-97B1-031CF800D3F6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {19BEA527-EDC6-4A18-A361-017EE14784C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {19BEA527-EDC6-4A18-A361-017EE14784C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {19BEA527-EDC6-4A18-A361-017EE14784C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {19BEA527-EDC6-4A18-A361-017EE14784C1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11D768D7-9668-4916-835A-1CCAA7F38AED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11D768D7-9668-4916-835A-1CCAA7F38AED}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11D768D7-9668-4916-835A-1CCAA7F38AED}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11D768D7-9668-4916-835A-1CCAA7F38AED}.Release|Any CPU.Build.0 = Release|Any CPU
+ {193F9B5C-0452-44B9-9F36-C75D44A52735}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {193F9B5C-0452-44B9-9F36-C75D44A52735}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {193F9B5C-0452-44B9-9F36-C75D44A52735}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {193F9B5C-0452-44B9-9F36-C75D44A52735}.Release|Any CPU.Build.0 = Release|Any CPU
+ {103F3E6D-0F03-4AD7-A971-6296FB243500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {103F3E6D-0F03-4AD7-A971-6296FB243500}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {103F3E6D-0F03-4AD7-A971-6296FB243500}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {103F3E6D-0F03-4AD7-A971-6296FB243500}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/VisionFrame/App.xaml b/VisionFrame/App.xaml
new file mode 100644
index 0000000..e30efc4
--- /dev/null
+++ b/VisionFrame/App.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+ ./Assets/Fonts/#iconfont
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VisionFrame/App.xaml.cs b/VisionFrame/App.xaml.cs
new file mode 100644
index 0000000..f077727
--- /dev/null
+++ b/VisionFrame/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace VisionFrame
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/VisionFrame/AssemblyInfo.cs b/VisionFrame/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/VisionFrame/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/VisionFrame/Assets/Fonts/iconfont.ttf b/VisionFrame/Assets/Fonts/iconfont.ttf
new file mode 100644
index 0000000..c290065
Binary files /dev/null and b/VisionFrame/Assets/Fonts/iconfont.ttf differ
diff --git a/VisionFrame/Assets/Images/Logo_64.png b/VisionFrame/Assets/Images/Logo_64.png
new file mode 100644
index 0000000..4ede25a
Binary files /dev/null and b/VisionFrame/Assets/Images/Logo_64.png differ
diff --git a/VisionFrame/Assets/Styles/ButtonStyles.xaml b/VisionFrame/Assets/Styles/ButtonStyles.xaml
new file mode 100644
index 0000000..e8c04fe
--- /dev/null
+++ b/VisionFrame/Assets/Styles/ButtonStyles.xaml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VisionFrame/Assets/Styles/ComboBoxStyles.xaml b/VisionFrame/Assets/Styles/ComboBoxStyles.xaml
new file mode 100644
index 0000000..bba5010
--- /dev/null
+++ b/VisionFrame/Assets/Styles/ComboBoxStyles.xaml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VisionFrame/Assets/Styles/TabControlStyles.xaml b/VisionFrame/Assets/Styles/TabControlStyles.xaml
new file mode 100644
index 0000000..cd6e9cb
--- /dev/null
+++ b/VisionFrame/Assets/Styles/TabControlStyles.xaml
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VisionFrame/Models/CatalogModel.cs b/VisionFrame/Models/CatalogModel.cs
new file mode 100644
index 0000000..2430ec1
--- /dev/null
+++ b/VisionFrame/Models/CatalogModel.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Models
+{
+ internal class CatalogModel
+ {
+ public bool IsSelected { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public string Icon { get; set; }
+
+ public List Components { get; set; }
+ }
+}
diff --git a/VisionFrame/Models/ComponentModel.cs b/VisionFrame/Models/ComponentModel.cs
new file mode 100644
index 0000000..c064468
--- /dev/null
+++ b/VisionFrame/Models/ComponentModel.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Models
+{
+ internal class ComponentModel
+ {
+ public string Icon { get; set; }
+ public string Name { get; set; }
+ public string TargetNode { get; set; }
+ public string TargetModel { get; set; }
+
+ public double W { get; set; }
+ public double H { get; set; }
+ }
+}
diff --git a/VisionFrame/Models/FlowArgModel.cs b/VisionFrame/Models/FlowArgModel.cs
new file mode 100644
index 0000000..249501b
--- /dev/null
+++ b/VisionFrame/Models/FlowArgModel.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Zhaoxi.VisionFrame.Models
+{
+ public class FlowArgModel
+ {
+ public string ArgName { get; set; }
+ public string ArgType { get; set; }
+ public object Value { get; set; }
+ }
+}
diff --git a/VisionFrame/Models/LinkModel.cs b/VisionFrame/Models/LinkModel.cs
new file mode 100644
index 0000000..5a9847e
--- /dev/null
+++ b/VisionFrame/Models/LinkModel.cs
@@ -0,0 +1,56 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Models
+{
+ public class LinkModel : ObservableObject
+ {
+ public string StartAnchor { get; set; }
+ private string _endAnchor;
+
+ public string EndAnchor
+ {
+ get { return _endAnchor; }
+ set { SetProperty(ref _endAnchor, value); }
+ }
+
+
+ public string StartId { get; set; }
+ public string EndId { get; set; }
+
+ private double _startX;
+
+ public double StartX
+ {
+ get { return _startX; }
+ set { SetProperty(ref _startX, value); }
+ }
+ private double _startY;
+
+ public double StartY
+ {
+ get { return _startY; }
+ set { SetProperty(ref _startY, value); }
+ }
+ private double _endX;
+
+ public double EndX
+ {
+ get { return _endX; }
+ set { SetProperty(ref _endX, value); }
+ }
+ private double _endY;
+
+ public double EndY
+ {
+ get { return _endY; }
+ set { SetProperty(ref _endY, value); }
+ }
+
+ public string Condition { get; set; }
+ }
+}
diff --git a/VisionFrame/Models/LogModel.cs b/VisionFrame/Models/LogModel.cs
new file mode 100644
index 0000000..96ddc2d
--- /dev/null
+++ b/VisionFrame/Models/LogModel.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VisionFrame.Models
+{
+ public class LogModel
+ {
+ public DateTime Time { get; set; }
+ public string NodeName { get; set; }
+ public string LogMessage { get; set; }
+ }
+}
diff --git a/VisionFrame/Models/NodeModel.cs b/VisionFrame/Models/NodeModel.cs
new file mode 100644
index 0000000..69f6dda
--- /dev/null
+++ b/VisionFrame/Models/NodeModel.cs
@@ -0,0 +1,64 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Zhaoxi.VisionFrame.Base;
+
+namespace Zhaoxi.VisionFrame.Models
+{
+ public class NodeModel : ObservableObject
+ {
+ // 每个节点实例 唯一编号
+ public string NodeId { get; } = Guid.NewGuid().ToString();
+
+ public object TargetNodeObject { get; set; }
+
+
+ private double _x;
+
+ public double X
+ {
+ get { return _x; }
+ set { SetProperty(ref _x, value); }
+ }
+ private double _y;
+
+ public double Y
+ {
+ get { return _y; }
+ set { SetProperty(ref _y, value); }
+ }
+
+ public double W { get; set; }
+ public double H { get; set; }
+
+
+ private bool _isSelected = false;
+
+ public bool IsSelected
+ {
+ get { return _isSelected; }
+ set { SetProperty(ref _isSelected, value); }
+ }
+
+
+ public bool ShowAnchorT { get; set; } = true;
+ public bool ShowAnchorB { get; set; } = true;
+ public bool ShowAnchorL { get; set; } = true;
+ public bool ShowAnchorR { get; set; } = true;
+
+ public void SetAnchorShow(string anchor, bool show)
+ {
+ PropertyInfo pi = this.GetType().GetProperty("ShowAnchor" + anchor);
+ if (pi != null)
+ {
+ pi.SetValue(this, show);
+ this.OnPropertyChanged("ShowAnchor" + anchor);
+ }
+ }
+ }
+}
diff --git a/VisionFrame/Nodes/LineNode.xaml b/VisionFrame/Nodes/LineNode.xaml
new file mode 100644
index 0000000..b10f496
--- /dev/null
+++ b/VisionFrame/Nodes/LineNode.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
diff --git a/VisionFrame/Nodes/LineNode.xaml.cs b/VisionFrame/Nodes/LineNode.xaml.cs
new file mode 100644
index 0000000..740da45
--- /dev/null
+++ b/VisionFrame/Nodes/LineNode.xaml.cs
@@ -0,0 +1,676 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+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 VisionFrame.Nodes
+{
+ ///
+ /// LineNode.xaml 的交互逻辑
+ ///
+ public partial class LineNode : UserControl, INotifyPropertyChanged
+ {
+ public double X1
+ {
+ get { return (double)GetValue(X1Property); }
+ set { SetValue(X1Property, value); }
+ }
+ public static readonly DependencyProperty X1Property =
+ DependencyProperty.Register("X1", typeof(double),
+ typeof(LineNode),
+ new PropertyMetadata(0.0, OnPropertyChanged));
+
+ public double Y1
+ {
+ get { return (double)GetValue(Y1Property); }
+ set { SetValue(Y1Property, value); }
+ }
+ public static readonly DependencyProperty Y1Property =
+ DependencyProperty.Register("Y1", typeof(double),
+ typeof(LineNode),
+ new PropertyMetadata(0.0, OnPropertyChanged));
+
+
+
+ public double X2
+ {
+ get { return (double)GetValue(X2Property); }
+ set { SetValue(X2Property, value); }
+ }
+ public static readonly DependencyProperty X2Property =
+ DependencyProperty.Register("X2", typeof(double),
+ typeof(LineNode),
+ new PropertyMetadata(0.0, OnPropertyChanged));
+
+ public double Y2
+ {
+ get { return (double)GetValue(Y2Property); }
+ set { SetValue(Y2Property, value); }
+ }
+ public static readonly DependencyProperty Y2Property =
+ DependencyProperty.Register("Y2", typeof(double),
+ typeof(LineNode),
+ new PropertyMetadata(0.0, OnPropertyChanged));
+
+
+ private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ (d as LineNode).InvalidateVisual();
+ }
+
+ public string StartAnchor
+ {
+ get { return (string)GetValue(StartAnchorProperty); }
+ set { SetValue(StartAnchorProperty, value); }
+ }
+ public static readonly DependencyProperty StartAnchorProperty =
+ DependencyProperty.Register("StartAnchor", typeof(string),
+ typeof(LineNode),
+ new PropertyMetadata(""));
+
+ public string EndAnchor
+ {
+ get { return (string)GetValue(EndAnchorProperty); }
+ set { SetValue(EndAnchorProperty, value); }
+ }
+ public static readonly DependencyProperty EndAnchorProperty =
+ DependencyProperty.Register("EndAnchor", typeof(string),
+ typeof(LineNode),
+ new PropertyMetadata(""));
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public Thickness CancelLocation { get; set; }
+
+
+
+ public LineNode()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ List points = new List();
+
+ // 起始与终止锚点坐标
+ Point start = new Point(X1, Y1);
+ Point end = new Point(X2, Y2);
+ //
+ if (EndAnchor == "N")
+ {
+ if (X2 < X1)
+ end += new Vector(2, 0);
+ else if (X2 > X1)
+ end += new Vector(-2, 0);
+
+ if (Y2 > Y1)
+ end += new Vector(0, -2);
+ else if (Y2 < Y1)
+ end += new Vector(0, 2);
+ }
+
+ // 起始延伸坐标
+ Point start_extend = new Point(X1, Y1);
+ if (StartAnchor == "T")
+ start_extend = new Point(X1, Y1 - 20);
+ else if (StartAnchor == "B")
+ start_extend = new Point(X1, Y1 + 20);
+ else if (StartAnchor == "L")
+ start_extend = new Point(X1 - 20, Y1);
+ else if (StartAnchor == "R")
+ start_extend = new Point(X1 + 20, Y1);
+
+ // 终点延伸坐标
+ Point end_extend = new Point(end.X, end.Y);
+ if (EndAnchor == "T")
+ end_extend = new Point(end.X, end.Y - 20);
+ else if (EndAnchor == "B")
+ end_extend = new Point(end.X, end.Y + 20);
+ else if (EndAnchor == "L")
+ end_extend = new Point(end.X - 20, end.Y);
+ else if (EndAnchor == "R")
+ end_extend = new Point(end.X + 20, end.Y);
+
+ // 添加第一点
+ points.Add(start);
+
+ // 添加延伸点(针对第一点),起点是哪个锚点
+ points.Add(start_extend);
+
+
+ // 添加动态点
+ // 锚点名称 TBLR 反射
+ MethodInfo mi = this.GetType().GetMethod(StartAnchor + "2" + EndAnchor,
+ BindingFlags.NonPublic | BindingFlags.Instance);
+ if (mi == null) return;
+ Point[] ps = (Point[])mi.Invoke(this, new object[] {
+ start_extend,
+ end_extend
+ });
+ points.AddRange(ps);
+ //points.AddRange(this.B2T(new Point(X1, Y1 + 20), new Point(X2, Y2 - 20)));
+
+
+ // 添加一个延伸点(针对最终点),终点是哪个锚点
+ points.Add(end_extend);
+
+ // 添加最终点
+ points.Add(end);
+
+
+ //StreamGeometry geometry = new StreamGeometry();
+ //using (StreamGeometryContext ctx = geometry.Open())
+ //{
+ // Point first = points.FirstOrDefault();
+ // ctx.BeginFigure(first, false, false);
+ // ctx.PolyLineTo(points, true, true);
+ //}
+
+ var geometry = GetBrokenGeometry(points.ToArray(), false, false);
+
+ if (EndAnchor != "N")
+ {
+ //points[points.Count - 2];// 终延
+ //points[points.Count - 1];// 终
+ Point[] arrow_points = this.GetArrowPoints(points[points.Count - 2], points[points.Count - 1]);
+ var arrow_geo = this.GetBrokenGeometry(arrow_points, true, true);
+ PathGeometry path = PathGeometry.CreateFromGeometry(geometry);
+ // 整合
+ path.AddGeometry(arrow_geo);
+ path.Freeze();
+
+ geometry = path;
+ }
+
+
+ //drawingContext.DrawLine(new Pen(Brushes.Red, 2), new Point(X1, Y1), new Point(X2, Y2));
+ drawingContext.DrawGeometry(
+ Brushes.Orange,
+ new Pen(Brushes.Orange, 2),
+ geometry);
+
+
+ if (ps.Length == 2)
+ {
+ var p = ps[0] + (ps[1] - ps[0]) / 2;
+ CancelLocation = new Thickness(p.X - 7, p.Y - 7, 0, 0);
+ }
+ else if (ps.Length == 1)
+ CancelLocation = new Thickness(ps[0].X - 7, ps[0].Y - 7, 0, 0);
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CancelLocation)));
+ }
+
+ private Geometry GetBrokenGeometry(Point[] points, bool isFill, bool isClose)
+ {
+ StreamGeometry geometry = new StreamGeometry();
+ using (StreamGeometryContext ctx = geometry.Open())
+ {
+ Point first = points.FirstOrDefault();
+ ctx.BeginFigure(first, isFill, isClose);
+ ctx.PolyLineTo(points, true, true);
+ }
+ return geometry;
+ }
+ private Point[] GetArrowPoints(Point start, Point end)
+ {
+ Vector vec = start - end;
+ // 规范化 单位化
+ vec.Normalize();
+ vec *= 8;
+
+ Matrix matrix = new Matrix();
+ matrix.Rotate(20);
+ Point p1 = end + vec * matrix;
+ matrix.Rotate(-40);
+ Point p2 = end + vec * matrix;
+
+ return new Point[] { p1, end, p2 };
+ }
+
+ #region 下锚点
+ // 下对上
+ private Point[] B2T(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (end.Y > start.Y)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, start.Y + cy));
+ }
+ else if (end.Y < start.Y)
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(start.X + cx, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // 下对左
+ private Point[] B2L(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else if (start.Y < end.Y)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // 下对右
+ private Point[] B2R(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+
+ if (start.X < end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else if (start.Y < end.Y)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // 下对下
+ private Point[] B2B(Point start, Point end)
+ {
+ double cy = end.Y - start.Y;
+
+ List ps = new List();
+ if (start.Y > end.Y)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else if (start.Y < end.Y)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // 下对空
+ private Point[] B2N(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.Y > end.Y)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+ #endregion
+
+ #region 右锚点
+ // From右锚点To上锚点
+ private Point[] R2T(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+
+ List ps = new List();
+
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else if (start.Y < end.Y)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // From右锚点To左锚点
+ private Point[] R2L(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, end.Y - cy));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // From右锚点To右锚点
+ private Point[] R2R(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // From右锚点To下锚点
+ private Point[] R2B(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+
+ if (start.X > end.X && start.Y > end.Y)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, start.Y + cy));
+ }
+ else if (start.X < end.X && start.Y < end.Y)
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ else if (start.X < end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // Fromd右锚点To NULL
+ private Point[] R2N(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+
+ return ps.ToArray();
+ }
+ #endregion
+
+ #region 左锚点
+ // From左锚点To上锚点
+ private Point[] L2T(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.X > end.X && start.Y > end.Y)
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ else if (start.X < end.X && start.Y < end.Y)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, end.Y - cy));
+ }
+ else if (start.X < end.X)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // From左锚点To左锚点
+ private Point[] L2L(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // From左锚点To右锚点
+ private Point[] L2R(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.X < end.X)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, start.Y + cy));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(start.X + cx, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+
+ // From左锚点To下锚点
+ private Point[] L2B(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.X < end.X && start.Y > end.Y)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, end.Y - cy));
+ }
+ else if (start.X > end.X && start.Y < end.Y)
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ else if (start.X > end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // Fromd左锚点To NULL
+ private Point[] L2N(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.X > end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+
+ return ps.ToArray();
+ }
+ #endregion
+
+ #region 上锚点
+ // From上锚点To上锚点
+ private Point[] T2T(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.Y > end.Y)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // From上锚点To下锚点
+ private Point[] T2B(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.Y > end.Y)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, end.Y - cy));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // From上锚点To左锚点
+ private Point[] T2L(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.Y > end.Y && start.X > end.X)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, end.Y - cy));
+ }
+ else if (start.Y < end.Y && start.X < end.X)
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ else if (start.X < end.X)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // From上锚点To右锚点
+ private Point[] T2R(Point start, Point end)
+ {
+ double cx = (end.X - start.X) / 2;
+ double cy = (end.Y - start.Y) / 2;
+
+ List ps = new List();
+ if (start.Y > end.Y && start.X > end.X)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else if (start.Y < end.Y && start.X < end.X)
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+ else if (start.X < end.X)
+ {
+ ps.Add(new Point(start.X, start.Y + cy));
+ ps.Add(new Point(end.X, end.Y - cy));
+ }
+ else
+ {
+ ps.Add(new Point(start.X + cx, start.Y));
+ ps.Add(new Point(end.X - cx, end.Y));
+ }
+ return ps.ToArray();
+ }
+
+ // Fromd上锚点To NULL
+ private Point[] T2N(Point start, Point end)
+ {
+ List ps = new List();
+ if (start.Y > end.Y)
+ {
+ ps.Add(new Point(start.X, end.Y));
+ }
+ else
+ {
+ ps.Add(new Point(end.X, start.Y));
+ }
+
+ return ps.ToArray();
+ }
+ #endregion
+ }
+}
diff --git a/VisionFrame/Nodes/StartNode.xaml b/VisionFrame/Nodes/StartNode.xaml
new file mode 100644
index 0000000..d2a4af6
--- /dev/null
+++ b/VisionFrame/Nodes/StartNode.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VisionFrame/Nodes/StartNode.xaml.cs b/VisionFrame/Nodes/StartNode.xaml.cs
new file mode 100644
index 0000000..31ca1d1
--- /dev/null
+++ b/VisionFrame/Nodes/StartNode.xaml.cs
@@ -0,0 +1,40 @@
+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;
+using VisionFrame.Base;
+using VisionFrame.Models;
+
+namespace VisionFrame.Nodes
+{
+ ///
+ /// StartNode.xaml 的交互逻辑
+ ///
+ public partial class StartNode : NodeBase
+ {
+ public StartNode()
+ {
+ InitializeComponent();
+ }
+
+ //public ICommand AnchorDownCommand { get; set; }
+ //public ICommand AnchorUpCommand { get; set; }
+
+ private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ // T L R B
+ AnchorDownCommand?.Execute((this.DataContext as NodeModelBase).NodeId + ";B");
+ e.Handled = true;
+ }
+ }
+}
diff --git a/VisionFrame/Nodes/StartNodeModel.cs b/VisionFrame/Nodes/StartNodeModel.cs
new file mode 100644
index 0000000..52c1b92
--- /dev/null
+++ b/VisionFrame/Nodes/StartNodeModel.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VisionFrame.Base;
+
+namespace VisionFrame.Nodes
+{
+ public class StartNodeModel : NodeModelBase
+ {
+ }
+}
diff --git a/VisionFrame/ViewModels/FlowTabViewModel.cs b/VisionFrame/ViewModels/FlowTabViewModel.cs
new file mode 100644
index 0000000..b03f31c
--- /dev/null
+++ b/VisionFrame/ViewModels/FlowTabViewModel.cs
@@ -0,0 +1,1113 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Media.Media3D;
+using System.Xml.Linq;
+using VisionFrame.Base;
+using VisionFrame.Base.Models;
+using VisionFrame.Models;
+using VisionFrame.Nodes;
+
+namespace VisionFrame.ViewModels
+{
+ public class FlowTabViewModel : ObservableObject, IFlowContext
+ {
+ private bool _isCurrent;
+
+ public bool IsCurrent
+ {
+ get { return _isCurrent; }
+ set
+ {
+ SetProperty(ref _isCurrent, value);
+
+ if (!value) TitleEditVisible = Visibility.Hidden;
+ }
+ }
+
+ #region Title与编辑
+ private string _title;
+
+ public string Title
+ {
+ get { return _title; }
+ set { SetProperty(ref _title, value); }
+ }
+
+
+ private Visibility _titleEditVisible = Visibility.Hidden;
+
+ public Visibility TitleEditVisible
+ {
+ get { return _titleEditVisible; }
+ set { SetProperty(ref _titleEditVisible, value); }
+ }
+
+
+ public ICommand SelectCommand { get; set; }
+ public ICommand EditTitleCommand { get; set; }
+ public ICommand AcceptTitleCommand { get; set; }
+ #endregion
+
+ #region 节点创建
+ public ObservableCollection NodeList { get; set; } =
+ new ObservableCollection();
+
+ public ICommand DragDropCommand { get; set; }
+
+ public ICommand DeleteElementCommand { get; set; }
+ #endregion
+
+ #region 连线集合
+ public ObservableCollection LinkList { get; set; } =
+ new ObservableCollection();
+ #endregion
+
+ #region 节点移动相关动作
+ public ICommand NodeMouseDownCommand { get; set; }
+ public ICommand NodeMouseMoveCommand { get; set; }
+ public ICommand NodeMouseUpCommand { get; set; }
+ #endregion
+
+ #region 锚点操作
+ public ICommand AnchorDownCommand { get; set; }
+ public ICommand AnchorUpCommand { get; set; }
+ #endregion
+
+ #region 流程控制
+ public ICommand RunCommand { get; set; }
+ public ICommand CircleCommand { get; set; }
+ public ICommand StopCommand { get; set; }
+ public ICommand StepCommand { get; set; }
+ #endregion
+
+ #region 流程参数管理
+ public ObservableCollection ArgumentList { get; set; } =
+ new ObservableCollection();
+
+ public ICommand AddArgumentCommand { get; set; }
+ public ICommand DelArgumentCommand { get; set; }
+
+ public List ArgTypeList { get; set; } = new List
+ {
+ "Int32","IntPtr","Double","String","Boolean"
+ };
+
+
+ // 参数处理相关命令
+ public ICommand SelectFolderCommand { get; set; }
+ #endregion
+
+ private ImageSource _previewImage;
+ ///
+ /// 预览图像
+ ///
+ public ImageSource PreviewImage
+ {
+ get { return _previewImage; }
+ set { SetProperty(ref _previewImage, value); }
+ }
+
+
+ public ObservableCollection LogList { get; set; } =
+ new ObservableCollection();
+
+
+ //画布缩放
+ #region 画布缩放
+ private double _mainZoom = 1.0;
+
+ public double MainZoom
+ {
+ get { return _mainZoom; }
+ set { SetProperty(ref _mainZoom, value); }
+ }
+
+ private double _dragX = 0;
+
+ public double DragX
+ {
+ get { return _dragX; }
+ set { SetProperty(ref _dragX, value); }
+ }
+
+ private double _dragY = 0;
+
+ public double DragY
+ {
+ get { return _dragY; }
+ set { SetProperty(ref _dragY, value); }
+ }
+
+ public ICommand MouseWheelCommand { get; set; }
+
+ public ICommand CanvasMouseDownCommand { get; set; }
+ public ICommand CanvasMouseUpCommand { get; set; }
+ public ICommand CanvasMouseMoveCommand { get; set; }
+
+ public ICommand ZoomIncreaseCommand { get; set; }
+ public ICommand ZoomReduceCommand { get; set; }
+ public ICommand ZoomResetCommand { get; set; }
+ #endregion
+
+ #region 鹰眼视图
+ private double _hwakeyeZoom = 1.0;
+
+ public double HwakeyeZoom
+ {
+ get { return _hwakeyeZoom; }
+ set { SetProperty(ref _hwakeyeZoom, value); }
+ }
+
+ private double _hwakeyeDragX;
+
+ public double HwakeyeDragX
+ {
+ get { return _hwakeyeDragX; }
+ set { SetProperty(ref _hwakeyeDragX, value); }
+ }
+ private double _hwakeyeDragY;
+
+ public double HwakeyeDragY
+ {
+ get { return _hwakeyeDragY; }
+ set { SetProperty(ref _hwakeyeDragY, value); }
+ }
+
+ #endregion
+
+
+ public List Operator { get; set; } = new List
+ {
+ "=","<",">","<=",">=","!="
+ };
+
+ public FlowTabViewModel()
+ {
+ SelectCommand = new RelayCommand(() =>
+ {
+ this.IsCurrent = true;
+ });
+ DragDropCommand = new RelayCommand