领胜LDS 键盘AOI检测项目
xcd
2020-06-27 998ad559883116280541553b761221bd8d486b9e
M071主要流程配置
19个文件已添加
14个文件已修改
1564 ■■■■■ 已修改文件
M071.sln 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Device/DeviceBase/CameraBase.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Device/DeviceBase/PLCBase.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Device/Interface/IMotion.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Bro.Common.Model.csproj 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Forms/FrmDeviceOpConfigEditor.Designer.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Forms/FrmDeviceOpConfigEditor.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Forms/FrmDeviceOpConfigEditor.resx 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Helper/PropertyConvertHelper.cs 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Model/CustomizedPoint.cs 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Model/DeviceOpBind.cs 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Selector/DeviceSelector.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Selector/ProcessMethodSelector.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.Gocator/Bro.Device.Gocator.csproj 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.Gocator/GocatorConfig.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.Gocator/GocatorDriver.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.Gocator/Properties/AssemblyInfo.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Bro.M071.Process.csproj 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Config.cs 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Converters.cs 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Models.cs 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/M071Process.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Properties/AssemblyInfo.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Properties/Resources.Designer.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Properties/Resources.resx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Properties/Settings.Designer.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/Properties/Settings.settings 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M071.Process/packages.config 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Process/ProcessConfig.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.UI.Model.Winform/Element/CrossHair.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.UI.Model.Winform/Element/CrossHairWithAngle.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.UI.Model.Winform/Element/PointIndicator.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.UI.Model.Winform/Element/ROI_Circle.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
M071.sln
@@ -19,6 +19,10 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bro.UI.Model.Winform", "src\Bro.UI.Model.Winform\Bro.UI.Model.Winform.csproj", "{741F6491-57C7-479A-B391-09BBA9FBA9DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bro.M071.Process", "src\Bro.M071.Process\Bro.M071.Process.csproj", "{B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bro.Device.Gocator", "src\Bro.Device.Gocator\Bro.Device.Gocator.csproj", "{112009F0-7902-454B-9A6C-A3AFC8FA8FFF}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
@@ -125,6 +129,30 @@
        {741F6491-57C7-479A-B391-09BBA9FBA9DC}.Release|x64.Build.0 = Release|Any CPU
        {741F6491-57C7-479A-B391-09BBA9FBA9DC}.Release|x86.ActiveCfg = Release|Any CPU
        {741F6491-57C7-479A-B391-09BBA9FBA9DC}.Release|x86.Build.0 = Release|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Debug|x64.ActiveCfg = Debug|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Debug|x64.Build.0 = Debug|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Debug|x86.ActiveCfg = Debug|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Debug|x86.Build.0 = Debug|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Release|Any CPU.Build.0 = Release|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Release|x64.ActiveCfg = Release|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Release|x64.Build.0 = Release|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Release|x86.ActiveCfg = Release|Any CPU
        {B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}.Release|x86.Build.0 = Release|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Debug|x64.ActiveCfg = Debug|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Debug|x64.Build.0 = Debug|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Debug|x86.ActiveCfg = Debug|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Debug|x86.Build.0 = Debug|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Release|Any CPU.Build.0 = Release|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Release|x64.ActiveCfg = Release|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Release|x64.Build.0 = Release|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Release|x86.ActiveCfg = Release|Any CPU
        {112009F0-7902-454B-9A6C-A3AFC8FA8FFF}.Release|x86.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
src/Bro.Common.Device/DeviceBase/CameraBase.cs
@@ -616,7 +616,7 @@
    #region Converter & Editor
    public class HalconSerialNumConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            HTuple deviceList = null;
@@ -654,7 +654,7 @@
    public class CameraDeviceConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
src/Bro.Common.Device/DeviceBase/PLCBase.cs
@@ -552,7 +552,7 @@
    public class PLCDeviceConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
src/Bro.Common.Device/Interface/IMotion.cs
@@ -1,4 +1,5 @@
using System;
using Bro.Common.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -14,9 +15,14 @@
        List<AxisInfo> GetCurrentAxisInfo(params string[] axisName);
    }
    public class AxisInfo
    public class AxisInfo : IComplexDisplay
    {
        public string AxisName { get; set; }
        public float AxisLocation { get; set; }
        public double AxisLocation { get; set; }
        public string GetDisplayText()
        {
            return $"{AxisName}:{AxisLocation}";
        }
    }
}
src/Bro.Common.Model/Bro.Common.Model.csproj
@@ -67,6 +67,12 @@
    <Compile Include="Factory\FactoryHelper.cs" />
    <Compile Include="Factory\ProcessFactory.cs" />
    <Compile Include="Factory\UIFactory.cs" />
    <Compile Include="Forms\FrmDeviceOpConfigEditor.cs">
      <SubType>Form</SubType>
    </Compile>
    <Compile Include="Forms\FrmDeviceOpConfigEditor.Designer.cs">
      <DependentUpon>FrmDeviceOpConfigEditor.cs</DependentUpon>
    </Compile>
    <Compile Include="Forms\FrmOpConfigEdit.cs">
      <SubType>Form</SubType>
    </Compile>
@@ -103,6 +109,7 @@
    <Compile Include="Log\LoggerHelper.cs" />
    <Compile Include="Model\CustomizedPoint.cs" />
    <Compile Include="Model\CustomizedRectangle.cs" />
    <Compile Include="Model\DeviceOpBind.cs" />
    <Compile Include="Model\ModbusFrame.cs" />
    <Compile Include="Model\MonitorSet.cs" />
    <Compile Include="Model\PageRequest.cs" />
@@ -128,6 +135,9 @@
    <None Include="packages.config" />
  </ItemGroup>
  <ItemGroup>
    <EmbeddedResource Include="Forms\FrmDeviceOpConfigEditor.resx">
      <DependentUpon>FrmDeviceOpConfigEditor.cs</DependentUpon>
    </EmbeddedResource>
    <EmbeddedResource Include="Forms\FrmOpConfigEdit.resx">
      <DependentUpon>FrmOpConfigEdit.cs</DependentUpon>
    </EmbeddedResource>
src/Bro.Common.Model/Forms/FrmDeviceOpConfigEditor.Designer.cs
New file
@@ -0,0 +1,119 @@
namespace Bro.Common.Model.Forms
{
    partial class FrmDeviceOpConfigEditor
    {
        /// <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.btnReset = new System.Windows.Forms.Button();
            this.btnCancel = new System.Windows.Forms.Button();
            this.btnConfirm = new System.Windows.Forms.Button();
            this.propGrid = new System.Windows.Forms.PropertyGrid();
            this.cboDevice = new System.Windows.Forms.ComboBox();
            this.SuspendLayout();
            //
            // btnReset
            //
            this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.btnReset.BackColor = System.Drawing.Color.Red;
            this.btnReset.ForeColor = System.Drawing.Color.White;
            this.btnReset.Location = new System.Drawing.Point(231, 14);
            this.btnReset.Name = "btnReset";
            this.btnReset.Size = new System.Drawing.Size(75, 23);
            this.btnReset.TabIndex = 4;
            this.btnReset.Text = "重置配置";
            this.btnReset.UseVisualStyleBackColor = false;
            this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
            //
            // btnCancel
            //
            this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.btnCancel.Location = new System.Drawing.Point(312, 14);
            this.btnCancel.Name = "btnCancel";
            this.btnCancel.Size = new System.Drawing.Size(75, 23);
            this.btnCancel.TabIndex = 5;
            this.btnCancel.Text = "取消";
            this.btnCancel.UseVisualStyleBackColor = true;
            this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
            //
            // btnConfirm
            //
            this.btnConfirm.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.btnConfirm.Location = new System.Drawing.Point(393, 14);
            this.btnConfirm.Name = "btnConfirm";
            this.btnConfirm.Size = new System.Drawing.Size(75, 23);
            this.btnConfirm.TabIndex = 6;
            this.btnConfirm.Text = "确认";
            this.btnConfirm.UseVisualStyleBackColor = true;
            this.btnConfirm.Click += new System.EventHandler(this.btnConfirm_Click);
            //
            // propGrid
            //
            this.propGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
            | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));
            this.propGrid.Location = new System.Drawing.Point(3, 56);
            this.propGrid.Name = "propGrid";
            this.propGrid.Size = new System.Drawing.Size(475, 346);
            this.propGrid.TabIndex = 3;
            this.propGrid.ToolbarVisible = false;
            //
            // cboDevice
            //
            this.cboDevice.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));
            this.cboDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboDevice.FormattingEnabled = true;
            this.cboDevice.Location = new System.Drawing.Point(12, 16);
            this.cboDevice.Name = "cboDevice";
            this.cboDevice.Size = new System.Drawing.Size(190, 20);
            this.cboDevice.TabIndex = 7;
            //
            // FrmDeviceOpConfigEditor
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(480, 404);
            this.Controls.Add(this.cboDevice);
            this.Controls.Add(this.btnReset);
            this.Controls.Add(this.btnCancel);
            this.Controls.Add(this.btnConfirm);
            this.Controls.Add(this.propGrid);
            this.Name = "FrmDeviceOpConfigEditor";
            this.ShowIcon = false;
            this.Text = "操作设备及配置编辑界面";
            this.ResumeLayout(false);
        }
        #endregion
        private System.Windows.Forms.Button btnReset;
        private System.Windows.Forms.Button btnCancel;
        private System.Windows.Forms.Button btnConfirm;
        private System.Windows.Forms.PropertyGrid propGrid;
        private System.Windows.Forms.ComboBox cboDevice;
    }
}
src/Bro.Common.Model/Forms/FrmDeviceOpConfigEditor.cs
New file
@@ -0,0 +1,126 @@
using Autofac;
using Bro.Common.Base;
using Bro.Common.Factory;
using Bro.Common.Helper;
using Bro.Common.Interface;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Bro.Common.Model.Forms
{
    public partial class FrmDeviceOpConfigEditor : Form
    {
        public FrmDeviceOpConfigEditor()
        {
            InitializeComponent();
        }
        //private DeviceOpBind bind = null;
        public DeviceOpBind Bind { get; set; }
        IOperationConfig backOpConfig = new OperationConfigBase();
        List<IDevice> deviceList = null;
        IDevice currentDevice = null;
        public FrmDeviceOpConfigEditor(DeviceOpBind bind)
        {
            InitializeComponent();
            InitialDeviceCbo();
            Bind = bind;
            if (!string.IsNullOrWhiteSpace(bind.Device))
            {
                cboDevice.SelectedValue = bind.Device;
                currentDevice = deviceList.FirstOrDefault(u => u.Id == bind.Device);
                if (currentDevice != null)
                {
                    var attr = currentDevice.GetType().GetCustomAttribute<DeviceAttribute>();
                    if (attr != null)
                    {
                        backOpConfig = ConfigFactory.GetOperationConfig(attr.TypeCode);
                    }
                }
            }
            else
            {
                if (cboDevice.Items.Count > 0)
                    cboDevice.SelectedIndex = 0;
            }
            cboDevice.SelectedIndexChanged += CboDevice_SelectedIndexChanged;
            if (bind.OpConfig == null)
            {
                bind.OpConfig = new OperationConfigBase();
                if (currentDevice != null)
                {
                    var attr = currentDevice.GetType().GetCustomAttribute<DeviceAttribute>();
                    if (attr != null)
                        bind.OpConfig = ConfigFactory.GetOperationConfig(attr.TypeCode);
                }
            }
            backOpConfig.DataFrom(bind.OpConfig);
            propGrid.SelectedObject = bind.OpConfig;
        }
        private void CboDevice_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeOpConfigByDevice();
        }
        private void ChangeOpConfigByDevice()
        {
            var device = deviceList.FirstOrDefault(u => u.Id == cboDevice.SelectedValue.ToString());
            if (device != null)
            {
                var attr = device.GetType().GetCustomAttribute<DeviceAttribute>();
                if (attr != null)
                {
                    propGrid.SelectedObject = ConfigFactory.GetOperationConfig(attr.TypeCode);
                }
            }
        }
        private void InitialDeviceCbo()
        {
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                deviceList = scope.Resolve<List<IDevice>>();
                if (deviceList.Count > 0)
                {
                    UIHelper.SetCombo(cboDevice, new List<ISimpleDevice>(deviceList), "Name", "Id");
                }
            }
        }
        private void btnCancel_Click(object sender, EventArgs e)
        {
            Bind.OpConfig = backOpConfig;
            this.DialogResult = DialogResult.Cancel;
        }
        private void btnConfirm_Click(object sender, EventArgs e)
        {
            Bind.Device = cboDevice.SelectedValue.ToString();
            Bind.OpConfig = propGrid.SelectedObject as IOperationConfig;
            this.DialogResult = DialogResult.OK;
        }
        private void btnReset_Click(object sender, EventArgs e)
        {
            ChangeOpConfigByDevice();
        }
    }
}
src/Bro.Common.Model/Forms/FrmDeviceOpConfigEditor.resx
New 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>
src/Bro.Common.Model/Helper/PropertyConvertHelper.cs
@@ -112,7 +112,7 @@
        //    set => hash = value;
        //}
        public abstract Hashtable GetConvertHash();
        public abstract Hashtable GetConvertHash(ITypeDescriptorContext context);
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
@@ -121,7 +121,7 @@
        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            Hash = GetConvertHash();
            Hash = GetConvertHash(context);
            string[] ids = new string[Hash.Values.Count];
            int i = 0;
@@ -216,7 +216,26 @@
                {
                    return (value as IComplexDisplay).GetDisplayText();
                }
                else
                else if (value is IEnumerable enumList)
                {
                    string display = "";
                    bool iComplexDisplayMatch = false;
                    var enumrator = enumList.GetEnumerator();
                    while (enumrator.MoveNext())
                    {
                        if (enumrator.Current is IComplexDisplay d)
                        {
                            iComplexDisplayMatch = true;
                            display += $"{d.GetDisplayText()} ";
                        }
                    }
                    if (iComplexDisplayMatch)
                    {
                        return display.Trim();
                    }
                }
                {
                    return JsonConvert.SerializeObject(value);
                }
@@ -571,7 +590,7 @@
                }
                form.ShowDialog();
                List<string> returnStrs = tbox.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                List<string> returnStrs = tbox.Text.Split(new char[] { '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                switch (typeof(T).Name)
                {
@@ -816,7 +835,7 @@
        }
    }
    public class DeviceTypeConverter : StringConverter
    public class DeviceTypeConverter : StringConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
@@ -847,7 +866,7 @@
            return new StandardValuesCollection(devices);
        }
    }
    public class DeviceInitialConfigEditor<T> : UITypeEditor where T : class, IInitialConfig
    {
        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
src/Bro.Common.Model/Model/CustomizedPoint.cs
@@ -15,10 +15,10 @@
    /// </summary>
    public class CustomizedPoint : IComplexDisplay, ICSVOutput, INotifyPropertyChanged
    {
        private float x = 0;
        private double x = 0;
        [Category("坐标设置")]
        [Description("X坐标")]
        public float X
        public double X
        {
            get => x;
            set
@@ -31,10 +31,10 @@
            }
        }
        private float y = 0;
        private double y = 0;
        [Category("坐标设置")]
        [Description("Y坐标")]
        public float Y
        public double Y
        {
            get => y;
            set
@@ -49,7 +49,7 @@
        public CustomizedPoint() { }
        public CustomizedPoint(float x, float y)
        public CustomizedPoint(double x, double y)
        {
            X = x;
            Y = y;
@@ -95,7 +95,7 @@
            List<CustomizedPoint> points = new List<CustomizedPoint>();
            for (int i = 0; i < Xs.Count && i < Ys.Count; i++)
            {
                points.Add(new CustomizedPoint((float)Xs[i], (float)Ys[i]));
                points.Add(new CustomizedPoint(Xs[i], Ys[i]));
            }
            return points;
@@ -333,19 +333,19 @@
    {
        [Category("标定坐标")]
        [Description("平台坐标-X")]
        public float X { get; set; }
        public double X { get; set; }
        [Category("标定坐标")]
        [Description("平台坐标-X")]
        public float Y { get; set; }
        public double Y { get; set; }
        [Category("像素坐标")]
        [Description("像素坐标-U")]
        public float U { get; set; }
        public double U { get; set; }
        [Category("像素坐标")]
        [Description("像素坐标-V")]
        public float V { get; set; }
        public double V { get; set; }
        private bool isBasePoint = false;
        [Category("快捷设置")]
@@ -419,7 +419,7 @@
        //    }
        //}
        public float MainAxisValue
        public double MainAxisValue
        {
            get => ((Direction ?? PriorityDirection.X) == PriorityDirection.X) ? X : Y;
            set
@@ -435,7 +435,7 @@
            }
        }
        public float MinorAxisValue
        public double MinorAxisValue
        {
            get => ((Direction ?? PriorityDirection.X) == PriorityDirection.X) ? Y : X;
            set
src/Bro.Common.Model/Model/DeviceOpBind.cs
New file
@@ -0,0 +1,88 @@
using Autofac;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model.Forms;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.Design;
namespace Bro.Common.Model
{
    public class DeviceOpBind : IComplexDisplay
    {
        [Category("设备名称")]
        [Description("设备名称")]
        [TypeConverter(typeof(DeviceSelectorConverter<IDevice>))]
        public virtual string Device { get; set; }
        [Category("操作配置")]
        [Description("操作配置")]
        public IOperationConfig OpConfig { get; set; } = null;
        public string GetDisplayText()
        {
            string msg = "";
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                List<IDevice> deviceList = scope.Resolve<List<IDevice>>();
                if (Device != null)
                {
                    var device = deviceList.FirstOrDefault(u => u.Id == Device);
                    if (device != null)
                    {
                        msg += device.Name + " ";
                    }
                }
            }
            if (OpConfig is IComplexDisplay d)
            {
                msg += d.GetDisplayText();
            }
            else
            {
                msg += JsonConvert.SerializeObject(OpConfig);
            }
            return msg;
        }
    }
    public class IOperationConfigByDeviceEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.Modal;
        }
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (edSvc != null)
            {
                if (value == null)
                {
                    value = new DeviceOpBind();
                }
                if (value is DeviceOpBind bind)
                {
                    FrmDeviceOpConfigEditor frmDeviceOpEditor = new FrmDeviceOpConfigEditor(bind);
                    frmDeviceOpEditor.ShowDialog();
                    return frmDeviceOpEditor.Bind;
                }
            }
            return base.EditValue(context, provider, value);
        }
    }
}
src/Bro.Common.Model/Selector/DeviceSelector.cs
@@ -3,13 +3,14 @@
using Bro.Common.Interface;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace Bro.Common.Model
{
    public class DeviceSelectorConverter<T> : ComboBoxItemTypeConvert where T : IDevice
    public class DeviceSelectorConverter<T> : ComboBoxItemTypeConvert //where T : IDevice
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
src/Bro.Common.Model/Selector/ProcessMethodSelector.cs
@@ -10,7 +10,7 @@
{
    public class ProcessMethodSelectorConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash()
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
New file
@@ -0,0 +1,49 @@
<?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>{112009F0-7902-454B-9A6C-A3AFC8FA8FFF}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Bro.Device.Gocator</RootNamespace>
    <AssemblyName>Bro.Device.Gocator</AssemblyName>
    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>..\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <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="GocatorConfig.cs" />
    <Compile Include="GocatorDriver.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
src/Bro.Device.Gocator/GocatorConfig.cs
New file
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bro.Device.Gocator
{
    class GocatorConfig
    {
    }
}
src/Bro.Device.Gocator/GocatorDriver.cs
New file
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bro.Device.Gocator
{
    class GocatorDriver
    {
    }
}
src/Bro.Device.Gocator/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("Bro.Device.Gocator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Bro.Device.Gocator")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("112009f0-7902-454b-9a6c-a3afc8fa8fff")]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
src/Bro.M071.Process/Bro.M071.Process.csproj
New file
@@ -0,0 +1,95 @@
<?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>{B50C1309-495C-4ADF-8A3D-6F6A06CCC4CC}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>Bro.M071.Process</RootNamespace>
    <AssemblyName>Bro.M071.Process</AssemblyName>
    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>..\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </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>
  </PropertyGroup>
  <PropertyGroup>
    <StartupObject />
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Autofac, Version=4.9.4.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
      <HintPath>..\..\packages\Autofac.4.9.4\lib\net45\Autofac.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Deployment" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="M071Converters.cs" />
    <Compile Include="M071Config.cs" />
    <Compile Include="M071Models.cs" />
    <Compile Include="M071Process.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <EmbeddedResource Include="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
      <SubType>Designer</SubType>
    </EmbeddedResource>
    <Compile Include="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
    <None Include="packages.config" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
    <Compile Include="Properties\Settings.Designer.cs">
      <AutoGen>True</AutoGen>
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
    </Compile>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Bro.Common.Device\Bro.Common.Device.csproj">
      <Project>{987308DD-8BAA-463A-94E2-77D62E01A5BF}</Project>
      <Name>Bro.Common.Device</Name>
    </ProjectReference>
    <ProjectReference Include="..\Bro.Common.Model\Bro.Common.Model.csproj">
      <Project>{1A3CBFE7-3F78-42C3-95C5-10360450DBEA}</Project>
      <Name>Bro.Common.Model</Name>
    </ProjectReference>
    <ProjectReference Include="..\Bro.Process\Bro.Process.csproj">
      <Project>{197C5AA8-9609-4D1C-B1E3-5879006EAAF4}</Project>
      <Name>Bro.Process</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
src/Bro.M071.Process/M071Config.cs
New file
@@ -0,0 +1,66 @@
using Bro.Common.Helper;
using Bro.Common.Model;
using Bro.Process;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bro.M071.Process
{
    [Process("M071", EnumHelper.DeviceAttributeType.InitialConfig)]
    public partial class M071Config : ProcessConfigBase
    {
        #region 公共字典配置
        [Category("公共字典配置")]
        [Description("键盘键名集合")]
        [TypeConverter(typeof(SimpleCollectionConvert<string>))]
        [Editor(typeof(SimpleCollectionEditor<string>), typeof(UITypeEditor))]
        public List<string> KeyNameCollection { get; set; } = new List<string>();
        [Category("公共字典配置")]
        [Description("检测标准集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<Spec>), typeof(UITypeEditor))]
        public List<Spec> MeasureSpecCollection { get; set; } = new List<Spec>();
        [Category("公共字典配置")]
        [Description("单键算法配置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<KeyAlgorithem>), typeof(UITypeEditor))]
        public List<KeyAlgorithem> KeyAlgorithemCollection { get; set; } = new List<KeyAlgorithem>();
        [Category("公共字典配置")]
        [Description("单键结果配置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<KeyResult>), typeof(UITypeEditor))]
        public List<KeyResult> KeyResultCollection { get; set; } = new List<KeyResult>();
        #endregion
        [Category("背景图片设置")]
        [Description("运行背景图片路径")]
        [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
        public string BackgroundImagePath { get; set; }
        [Category("检测设置")]
        [Description("拍摄点位设置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<SnapshotPoint>), typeof(UITypeEditor))]
        public List<SnapshotPoint> SnapshotPointCollection { get; set; } = new List<SnapshotPoint>();
        [Category("检测设置")]
        [Description("视觉检测单键配置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<KeyUnit>), typeof(UITypeEditor))]
        public List<KeyUnit> KeyUnitCollection { get; set; } = new List<KeyUnit>();
        [Category("检测设置")]
        [Description("检测配置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<MeasurementUint>), typeof(UITypeEditor))]
        public List<MeasurementUint> MeasurementUnitCollection { get; set; } = new List<MeasurementUint>();
    }
}
src/Bro.M071.Process/M071Converters.cs
New file
@@ -0,0 +1,191 @@
using Autofac;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace Bro.M071.Process
{
    internal class KeyNameDictConverter : StringConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }
        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
                if (iConfig is M071Config config)
                {
                    return new StandardValuesCollection(config.KeyNameCollection);
                }
            }
            return base.GetStandardValues(context); ;
        }
    }
    internal class SnapshotPointConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
                if (iConfig is M071Config config)
                {
                    config.SnapshotPointCollection.ForEach(s =>
                    {
                        table[s.Id] = s.Name;
                    });
                }
            }
            return table;
        }
    }
    internal class KeyAlgorithemConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
                if (iConfig is M071Config config)
                {
                    config.KeyAlgorithemCollection.ForEach(s =>
                    {
                        table[s.Id] = s.Name;
                    });
                }
            }
            return table;
        }
    }
    internal class KeyResultConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
                if (iConfig is M071Config config)
                {
                    config.KeyResultCollection.ForEach(s =>
                    {
                        table[s.Id] = s.Name;
                    });
                }
            }
            return table;
        }
    }
    //internal class KeyUnitConverter : ComboBoxItemTypeConvert
    //{
    //    public override Hashtable GetConvertHash(ITypeDescriptorContext context)
    //    {
    //        Hashtable table = new Hashtable();
    //        using (var scope = GlobalVar.Container.BeginLifetimeScope())
    //        {
    //            IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
    //            if (iConfig is M071Config config)
    //            {
    //                config.KeyUnitCollection.ForEach(s =>
    //                {
    //                    table[s.Id] = s.Key;
    //                });
    //            }
    //        }
    //        return table;
    //    }
    //}
    internal class KeyUnitResultConverter : ComboBoxItemTypeConvert
    {
        public override Hashtable GetConvertHash(ITypeDescriptorContext context)
        {
            Hashtable table = new Hashtable();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
                if (iConfig is M071Config config)
                {
                    var resultIds = config.KeyUnitCollection.Where(u => u.Key == (context.Instance as KeyUnitBind).Key).Select(u => u.KeyResult).ToList();
                    var result = config.KeyResultCollection.Where(u => resultIds.Contains(u.Id)).SelectMany(u => u.Results).ToList();
                    if (result != null && result.Count > 0)
                    {
                        result.ForEach(r =>
                        {
                            table[r] = r;
                        });
                        table[""] = "";
                    }
                }
            }
            return table;
        }
    }
    internal class MeasureTypeConverter : StringConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }
        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                IProcessConfig iConfig = scope.Resolve<IProcessConfig>();
                if (iConfig is M071Config config)
                {
                    return new StandardValuesCollection(config.MeasureSpecCollection.Select(u => u.Code).ToList());
                }
            }
            return base.GetStandardValues(context); ;
        }
    }
}
src/Bro.M071.Process/M071Models.cs
New file
@@ -0,0 +1,205 @@
using Autofac;
using Bro.Common.Base;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bro.M071.Process
{
    public class KeyAlgorithem : IComplexDisplay
    {
        [Browsable(false)]
        public string Id { get; set; } = Guid.NewGuid().ToString();
        [Category("单键算法配置")]
        [Description("算法名称")]
        public string Name { get; set; }
        [Category("单键算法配置")]
        [Description("算法路径")]
        [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
        public string AlgorithemPath { get; set; }
        public string GetDisplayText()
        {
            return $"{Name} -- {AlgorithemPath}";
        }
    }
    public class KeyResult : IComplexDisplay
    {
        [Browsable(false)]
        public string Id { get; set; } = Guid.NewGuid().ToString();
        [Category("单键结果配置")]
        [Description("结果配置名称")]
        public string Name { get; set; }
        [Category("单键结果配置")]
        [Description("结果配置集合")]
        [TypeConverter(typeof(SimpleCollectionConvert<string>))]
        [Editor(typeof(SimpleCollectionEditor<string>), typeof(UITypeEditor))]
        public List<string> Results { get; set; } = new List<string>();
        public string GetDisplayText()
        {
            return $"{Name} -- {(string.Join(" ", Results))}";
        }
    }
    public class SnapshotPoint : IComplexDisplay, IHalconToolPath
    {
        public string GetDisplayText()
        {
            return Name + " " + string.Join(" ", Destination.Select(u => u.GetDisplayText())) + " " + CameraOp.GetDisplayText();
        }
        public List<string> GetHalconToolPathList()
        {
            if (CameraOp.OpConfig is IHalconToolPath path)
            {
                return path.GetHalconToolPathList();
            }
            else
            {
                return new List<string>();
            }
        }
        [Browsable(false)]
        public string Id { get; set; } = Guid.NewGuid().ToString();
        [Category("拍照点名称")]
        [Description("拍照点名称")]
        public string Name { get; set; }
        [Category("设备配置")]
        [Description("运动设备")]
        [TypeConverter(typeof(DeviceSelectorConverter<IMotion>))]
        public string MotionDevice { get; set; }
        [Category("运动点位")]
        [Description("运动点位")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(ComplexCollectionEditor<AxisInfo>), typeof(UITypeEditor))]
        public List<AxisInfo> Destination { get; set; } = new List<AxisInfo>();
        [Category("相机和操作配置")]
        [Description("相机和操作配置")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(IOperationConfigByDeviceEditor), typeof(UITypeEditor))]
        public DeviceOpBind CameraOp { get; set; } = new DeviceOpBind();
    }
    public class KeyUnit : IComplexDisplay, IHalconToolPath
    {
        private string key = "";
        [Category("键名配置")]
        [Description("单键检测键名")]
        [TypeConverter(typeof(KeyNameDictConverter))]
        public string Key
        {
            get => key;
            set
            {
                AlignName = key = value;
            }
        }
        [Category("键名配置")]
        [Description("别名")]
        public string AlignName { get; set; }
        [Category("图像来源")]
        [Description("拍照点位")]
        [TypeConverter(typeof(SnapshotPointConverter))]
        public string SnapshotPoint { get; set; }
        [Category("图像来源")]
        [Description("拍照点位获取的图片索引")]
        public int ImageIndex { get; set; } = 0;
        [Category("算法配置")]
        [Description("单键检测配置算法类型")]
        [TypeConverter(typeof(KeyAlgorithemConverter))]
        public string KeyAlgorithem { get; set; }
        [Category("算法配置")]
        [Description("单键检测配置结果类型")]
        [TypeConverter(typeof(KeyResultConverter))]
        public string KeyResult { get; set; }
        public string GetDisplayText()
        {
            return $"{AlignName}";
        }
        public List<string> GetHalconToolPathList()
        {
            return null;
        }
    }
    public class MeasurementUint : IComplexDisplay
    {
        [Category("名称")]
        [Description("名称")]
        public string Name { get; set; } = "";
        [Category("算法配置")]
        [Description("检测和标准类型")]
        [TypeConverter(typeof(MeasureTypeConverter))]
        public string MeasureType { get; set; }
        [Category("算法配置")]
        [Description("涉及单键集合")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(ComplexCollectionEditor<KeyUnitBind>), typeof(UITypeEditor))]
        public List<KeyUnitBind> KeyUnitCollection { get; set; } = new List<KeyUnitBind>();
        [Browsable(false)]
        public RectangleF DisplayLocation { get; set; } = new RectangleF();
        public string GetDisplayText()
        {
            if (string.IsNullOrWhiteSpace(Name))
            {
                return string.Join("-", KeyUnitCollection.Select(u => u.GetDisplayText())) + "-" + MeasureType.ToString();
            }
            else
            {
                return Name;
            }
        }
    }
    public class KeyUnitBind : IComplexDisplay
    {
        [Category("单键配置")]
        [Description("单键配置")]
        [TypeConverter(typeof(KeyNameDictConverter))]
        public string Key { get; set; }
        [Category("单键配置")]
        [Description("单键结果配置")]
        [TypeConverter(typeof(KeyUnitResultConverter))]
        public string KeyResult { get; set; } = "";
        public string GetDisplayText()
        {
            string msg = Key;
            msg += string.IsNullOrWhiteSpace(KeyResult) ? "" : $"-{KeyResult}";
            return msg;
        }
    }
}
src/Bro.M071.Process/M071Process.cs
New file
@@ -0,0 +1,27 @@
using Bro.Common.Helper;
using Bro.Common.Model;
using Bro.Process;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bro.M071.Process
{
    [Process("M071", EnumHelper.DeviceAttributeType.Device)]
    public partial class M071Process : ProcessControl
    {
        #region 构造函数
        public M071Process() : base() { }
        public M071Process(string productionCode) : base(productionCode) { }
        #endregion
        #region 配置
        M071Config Config
        {
            get => IConfig as M071Config;
        }
        #endregion
    }
}
src/Bro.M071.Process/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("Bro.M071.Process")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Bro.M071.Process")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("b50c1309-495c-4adf-8a3d-6f6a06ccc4cc")]
// 程序集的版本信息由下列四个值组成:
//
//      主版本
//      次版本
//      生成号
//      修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
src/Bro.M071.Process/Properties/Resources.Designer.cs
New file
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     此代码由工具生成。
//     运行时版本: 4.0.30319.42000
//
//     对此文件的更改可能导致不正确的行为,如果
//     重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace Bro.M071.Process.Properties
{
    /// <summary>
    ///   强类型资源类,用于查找本地化字符串等。
    /// </summary>
    // 此类是由 StronglyTypedResourceBuilder
    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
    // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
    // (以 /str 作为命令选项),或重新生成 VS 项目。
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resources
    {
        private static global::System.Resources.ResourceManager resourceMan;
        private static global::System.Globalization.CultureInfo resourceCulture;
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resources()
        {
        }
        /// <summary>
        ///   返回此类使用的缓存 ResourceManager 实例。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager
        {
            get
            {
                if ((resourceMan == null))
                {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bro.M071.Process.Properties.Resources", typeof(Resources).Assembly);
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        /// <summary>
        ///   覆盖当前线程的 CurrentUICulture 属性
        ///   使用此强类型的资源类的资源查找。
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture
        {
            get
            {
                return resourceCulture;
            }
            set
            {
                resourceCulture = value;
            }
        }
    }
}
src/Bro.M071.Process/Properties/Resources.resx
New file
@@ -0,0 +1,117 @@
<?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.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: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" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
            </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" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
            </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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
</root>
src/Bro.M071.Process/Properties/Settings.Designer.cs
New file
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Bro.M071.Process.Properties
{
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
    {
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        public static Settings Default
        {
            get
            {
                return defaultInstance;
            }
        }
    }
}
src/Bro.M071.Process/Properties/Settings.settings
New file
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
  <Profiles>
    <Profile Name="(Default)" />
  </Profiles>
  <Settings />
</SettingsFile>
src/Bro.M071.Process/packages.config
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Autofac" version="4.9.4" targetFramework="net452" />
</packages>
src/Bro.Process/ProcessConfig.cs
@@ -75,6 +75,7 @@
            configs.AddRange(CameraConfigCollection);
            configs.AddRange(PLCConfigCollection);
            configs.AddRange(DeviceConfigs);
            return configs;
        }
src/Bro.UI.Model.Winform/Element/CrossHair.cs
@@ -117,15 +117,15 @@
        public override void Draw(Graphics g)
        {
            g.DrawRectangle(Pen, CenterPoint.U - HalfRectangleSize, CenterPoint.V - HalfRectangleSize, HalfRectangleSize * 2, HalfRectangleSize * 2);
            g.DrawRectangle(Pen, (float)CenterPoint.U - HalfRectangleSize, (float)CenterPoint.V - HalfRectangleSize, HalfRectangleSize * 2, HalfRectangleSize * 2);
            g.DrawLine(Pen, CenterPoint.U, CenterPoint.V - HalfLength, CenterPoint.U, CenterPoint.V + HalfLength);
            g.DrawLine(Pen, CenterPoint.U - HalfLength, CenterPoint.V, CenterPoint.U + HalfLength, CenterPoint.V);
            g.DrawLine(Pen, (float)CenterPoint.U, (float)CenterPoint.V - HalfLength, (float)CenterPoint.U, (float)CenterPoint.V + HalfLength);
            g.DrawLine(Pen, (float)CenterPoint.U - HalfLength, (float)CenterPoint.V, (float)CenterPoint.U + HalfLength, (float)CenterPoint.V);
            if (IsShowRemark)
            {
                string info = Index.ToString() + ":(" + CenterPoint.X.ToString() + ";" + CenterPoint.Y.ToString() + ")";
                g.DrawString(info, new Font("NewRoman", FontSize), new SolidBrush(Pen.Color), new PointF(CenterPoint.U + FontDistance, CenterPoint.V + FontDistance));
                g.DrawString(info, new Font("NewRoman", FontSize), new SolidBrush(Pen.Color), new PointF((float)CenterPoint.U + FontDistance, (float)CenterPoint.V + FontDistance));
            }
        }
src/Bro.UI.Model.Winform/Element/CrossHairWithAngle.cs
@@ -179,18 +179,18 @@
        public override void Draw(Graphics g)
        {
            g.DrawRectangle(Pen, ImagePoint.X - HalfRectangleSize, ImagePoint.Y - HalfRectangleSize, HalfRectangleSize * 2, HalfRectangleSize * 2);
            g.DrawRectangle(Pen, (float)ImagePoint.X - HalfRectangleSize, (float)ImagePoint.Y - HalfRectangleSize, HalfRectangleSize * 2, HalfRectangleSize * 2);
            g.DrawLine(Pen, ImagePoint.X, ImagePoint.Y - HalfLength, ImagePoint.X, ImagePoint.Y + HalfLength);
            g.DrawLine(Pen, ImagePoint.X - HalfLength, ImagePoint.Y, ImagePoint.X + HalfLength, ImagePoint.Y);
            g.DrawLine(Pen, (float)ImagePoint.X, (float)ImagePoint.Y - HalfLength, (float)ImagePoint.X, (float)ImagePoint.Y + HalfLength);
            g.DrawLine(Pen, (float)ImagePoint.X - HalfLength, (float)ImagePoint.Y, (float)ImagePoint.X + HalfLength, (float)ImagePoint.Y);
            Pen arrowPen = new Pen(Pen.Color, Pen.Width + 3.5f);
            arrowPen.StartCap = System.Drawing.Drawing2D.LineCap.AnchorMask;
            arrowPen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
            g.DrawLine(arrowPen, ImagePoint.X, ImagePoint.Y, ImagePoint.X + (float)(HalfLength * Math.Cos(Angle * Math.PI / 180.0)), ImagePoint.Y - (float)(HalfLength * Math.Sin(Angle * Math.PI / 180.0)));
            g.DrawLine(arrowPen, (float)ImagePoint.X, (float)ImagePoint.Y, (float)ImagePoint.X + (float)(HalfLength * Math.Cos(Angle * Math.PI / 180.0)), (float)ImagePoint.Y - (float)(HalfLength * Math.Sin(Angle * Math.PI / 180.0)));
            string info = $"{Index}  图像坐标:{ImagePoint.X},{ImagePoint.Y}\r\n机台坐标:{PlatPoint.X},{PlatPoint.Y}\r\n角度:{Angle}";
            g.DrawString(info, new Font("NewRoman", FontSize), new SolidBrush(Pen.Color), new PointF(ImagePoint.X + FontDistance, ImagePoint.Y + FontDistance));
            g.DrawString(info, new Font("NewRoman", FontSize), new SolidBrush(Pen.Color), new PointF((float)ImagePoint.X + FontDistance, (float)ImagePoint.Y + FontDistance));
        }
        public override bool IsMouseHover(Point p)
src/Bro.UI.Model.Winform/Element/PointIndicator.cs
@@ -68,12 +68,12 @@
        public override void Draw(Graphics g)
        {
            g.DrawEllipse(Pen, new RectangleF(Center.X - Radius, Center.Y - Radius, Radius * 2, Radius * 2));
            g.DrawLine(Pen, Center.X, Center.Y - Radius, Center.X, Center.Y + Radius);
            g.DrawLine(Pen, Center.X - Radius, Center.Y, Center.X + Radius, Center.Y);
            g.DrawEllipse(Pen, new RectangleF((float)Center.X - Radius, (float)Center.Y - Radius, Radius * 2, Radius * 2));
            g.DrawLine(Pen, (float)Center.X, (float)Center.Y - Radius, (float)Center.X, (float)Center.Y + Radius);
            g.DrawLine(Pen, (float)Center.X - Radius, (float)Center.Y, (float)Center.X + Radius, (float)Center.Y);
            string info = GetDisplayText();
            g.DrawString(info, new Font("NewRoman", FontSize), new SolidBrush(Pen.Color), new PointF(Center.X - Radius - FontDistance, Center.Y - Radius - FontDistance));
            g.DrawString(info, new Font("NewRoman", FontSize), new SolidBrush(Pen.Color), new PointF((float)Center.X - Radius - FontDistance, (float)Center.Y - Radius - FontDistance));
        }
        public override string GetDisplayText()
src/Bro.UI.Model.Winform/Element/ROI_Circle.cs
@@ -45,7 +45,7 @@
        public override void Draw(Graphics g)
        {
            g.FillEllipse(new SolidBrush(Color.Red), new RectangleF(Center.X, Center.Y, 1.0f, 1.0f));
            g.FillEllipse(new SolidBrush(Color.Red), new RectangleF((float)Center.X, (float)Center.Y, 1.0f, 1.0f));
            Pen pen = null;
            if (ROI_Index == "1")
@@ -56,7 +56,7 @@
                    "P1",
                    new Font(new FontFamily("宋体"), 40.0f),
                    new SolidBrush(Color.Red),
                    new PointF(Center.X - Radius - 80, Center.Y - Radius - 40)
                    new PointF((float)Center.X - Radius - 80, (float)Center.Y - Radius - 40)
                    );
            }
            else
@@ -65,11 +65,11 @@
            }
            pen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
            g.DrawEllipse(pen, Center.X - Radius, Center.Y - Radius, Radius * 2, Radius * 2);
            g.DrawEllipse(pen, (float)Center.X - Radius, (float)Center.Y - Radius, Radius * 2, Radius * 2);
            if (ROI_Index == "1")
            {
                g.DrawEllipse(pen, Center.X - Radius - 15, Center.Y - Radius - 15, Radius * 2 + 30, Radius * 2 + 30);
                g.DrawEllipse(pen, (float)Center.X - Radius - 15, (float)Center.Y - Radius - 15, Radius * 2 + 30, Radius * 2 + 30);
            }
        }