工欲善其事必先利其器,好的系统离不开好的流程设计,开发之前首先设计好,整个系统运行的实体,对象之间的基础关系。
本篇讲解流程设计相关内容。
一、实际操作带入设计思路
看了其他的系统可以看出,布局是左边是流程节点,中间是流程编辑,右边图像预览。
整个运行是依赖一个方案进行,方案与流程是一对多的关系,流程与节点是一对多的关系。例如下图
因此设计实体的时候就会有方案对象,记录方案基础信息,方案对象中包含流程集合,代表一个方案对应多个流程,流程对象信息中又包含了节点几何,代表流程中可以存在多个节点,节点对象中又有字段关联父级的关系。
二、流程实体的设计
在文章下方
三、节点到流程panel的拖拽布局
本文的UI是基于WINFORM进行绘制的,winform中panel有点类似于web的div 容器的概念, panel可以有相对基于父容器的坐标,并且可以基于mousedown等鼠标的操作事件进行一些列的位置变化,跟随鼠标而改变容器位置。节点从固定容器拖动到另外一个容器用到了控件里面的DragDrop,DragEnter事件,对需要拖动节点Button设置MouseDown事件,触发按下的时候激活 btn.DoDragDrop(btn, DragDropEffects.Copy | DragDropEffects.Move); ,DoDragDrop方法与流程节点的内容形成对应。代码段如下:
红色标注的地方就是实现控件拖拽到另外一个容器并生成新控件的核心了,拿到了新控件就可以进行实体对象的组装了。
/// <summary>
/// 工具栏
/// </summary>
private async void LoadToolButtonAsync()
{
var toolNodes = await VisionTaskService.LoadNodeButtonAsync();
FlowPanelToolMenu.Controls.Clear();
toolNodes = toolNodes.OrderBy(x => x.Id).ToList();
toolNodes.ForEach(tool =>
{
Button button = new Button();
button.FlatStyle = FlatStyle.Flat;
button.Text = tool.Name;
button.Tag = tool;
button.Width = 150;
//button.AllowDrop = true;
button.MouseDown += ToolButton_MouseDown;
button.Height = 40;
button.ForeColor = Color.White;
button.BackColor = ColorTranslator.FromHtml("#7a7374");
button.Click += ToolButton_Click;
button.ImageAlign = ContentAlignment.MiddleLeft;
button.Image = VisionTaskService.GetButtonImage(tool.Code);
FlowPanelToolMenu.Controls.Add(button);
});
}
private void ToolButton_MouseDown(object sender, MouseEventArgs e)
{
Button btn = (Button)sender;
//左键的话,标志位为true(表示拖拽开始)
if ((e.Button == System.Windows.Forms.MouseButtons.Left))
{
btn.DoDragDrop(btn, DragDropEffects.Copy | DragDropEffects.Move);
//形成拖拽效果,移动+拷贝的组合效果
}
}
/// <summary>
/// 加载流程
/// </summary>
private void LoadFlow()
{
TabFlow.TabPages.Clear();
this.LabelSchemeName.Text = SchemeConfig.Scheme.Name;
// 循环加载PageFlow
SchemeConfig.Scheme.FlowList.ForEach(flow =>
{
// 默认新增一个Tab
var page = new TabPage()
{
};
page.AllowDrop = true;
page.Paint += Page_Paint;
page.DragEnter += Page_DragEnter;
page.DragDrop += Page_DragDrop;
page.Text = flow.Name;
page.AutoScroll = true;
page.Tag = flow;
page.BackColor = Color.FromArgb(70, 71, 74);
if (!flow.FlowNodeList.IsListNullOrEmpty()) {
// 加载流程上的节点
// 创建FlowPanel
//List<SchemeFlowNodeEntity> tempNodeList = new List<SchemeFlowNodeEntity>();
flow.FlowNodeList.ForEach(node =>
{
AddFlowNodePanel(page, node, new Point(node.LocationX, node.LocationY));
});
// flow.FlowNodeList = tempNodeList;
}
TabFlow.TabPages.Add(page);
});
}
private void Page_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
bMouseDown = true;
}
private void Page_DragDrop(object sender, DragEventArgs e)
{
if (bMouseDown)
{
// 从事件参数 DragEventArgs 中获取被拖动的元素
Button btn = (Button)e.Data.GetData(typeof(Button));
var basicData = btn.Tag as BasicToolNodeEntity;
var nodeType = (ToolNodeTypeEnum)basicData.NodeType.Value;
SchemeFlowNodeEntity flowNodeData = null;
switch (nodeType) {
case ToolNodeTypeEnum.ImageSource:
flowNodeData = basicData.MapTo<ImageSourceContentModel>();
break;
case ToolNodeTypeEnum.Geometry:
flowNodeData = basicData.MapTo<GeometryContentModel>();
break;
case ToolNodeTypeEnum.Blob:
flowNodeData = basicData.MapTo<BlobContentModel>();
break;
case ToolNodeTypeEnum.ConditionResult:
flowNodeData = basicData.MapTo<ConditionResultContentModel>();
break;
case ToolNodeTypeEnum.ColorRgb:
flowNodeData = basicData.MapTo<ColorRgbContentModel>();
break;
case ToolNodeTypeEnum.MatchTemplate:
flowNodeData = basicData.MapTo<MatchTemplateContentModel>();
break;
default:
flowNodeData = basicData.MapTo<ImageSourceContentModel>();
break;
}
flowNodeData.InitData();
var page = ((TabPage)sender);
var flow = page.Tag as SchemeFlowInfoEntity;
flowNodeData.Id = GeneratePrimaryKeyIdHelper.GetPrimaryKeyId();
flowNodeData.SchemeId = flow.SchemeId;
flowNodeData.FlowId = flow.Id;
flow.FlowNodeList.Add(flowNodeData);
var point = this.PointToClient(Control.MousePosition);
var position = new Point(point.X - 200, point.Y - 140);// 除去容器的左边 与上边距离
flowNodeData.LocationX = position.X;
flowNodeData.LocationY = position.Y;
AddFlowNodePanel(page, flowNodeData, position);
//RefreshControls(new Control[] { grp, (GroupBox)sender });
bMouseDown = false;
}
}
-
/// <summary> /// 方案信息 /// </summary> public class SchemeInfo : BaseField, IEntity<long> { public SchemeInfo() { Id = GeneratePrimaryKeyIdHelper.GetPrimaryKeyId(); } public long Id { get; set; } /// <summary> /// 方案编码 /// </summary> public string Code { get; set; } /// <summary> /// 方案名称 /// </summary> public string Name { get; set; } /// <summary> /// 备注 /// </summary> public string Remark { get; set; } /// <summary> /// 流程节点 /// </summary> public List<SchemeFlowInfoEntity> FlowList { set; get; } /// <summary> /// 全局变量 /// </summary> public List<GlobalVariableModel> GlobalVariableList { set; get; } /// <summary> /// 设备配置 /// </summary> public GlobalDeviceConfig GlobalDeviceConfig { set; get; } } /// <summary> /// 流程信息 /// </summary> [MyTableName("Scheme_FlowInfo")] [MyPrimaryKey("Id", AutoIncrement = false)] public class SchemeFlowInfoEntity : BaseField, IEntity<long> { public SchemeFlowInfoEntity() { Id = GeneratePrimaryKeyIdHelper.GetPrimaryKeyId(); } public long Id { get; set; } /// <summary> /// 流程编码 /// </summary> public string Code { get; set; } /// <summary> /// 流程名称 /// </summary> public string Name { get; set; } /// <summary> /// 运行间隔ms /// </summary> public int? RunInterval { get; set; } /// <summary> /// 顺序 /// </summary> public int? SortNum { get; set; } /// <summary> /// 方案ID /// </summary> public long? SchemeId { get; set; } /// <summary> /// 备注 /// </summary> public string Remark { get; set; } /// <summary> /// 节点信息 /// </summary> [MyResultColumn] public List<SchemeFlowNodeEntity> FlowNodeList { set; get; } } /// <summary> /// 节点信息 /// </summary> [MyTableName("Scheme_FlowNode")] [MyPrimaryKey("Id", AutoIncrement = false)] public abstract class SchemeFlowNodeEntity : BaseField, IEntity<long> { /// <summary> /// /// </summary> public virtual void InitData() { } public SchemeFlowNodeEntity() { Id = GeneratePrimaryKeyIdHelper.GetPrimaryKeyId(); } public long Id { get; set; } /// <summary> /// 节点类型 /// </summary> public long? NodeType { get; set; } /// <summary> /// 节点名称 /// </summary> public string Code { get; set; } /// <summary> /// 流程名称 /// </summary> public string Name { get; set; } /// <summary> /// 自定义名称 /// </summary> public string CustomName { get; set; } /// <summary> /// 运行间隔ms /// </summary> public int? RunInterval { get; set; } /// <summary> /// 顺序 /// </summary> public int? SortNum { get; set; } /// <summary> /// 方案ID /// </summary> public long? SchemeId { get; set; } /// <summary> /// 流程ID /// </summary> public long? FlowId { get; set; } /// <summary> /// 上一步骤 /// </summary> public string LastStep { get; set; } /// <summary> /// 下一步骤 /// </summary> public string NextStep { get; set; } /// <summary> /// 备注 /// </summary> public string Remark { get; set; } public int LocationX { set; get; } public int LocationY { set; get; } /// <summary> /// 触发设置! /// </summary> [MyResultColumn] public List<GlobalVariableModel> TriggerConditions { set; get; } = new List<GlobalVariableModel>(); /// <summary> /// 回写设置! /// </summary> [MyResultColumn] public List<GlobalVariableModel> AfterRunConditions { set; get; } = new List<GlobalVariableModel>(); /// <summary> /// 节点结果数据 /// </summary> [MyResultColumn] public List<NodeResultModel> NodeResultModels { set; get; } = new List<NodeResultModel>(); [Newtonsoft.Json.JsonIgnore()] public List<HImageData> HImageDataList { set; get; } /// <summary> /// 算子参数 /// </summary> public AlgorithmToolPar ToolPar { set; get; } = new AlgorithmToolPar(); }