1. 添加标定操作及界面
2. 修改配置界面方法调用参数
3. SeerAGV添加电池信息监听
4. 添加AGV电池充电操作
5. 修改AGV状态变化操作,添加操作锁
15个文件已修改
949 ■■■■ 已修改文件
src/A032.Config/ConfigFrm.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Config/ConfigFrm.designer.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/AGVBindUnit.cs 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/AGVPath.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/Calibration/CalibrationConfig.cs 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/Calibration/CtrlCalib9PDynamic.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/Calibration/FrmCalib9PDynamic.cs 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/Forms/OperationConfigBindFrm.cs 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/ProcessControl.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/ProcessControl_Calibration.cs 372 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Process/ProcessControl_Method.cs 157 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Helper/UIHelper.cs 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Interface/IStationConfig.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.SeerAGV/SeerAGVConfig.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.SeerAGV/SeerAGVDriver.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/A032.Config/ConfigFrm.cs
@@ -42,6 +42,8 @@
                {
                    InitialCalibrationMethod();
                    InitialAllTestMethod();
                    InitialDevices();
                }
            }
        }
@@ -285,7 +287,7 @@
        private void btnStartCalibration_Click(object sender, EventArgs e)
        {
            btnStartCalibration.Enabled = false;
            IDevice device = cboCalibDevices.SelectedItem as IDevice;
            try
            {
                IOperationConfig config = propCalibrationConfig.SelectedObject as IOperationConfig;
@@ -294,7 +296,7 @@
                    config.InputPara = null;
                }
                _calibrationMethod.Invoke(Process, new object[] { config });
                _calibrationMethod.Invoke(Process, new object[] { config, device });
            }
            catch (Exception ex)
            {
@@ -314,6 +316,16 @@
        #region Test
        Dictionary<ProcessMethodAttribute, MethodInfo> _allTestMethod = new Dictionary<ProcessMethodAttribute, MethodInfo>();
        MethodInfo _testMethod = null;
        List<IDevice> _deviceList = new List<IDevice>();
        private void InitialDevices()
        {
            _deviceList = (Process as ProcessControl).GetDeviceList();
            List<ISimpleDevice> list = _deviceList.Select(u => u as ISimpleDevice).ToList();
            UIHelper.SetCombo(cboCalibDevices, list, "Name", "Id");
            UIHelper.SetCombo(cboDevices, list, "Name", "Id");
        }
        private void InitialAllTestMethod()
        {
@@ -337,28 +349,29 @@
        private void cboTestMethod_SelectedIndexChanged(object sender, EventArgs e)
        {
            //if (cboTestMethod.SelectedIndex >= 0)
            //{
            //    string methodCode = cboTestMethod.SelectedValue.ToString();
            //    var attr = _allTestMethod.Keys.FirstOrDefault(u => u.MethodCode == methodCode);
            //    if (attr == null)
            //        return;
            if (cboTestMethod.SelectedIndex >= 0)
            {
                string methodCode = cboTestMethod.SelectedValue.ToString();
                var attr = _allTestMethod.Keys.FirstOrDefault(u => u.MethodCode == methodCode);
                if (attr == null)
                    return;
            //    _testMethod = _allTestMethod[attr];
                _testMethod = _allTestMethod[attr];
            //    if (Process.StationConfig.ProcessOpConfigDict.Keys.Contains(methodCode))
            //    {
            //        propGridTestMethod.SelectedObject = Process.StationConfig.ProcessOpConfigDict[methodCode];
            //    }
            //    else
            //    {
            //        MessageBox.Show(@"Config of " + methodCode + @" is not found");
            //    }
            //}
                if (Process.StationConfig.ProcessOpConfigDict.Keys.Contains(methodCode))
                {
                    propGridTestMethod.SelectedObject = Process.StationConfig.ProcessOpConfigDict[methodCode];
                }
                else
                {
                    MessageBox.Show(@"Config of " + methodCode + @" is not found");
                }
            }
        }
        private void btnManualTrigger_Click(object sender, EventArgs e)
        {
            IDevice device = cboDevices.SelectedItem as IDevice;
            new Task((m) =>
            {
                MethodInfo method = m as MethodInfo;
@@ -370,14 +383,13 @@
                    invokeTimes = 1;
                }
                ProcessResponse response = null;
                if (invokeTimes == 1)
                {
                    try
                    {
                        Stopwatch sw = new Stopwatch();
                        sw.Start();
                        response = method.Invoke(Process, new object[] { propGridTestMethod.SelectedObject as IOperationConfig }) as ProcessResponse;
                        response = method.Invoke(Process, new object[] { propGridTestMethod.SelectedObject as IOperationConfig, device }) as ProcessResponse;
                        sw.Stop();
                        //RecordMsg($"{method.Name}调用耗时:{sw.ElapsedMilliseconds}ms");
src/A032.Config/ConfigFrm.designer.cs
@@ -75,6 +75,10 @@
            this.cboProductionCode = new System.Windows.Forms.ComboBox();
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            this.tsslLoginStatus = new System.Windows.Forms.ToolStripStatusLabel();
            this.cboDevices = new System.Windows.Forms.ComboBox();
            this.label5 = new System.Windows.Forms.Label();
            this.label6 = new System.Windows.Forms.Label();
            this.cboCalibDevices = new System.Windows.Forms.ComboBox();
            this.tabControl1.SuspendLayout();
            this.tbConfig.SuspendLayout();
            this.tbCalibration.SuspendLayout();
@@ -162,6 +166,8 @@
            // 
            // tbCalibration
            // 
            this.tbCalibration.Controls.Add(this.label6);
            this.tbCalibration.Controls.Add(this.cboCalibDevices);
            this.tbCalibration.Controls.Add(this.label1);
            this.tbCalibration.Controls.Add(this.propCalibrationConfig);
            this.tbCalibration.Controls.Add(this.btnStartCalibration);
@@ -177,7 +183,7 @@
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(22, 20);
            this.label1.Location = new System.Drawing.Point(262, 12);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(53, 12);
            this.label1.TabIndex = 7;
@@ -211,7 +217,7 @@
            | System.Windows.Forms.AnchorStyles.Right)));
            this.cboCalibrationMethod.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboCalibrationMethod.FormattingEnabled = true;
            this.cboCalibrationMethod.Location = new System.Drawing.Point(12, 41);
            this.cboCalibrationMethod.Location = new System.Drawing.Point(252, 33);
            this.cboCalibrationMethod.Name = "cboCalibrationMethod";
            this.cboCalibrationMethod.Size = new System.Drawing.Size(185, 20);
            this.cboCalibrationMethod.TabIndex = 4;
@@ -220,8 +226,10 @@
            // 
            this.tbTest.Controls.Add(this.label3);
            this.tbTest.Controls.Add(this.txtInvokeTimes);
            this.tbTest.Controls.Add(this.label5);
            this.tbTest.Controls.Add(this.label2);
            this.tbTest.Controls.Add(this.propGridTestMethod);
            this.tbTest.Controls.Add(this.cboDevices);
            this.tbTest.Controls.Add(this.btnTrigger);
            this.tbTest.Controls.Add(this.cboTestMethod);
            this.tbTest.Location = new System.Drawing.Point(4, 22);
@@ -254,7 +262,7 @@
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(21, 13);
            this.label2.Location = new System.Drawing.Point(262, 12);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(53, 12);
            this.label2.TabIndex = 11;
@@ -288,7 +296,7 @@
            | System.Windows.Forms.AnchorStyles.Right)));
            this.cboTestMethod.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboTestMethod.FormattingEnabled = true;
            this.cboTestMethod.Location = new System.Drawing.Point(11, 34);
            this.cboTestMethod.Location = new System.Drawing.Point(252, 33);
            this.cboTestMethod.Name = "cboTestMethod";
            this.cboTestMethod.Size = new System.Drawing.Size(185, 20);
            this.cboTestMethod.TabIndex = 9;
@@ -606,6 +614,43 @@
            this.tsslLoginStatus.Text = "未登录";
            this.tsslLoginStatus.Click += new System.EventHandler(this.tsslLoginStatus_Click);
            // 
            // cboDevices
            //
            this.cboDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboDevices.FormattingEnabled = true;
            this.cboDevices.Location = new System.Drawing.Point(21, 33);
            this.cboDevices.Name = "cboDevices";
            this.cboDevices.Size = new System.Drawing.Size(185, 20);
            this.cboDevices.TabIndex = 9;
            this.cboDevices.SelectedIndexChanged += new System.EventHandler(this.cboTestMethod_SelectedIndexChanged);
            //
            // label5
            //
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(31, 12);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(53, 12);
            this.label5.TabIndex = 11;
            this.label5.Text = "选择设备";
            //
            // label6
            //
            this.label6.AutoSize = true;
            this.label6.Location = new System.Drawing.Point(31, 12);
            this.label6.Name = "label6";
            this.label6.Size = new System.Drawing.Size(53, 12);
            this.label6.TabIndex = 13;
            this.label6.Text = "选择设备";
            //
            // cboCalibDevices
            //
            this.cboCalibDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboCalibDevices.FormattingEnabled = true;
            this.cboCalibDevices.Location = new System.Drawing.Point(21, 33);
            this.cboCalibDevices.Name = "cboCalibDevices";
            this.cboCalibDevices.Size = new System.Drawing.Size(185, 20);
            this.cboCalibDevices.TabIndex = 12;
            //
            // ConfigFrm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
@@ -688,6 +733,10 @@
        private System.Windows.Forms.StatusStrip statusStrip1;
        private System.Windows.Forms.ToolStripStatusLabel tsslLoginStatus;
        private System.Windows.Forms.CheckBox chkHardwareTrigger;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.ComboBox cboDevices;
        private System.Windows.Forms.Label label6;
        private System.Windows.Forms.ComboBox cboCalibDevices;
    }
}
src/A032.Process/AGVBindUnit.cs
@@ -191,6 +191,35 @@
            //TaskList.CollectionChanged += TaskList_CollectionChanged;
        }
        object agvStatusLock = new object();
        public bool SetAGVStatus(TaskStatus status)
        {
            lock (agvStatusLock)
            {
                switch (status)
                {
                    case TaskStatus.Available:
                        break;
                    case TaskStatus.Running:
                        if (AGVStatus == TaskStatus.Available)
                        {
                            AGVStatus = status;
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    case TaskStatus.Warning:
                        break;
                    default:
                        break;
                }
                AGVStatus = status;
                return true;
            }
        }
        //private void TaskList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        //{
        //    if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
@@ -288,9 +317,9 @@
            {
                ProcessConfig config = scope.Resolve<ProcessConfig>();
                config.RobotConfigCollection.ForEach(plc =>
                config.RobotConfigCollection.ForEach(robot =>
                {
                    _hash[plc.ID] = plc.Name;
                    _hash[robot.ID] = robot.Name;
                });
            }
        }
@@ -304,9 +333,9 @@
            {
                ProcessConfig config = scope.Resolve<ProcessConfig>();
                config.CameraConfigCollection.ForEach(plc =>
                config.CameraConfigCollection.ForEach(camera =>
                {
                    _hash[plc.ID] = plc.Name;
                    _hash[camera.ID] = camera.Name;
                });
            }
        }
src/A032.Process/AGVPath.cs
@@ -25,6 +25,8 @@
        LoadFullTray = 3,
        [Description("卸载满Tray地点")]
        UnloadFullTray = 4,
        [Description("充电地点")]
        Charge = 5,
    }
    public class PathPosition : IComplexDisplay
@@ -73,7 +75,7 @@
        [Description("该位置标准点位信息")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
        public CustomizedPoint StandardPoint { get; set; } = new CustomizedPoint();
        public CustomizedPointWithAngle StandardPoint { get; set; } = new CustomizedPointWithAngle();
        [Category("视觉配置")]
        [Description("该位置拍摄配置")]
@@ -102,7 +104,23 @@
                config.PositionCollection.ForEach(p =>
                {
                    _hash[p.PositionCode] = $"{p.PositionCode}-{p.Description.GetEnumDescription()}";
                    _hash[p.PositionCode] = $"{p.PositionCode}-{p.PositionNo}-{p.Description.GetEnumDescription()}";
                });
            }
        }
    }
    public class PositionNoConverter : ComboBoxItemTypeConvert
    {
        public override void GetConvertHash()
        {
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                var config = scope.Resolve<ProcessConfig>();
                config.PositionCollection.ForEach(p =>
                {
                    _hash[p.PositionNo] = $"{p.PositionNo}-{p.PositionCode}-{p.Description.GetEnumDescription()}";
                });
            }
        }
src/A032.Process/Calibration/CalibrationConfig.cs
@@ -1,6 +1,7 @@
using Bro.Common.Base;
using Bro.Common.Helper;
using Bro.Common.Model;
using Bro.Common.Model.Interface;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -14,12 +15,12 @@
namespace A032.Process.Calibration
{
    [Device("CalibrationCollection", "多步标定配置", EnumHelper.DeviceAttributeType.OperationConfig)]
    public class CalibrationConfigCollection : OperationConfigBase
    public class CalibrationConfigCollection : OperationConfigBase, IComplexDisplay,IHalconToolPath
    {
        [Category("关联配置")]
        [Description("位置代码")]
        [TypeConverter(typeof(PositionCodeConverter))]
        public string PositionCode { get; set; }
        [Description("位置序号")]
        [TypeConverter(typeof(PositionNoConverter))]
        public int PositionNo { get; set; }
        [Category("关联配置")]
        [Description("适用相机编号")]
@@ -31,10 +32,24 @@
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<CalibrationConfig>), typeof(UITypeEditor))]
        public List<CalibrationConfig> Configs { get; set; } = new List<CalibrationConfig>();
        [Category("显示配置")]
        [Description("是否显示UI,从UI启动标定")]
        public bool IsStartedFromUI { get; set; } = false;
        public string GetDisplayText()
        {
            return $"PositionNo:{PositionNo}; Configs:{Configs.Count}";
        }
        public List<string> GetHalconToolPathList()
        {
            return Configs.SelectMany(c => c.GetHalconToolPathList()).ToList();
        }
    }
    //[Device("Calibration", "单步标定配置", EnumHelper.DeviceAttributeType.OperationConfig)]
    public class CalibrationConfig : OperationConfigBase, IComplexDisplay, INotifyPropertyChanged
    public class CalibrationConfig : OperationConfigBase, IComplexDisplay, INotifyPropertyChanged, IHalconToolPath
    {
        private Bitmap image = null;
        [JsonIgnore]
@@ -63,7 +78,7 @@
        [Description("图像标准点坐标")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
        public CustomizedPoint ImageMarkPoint { get; set; } = new CustomizedPoint();
        public CustomizedPointWithAngle ImageMarkPoint { get; set; } = new CustomizedPointWithAngle();
        [Category("相机配置")]
        [Description("相机操作配置")]
@@ -83,5 +98,10 @@
        {
            return JsonConvert.SerializeObject(this);
        }
        public List<string> GetHalconToolPathList()
        {
            return CameraOpConfig.GetHalconToolPathList();
        }
    }
}
src/A032.Process/Calibration/CtrlCalib9PDynamic.cs
@@ -20,7 +20,7 @@
namespace A032.Process.Calibration
{
    [Device("Calibration_9P_Dynamic", "动态9点标定", EnumHelper.DeviceAttributeType.OperationConfigCtrl)]
    public partial class CtrlCalib9PDynamic : UserControl, IConfigCtrl<CameraBase, CalibrationConfigCollection>
    public partial class CtrlCalib9PDynamic : UserControl//, IConfigCtrl<CameraBase, CalibrationConfigCollection>
    {
        PubSubCenter PubSubCenter = PubSubCenter.GetInstance();
        AutoResetEvent _confirmHandle = new AutoResetEvent(false);
@@ -30,20 +30,29 @@
            InitializeComponent();
        }
        public CtrlCalib9PDynamic(ProcessControl process, IDevice device, IOperationConfig config, Action<List<CalibrationConfig>> finalCalculation)
        public CtrlCalib9PDynamic(ProcessControl process, CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position, Action<AGVBindUnit, int, int> commuAction, Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> finalCalculation)
        {
            InitializeComponent();
            ProcessControl = process;
            Camera = device as CameraBase;
            Config = config as CalibrationConfigCollection;
            Config = calibConfig as CalibrationConfigCollection;
            Camera = bind.Camera as CameraBase;
            Bind = bind;
            Position = position;
            CommuAction = commuAction;
            FinalCalculation = finalCalculation;
        }
        public CalibrationConfigCollection Config { get; set; }
        public CameraBase Camera { get; set; }
        public ProcessControl ProcessControl { get; set; }
        public Action<List<CalibrationConfig>> FinalCalculation { get; set; }
        AGVBindUnit Bind { get; set; }
        PathPosition Position { get; set; }
        Action<AGVBindUnit, int, int> CommuAction;
        CalibrationConfigCollection Config { get; set; }
        CameraBase Camera { get; set; }
        ProcessControl ProcessControl { get; set; }
        Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> FinalCalculation { get; set; }
        public CalibrationConfigCollection GetConfig()
        {
@@ -159,7 +168,7 @@
            if (chkManualConfirm.Checked)
            {
                this.Invoke(new Action(() => btnContinueCalib.Visible = true));                ;
                this.Invoke(new Action(() => btnContinueCalib.Visible = true)); ;
                _confirmHandle.WaitOne();
            }
@@ -188,14 +197,16 @@
        private void btnStartCalib_Click(object sender, EventArgs e)
        {
            if (chkOfflineCalib.Checked)
            {
                //ProcessControl.Calibration_Pick_9P_Dynamic_Offline(Config.Configs);
            }
            else
            {
                //ProcessControl.SendCalibStartSignal(Config.TriggerAddress);
            }
            //if (chkOfflineCalib.Checked)
            //{
            //    //ProcessControl.Calibration_Pick_9P_Dynamic_Offline(Config.Configs);
            //}
            //else
            //{
            //    //ProcessControl.SendCalibStartSignal(Config.TriggerAddress);
            //}
            ProcessControl.MultipleStepsProcess(Config, Bind, CommuAction);
        }
        private void btnLoadOfflineImages_Click(object sender, EventArgs e)
@@ -213,7 +224,7 @@
        private void btnStepRun_Click(object sender, EventArgs e)
        {
            //CalibrationConfig config = propGridConfig.SelectedObject as CalibrationConfig;
            CalibrationConfig config = propGridConfig.SelectedObject as CalibrationConfig;
            //if (!chkOfflineRun.Checked)
            //{
            //    ProcessControl.CalibMarkPoint(Camera, config, _selectedStepIndex + 1);
@@ -224,11 +235,14 @@
            //}
            //tsslInfo.Text = $"单步运算完成。标记点坐标:{config.ImageMarkPoint.X},{config.ImageMarkPoint.Y}";
            ProcessControl.SingleStepProcess(config, CommuAction, Bind, Position.PositionNo, _selectedStepIndex);
            tsslInfo.Text = $"单步运算完成。标记点坐标:{config.ImageMarkPoint.X},{config.ImageMarkPoint.Y}";
        }
        private void btnCalcuMatrix_Click(object sender, EventArgs e)
        {
            FinalCalculation.Invoke(Config.Configs);
            FinalCalculation.Invoke(Config, Bind, Position);
        }
        private void btnSnap_Click(object sender, EventArgs e)
src/A032.Process/Calibration/FrmCalib9PDynamic.cs
@@ -16,25 +16,49 @@
{
    public partial class FrmCalib9PDynamic : Form
    {
        //private CalibrationConfigCollection calibConfig;
        //private AGVBindUnit bind;
        //private PathPosition position;
        //private Action<AGVBindUnit, int, int> sendMessageToRobot_Calibration;
        //private Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> calculateMatrix;
        public FrmCalib9PDynamic()
        {
            InitializeComponent();
        }
        public FrmCalib9PDynamic(ProcessControl process, IDevice device, IOperationConfig config, Action<List<CalibrationConfig>> finalCalculation)
        //public FrmCalib9PDynamic(ProcessControl process, IDevice device, IOperationConfig config, Action<List<CalibrationConfig>> finalCalculation)
        //{
        //    InitializeComponent();
        //    Device = device as CameraBase;
        //    Config = config as CalibrationConfigCollection;
        //    FinalCalculation = finalCalculation;
        //    CtrlCalib9PDynamic = new CtrlCalib9PDynamic(process, device, config, finalCalculation);
        //}
        public FrmCalib9PDynamic(ProcessControl process, CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position, Action<AGVBindUnit, int, int> commuAction, Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> finalCalculation)
        {
            InitializeComponent();
            Device = device as CameraBase;
            Config = config as CalibrationConfigCollection;
            FinalCalculation = finalCalculation;
            CtrlCalib9PDynamic = new CtrlCalib9PDynamic(process, device, config, finalCalculation);
            //Config = calibConfig;
            //Bind = bind;
            //Camera = Bind.Camera;
            //Position = position;
            //CommuAction = commuAction;
            //FinalCalculation = finalCalculation;
            CtrlCalib9PDynamic = new CtrlCalib9PDynamic(process, calibConfig, bind, position, commuAction, finalCalculation);
        }
        CameraBase Device { get; set; }
        CalibrationConfigCollection Config { get; set; }
        //AGVBindUnit Bind { get; set; }
        //CameraBase Camera { get; set; }
        //CalibrationConfigCollection Config { get; set; }
        //PathPosition Position { get; set; }
        //Action<AGVBindUnit, int, int> CommuAction;
        //Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> FinalCalculation { get; set; }
        CtrlCalib9PDynamic CtrlCalib9PDynamic { get; set; }
        Action<List<CalibrationConfig>> FinalCalculation { get; set; }
        private void FrmCalib9PDynamic_Load(object sender, EventArgs e)
        {
src/A032.Process/Forms/OperationConfigBindFrm.cs
@@ -14,6 +14,8 @@
using Bro.Common.Base;
using Bro.Common.Factory;
using A032.Process;
using Bro.Common.Model;
using Autofac;
namespace Bro.Device.Station.Forms
{
@@ -141,38 +143,12 @@
        private void LoadMethodCodes()
        {
            IStationProcess sp = new ProcessControl();
            //switch (StationCode)
            //{
            //    case "S1":
            //        sp = new StationProcess_S1(false);
            //        break;
            //    case "S2":
            //        sp = new StationProcess_S2(false);
            //        break;
            //    case "S3":
            //        sp = new StationProcess_S3(false);
            //        break;
            //    case "S4":
            //        sp = new StationProcess_S4(false);
            //        break;
            //    case "S5":
            //        sp = new StationProcess_S5(false);
            //        break;
            //    case "S6":
            //        sp = new StationProcess_S6(false);
            //        break;
            //    case "S7":
            //        sp = new StationProcess_S7(false);
            //        break;
            //        //case "S0":
            //        //    sp = new StationProcess_S0(false);
            //        //    break;
            //}
            OpBinds = new Dictionary<string, IOperationConfig>();
            ProcessMethodList = sp.CollectProcessMethods();
            using (var scope = GlobalVar.Container.BeginLifetimeScope())
            {
                ProcessMethodList = scope.Resolve<List<ProcessMethodAttribute>>();
            }
            ProcessMethodList.ForEach(u =>
            {
src/A032.Process/ProcessControl.cs
@@ -263,7 +263,7 @@
            });
        }
        Dictionary<string, MethodInfo> InvokeMethodDict = new Dictionary<string, MethodInfo>();
        //Dictionary<string, MethodInfo> InvokeMethodDict = new Dictionary<string, MethodInfo>();
        public List<ProcessMethodAttribute> CollectProcessMethods()
        {
            List<ProcessMethodAttribute> resultList = new List<ProcessMethodAttribute>();
@@ -275,7 +275,7 @@
                if (attr != null)
                {
                    resultList.Add(attr);
                    InvokeMethodDict[attr.MethodCode] = m;
                    //InvokeMethodDict[attr.MethodCode] = m;
                }
            });
@@ -289,19 +289,16 @@
            StationConfig = LoadStationConfig(configPath);
            #region 个别配置的特别处理
            #endregion
            _warningRemains.CollectionChanged -= _warningRemains_CollectionChanged;
            _warningRemains.CollectionChanged += _warningRemains_CollectionChanged;
            InitialPLCs();
            InitialRobots();
            InitialAGVs();
            InitialRobots();
            InitialCameras();
            InitialAGVBindUnit();
            InitialMachineTrayNums();
            AutoFacRegister();
@@ -393,6 +390,7 @@
                agv.OnAGVPositoinChanged = OnAGVPositionChanged;
                agv.OnAGVTaskStatusChanged = OnAGVTaskStatusChanged;
                agv.OnAGVBatteryLvlChanged = OnAGVBatterLvlChanged;
            });
        }
@@ -542,6 +540,11 @@
            {
                InitialHalconTool(c as IHalconToolPath);
            });
            Config.ProcessOpConfigDict.Values.ToList().ForEach(c =>
            {
                InitialHalconTool(c as IHalconToolPath);
            });
            #endregion
        }
src/A032.Process/ProcessControl_Calibration.cs
@@ -3,99 +3,355 @@
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static Bro.Common.Helper.EnumHelper;
namespace A032.Process
{
    public partial class ProcessControl
    {
        CalibReplyMsg _calibReply = new CalibReplyMsg();
        [ProcessMethod("CalibrationCollection", "RobotCalibration", "机器人9点标定", true)]
        public ProcessResponse RobotCalibration(IOperationConfig config)
        public ProcessResponse RobotCalibration(IOperationConfig config, IDevice device)
        {
            CalibrationConfigCollection calibConfig = config as CalibrationConfigCollection;
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.CameraId == calibConfig.CameraId);
            if (bind == null)
            {
                throw new ProcessException("未能获取绑定设备信息");
            }
            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == calibConfig.PositionNo);
            if (position == null)
            {
                throw new ProcessException("未能获取正确位置信息");
            }
            if (calibConfig.Configs.Count < 9)
            {
                throw new ProcessException("请至少配置9个标定点位");
            }
            if (calibConfig.IsStartedFromUI)
            {
                FrmCalib9PDynamic frm = new FrmCalib9PDynamic(this, calibConfig, bind, position, SendMessageToRobot_Calibration, CalculateMatrix);
                frm.ShowDialog();
            }
            else
            {
                MultipleStepsProcess(calibConfig, bind, SendMessageToRobot_Calibration);
                CalculateMatrix(calibConfig, bind, position);
            }
            //for (int i = 0; i < calibConfig.Configs.Count; i++)
            //{
            //    bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.Calibration, Bro.Device.AuboRobot.RobotMsgParas.None, calibConfig.PositionNo, new List<float>() { i + 1 });
            //    _calibReply.CalibHandle.WaitOne();
            //    if (_calibReply.CalibIndex != (i + 1) || _calibReply.CalibPositionNo != calibConfig.PositionNo)
            //    {
            //        throw new ProcessException("标定反馈的索引或位置信息不一致");
            //    }
            //    calibConfig.Configs[i].CurrentPlatPoint = new CustomizedPoint(_calibReply.RobotPosition.X, _calibReply.RobotPosition.Y);
            //    using (HObject hImage = CollectHImage(bind.Camera, calibConfig.Configs[i].CameraOpConfig, "RobotCalibration"))
            //    {
            //        var tool = _halconToolDict[calibConfig.Configs[i].CameraOpConfig.AlgorithemPath];
            //        tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } });
            //        tool.RunProcedure();
            //        float x = (float)tool.GetResultTuple("OUTPUT_X").D;
            //        float y = (float)tool.GetResultTuple("OUTPUT_Y").D;
            //        float angel = (float)tool.GetResultTuple("OUTPUT_Angle").D;
            //        if (x < 0 || y < 0)
            //        {
            //            throw new ProcessException("获取点位信息不正确");
            //        }
            //        calibConfig.Configs[i].ImageMarkPoint = new CustomizedPointWithAngle(x, y, angel);
            //    }
            //}
            //HOperatorSet.VectorToHomMat2d(new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.Y).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.Y).ToArray()), out HTuple matrix);
            //var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode);
            //if (visionConfig != null)
            //{
            //    visionConfig.Matrix = new List<double>() { matrix[0], matrix[1], 0, matrix[3], matrix[4], 0 };
            //}
            //else
            //{
            //    Config.VisionConfigCollection.Add(new PositionVisionConfig()
            //    {
            //        CameraId = bind.CameraId,
            //        PositionCode = position.PositionCode,
            //        Matrix = new List<double>() { matrix[0], matrix[1], 0, matrix[3], matrix[4], 0 },
            //    });
            //}
            return new ProcessResponse(true);
        }
        [ProcessMethod("CalibrationCollection", "StandardPointCalibration", "标准点位标定", true)]
        public ProcessResponse StandardPointCalibration(IOperationConfig config)
        public ProcessResponse StandardPointCalibration(IOperationConfig config, IDevice device)
        {
            return new ProcessResponse(true);
        }
            CalibrationConfigCollection calibConfig = config as CalibrationConfigCollection;
        public void SendCalibrationStartSignal(int stepNum)
        {
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.CameraId == calibConfig.CameraId);
            if (bind == null)
            {
                throw new ProcessException("未能获取绑定设备信息");
            }
        }
            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == calibConfig.PositionNo);
            if (position == null)
            {
                throw new ProcessException("未能获取正确位置信息");
            }
        private int MultipleStepsCalibration(CalibrationConfigCollection configs, Action<List<CalibrationConfig>> FinalCalculation)
        {
            throw new NotImplementedException();
            if (calibConfig.Configs.Count < 1)
            {
                throw new ProcessException("请至少配置1个标定点位");
            }
            //configs.Configs.ForEach(c =>
            if (calibConfig.IsStartedFromUI)
            {
                FrmCalib9PDynamic frm = new FrmCalib9PDynamic(this, calibConfig, bind, position, SendMessageToRobot_Standard, CalculateStandardPoint);
                frm.ShowDialog();
            }
            else
            {
                MultipleStepsProcess(calibConfig, bind, SendMessageToRobot_Standard);
                CalculateStandardPoint(calibConfig, bind, position);
            }
            //for (int i = 0; i < calibConfig.Configs.Count; i++)
            //{
            //    c.Image = null;
            //});
            //    bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.Calibration, Bro.Device.AuboRobot.RobotMsgParas.None, calibConfig.PositionNo, new List<float>() { i + 1 });
            //if (string.IsNullOrWhiteSpace(configs.CameraId) || !CameraDict.ContainsKey(configs.CameraId))
            //{
            //    throw new ProcessException("标定配置未配置正确的相机编号");
            //}
            //    _calibReply.CalibHandle.WaitOne();
            //if (string.IsNullOrWhiteSpace(configs.PositionCode))
            //{
            //    throw new ProcessException("标定配置未指定路径位置");
            //}
            //CameraBase camera = CameraDict[configs.CameraId];
            //FrmCalib9PDynamic frm9PDynamic = new FrmCalib9PDynamic(this, camera, configs, FinalCalculation);
            //frm9PDynamic.ShowDialog();
            //if (configs.InputPara == null || configs.InputPara.Count <= 0)
            //{
            //    return (int)PLCReplyValue.NG;
            //}
            //if (configs.InputPara[0] <= 0 || configs.InputPara[0] > configs.Configs.Count)
            //{
            //    configs.InputPara = null;
            //    return (int)PLCReplyValue.IGNORE;
            //}
            //int sequence = configs.InputPara[0];
            ////第一次
            //if (sequence == 1)
            //{
            //    configs.Configs.ForEach(c =>
            //    if (_calibReply.CalibPositionNo != calibConfig.PositionNo)
            //    {
            //        c.Image = null;
            //        c.OfflineImagePath = "";
            //        c.CurrentPlatPoint = new CustomizedPoint();
            //        c.ImageMarkPoint = new CustomizedPoint();
            //        throw new ProcessException("标定反馈的位置信息不一致");
            //    }
            //    calibConfig.Configs[i].CurrentPlatPoint = new CustomizedPoint(_calibReply.RobotPosition.X, _calibReply.RobotPosition.Y);
            //    using (HObject hImage = CollectHImage(bind.Camera, calibConfig.Configs[i].CameraOpConfig, "RobotCalibration"))
            //    {
            //        var tool = _halconToolDict[calibConfig.Configs[i].CameraOpConfig.AlgorithemPath];
            //        tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } });
            //        tool.RunProcedure();
            //        float x = (float)tool.GetResultTuple("OUTPUT_X").D;
            //        float y = (float)tool.GetResultTuple("OUTPUT_Y").D;
            //        float angel = (float)tool.GetResultTuple("OUTPUT_Angle").D;
            //        if (x < 0 || y < 0)
            //        {
            //            throw new ProcessException("获取点位信息不正确");
            //        }
            //        calibConfig.Configs[i].ImageMarkPoint = new CustomizedPointWithAngle(x, y, angel);
            //    }
            //}
            //CustomizedPointWithAngle markPoint = calibConfig.Configs[0].ImageMarkPoint;
            //var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode);
            //if (visionConfig != null)
            //{
            //    visionConfig.StandardPoint = new CustomizedPointWithAngle(markPoint.X, markPoint.Y, markPoint.Angle);
            //}
            //else
            //{
            //    Config.VisionConfigCollection.Add(new PositionVisionConfig()
            //    {
            //        CameraId = bind.CameraId,
            //        PositionCode = position.PositionCode,
            //        StandardPoint = new CustomizedPointWithAngle(markPoint.X, markPoint.Y, markPoint.Angle),
            //    });
            //}
            //CalibrationConfig stepConfig = configs.Configs[sequence - 1];
            return new ProcessResponse(true);
        }
            //HDevEngineTool tool = _halconToolDict[]
        public void CalculateMatrix(CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position)
        {
            //HOperatorSet.VectorToHomMat2d(new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.Y).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.Y).ToArray()), out HTuple matrix);
            //CalibMarkPoint(camera, stepConfig, sequence);
            List<double> matrix = GetMovementMatrix(calibConfig.Configs.Select(u => u.ImageMarkPoint as CustomizedPoint).ToList(), calibConfig.Configs.Select(u => u.CurrentPlatPoint).ToList(), out string msg);
            ////获取当前平台点位
            //stepConfig.CurrentPlatPoint = new CustomizedPoint(_monitorList.Skip(locationStartIndex).Take(4).ToList());
            ////stepConfig.CurrentPlatPoint = new CustomizedPoint(configs.InputPara.Skip(1).Take(4).ToList());
            var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode);
            if (visionConfig != null)
            {
                visionConfig.Matrix = new List<double>() { matrix[0], matrix[1], 0, matrix[3], matrix[4], 0 };
            }
            else
            {
                Config.VisionConfigCollection.Add(new PositionVisionConfig()
                {
                    CameraId = bind.CameraId,
                    PositionCode = position.PositionCode,
                    Matrix = new List<double>() { matrix[0], matrix[1], 0, matrix[3], matrix[4], 0 },
                });
            }
            ////最后一次
            //if (sequence == configs.Configs.Count)
            PubSubCenter.Publish(PubTag.CalibAllDone.ToString(), "", msg, true);
        }
        /// <summary>
        /// 获取标定点的转换矩阵
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        protected List<double> GetMovementMatrix(List<CustomizedPoint> imagePoints, List<CustomizedPoint> platPoints, out string msg)
        {
            HTuple uList, vList, xList, yList, matrix;
            ConvertPointToHTuple(imagePoints, out uList, out vList);
            ConvertPointToHTuple(platPoints, out xList, out yList);
            HOperatorSet.VectorToHomMat2d(uList, vList, xList, yList, out matrix);
            double sum = 0;
            for (int i = 0; i < imagePoints.Count; i++)
            {
                HOperatorSet.AffineTransPoint2d(matrix, imagePoints[i].X, imagePoints[i].Y, out HTuple m, out HTuple n);
                sum += Math.Sqrt((Math.Pow((m.D - platPoints[i].X), 2) + Math.Pow((n.D - platPoints[i].Y), 2)));
            }
            //sum = ((sum / (double)Config.LengthPulseRatio) * 100.0) / ((double)imagePoints.Count);
            sum = sum / (double)imagePoints.Count;
            msg = $"标定点数量:{imagePoints.Count};单点误差:{sum.ToString()}脉冲";
            return matrix.DArr.ToList();
        }
        private void ConvertPointToHTuple(List<CustomizedPoint> point, out HTuple x, out HTuple y)
        {
            x = new HTuple(point.Select(p => p.X).ToArray());
            y = new HTuple(point.Select(p => p.Y).ToArray());
        }
        public void CalculateStandardPoint(CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position)
        {
            //var bind = Config.AGVBindCollection.FirstOrDefault(u => u.CameraId == calibConfig.CameraId);
            //if (bind == null)
            //{
            //    FinalCalculation?.Invoke(configs.Configs);
            //    throw new ProcessException("未能获取绑定设备信息");
            //}
            //configs.InputPara = null;
            //return (int)PLCReplyValue.OK;
            //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == calibConfig.PositionNo);
            //if (position == null)
            //{
            //    throw new ProcessException("未能获取正确位置信息");
            //}
            CustomizedPointWithAngle markPoint = calibConfig.Configs[0].ImageMarkPoint;
            var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode);
            if (visionConfig != null)
            {
                visionConfig.StandardPoint = new CustomizedPointWithAngle(markPoint.X, markPoint.Y, markPoint.Angle);
            }
            else
            {
                Config.VisionConfigCollection.Add(new PositionVisionConfig()
                {
                    CameraId = bind.CameraId,
                    PositionCode = position.PositionCode,
                    StandardPoint = new CustomizedPointWithAngle(markPoint.X, markPoint.Y, markPoint.Angle),
                });
            }
            PubSubCenter.Publish(PubTag.CalibAllDone.ToString(), "", $"标定完成,标准点:{markPoint.GetDisplayText()}", true);
        }
        public void MultipleStepsProcess(CalibrationConfigCollection calibConfig, AGVBindUnit bind, Action<AGVBindUnit, int, int> sendMessageToRobot)
        {
            for (int i = 0; i < calibConfig.Configs.Count; i++)
            {
                SingleStepProcess(calibConfig.Configs[i], sendMessageToRobot, bind, calibConfig.PositionNo, i);
            }
        }
        public void SendMessageToRobot_Calibration(AGVBindUnit bind, int positionNo, int index)
        {
            bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.Calibration, Bro.Device.AuboRobot.RobotMsgParas.None, positionNo, new List<float>() { index + 1 });
            _calibReply.CalibHandle.WaitOne();
            if (_calibReply.CalibIndex != (index + 1) || _calibReply.CalibPositionNo != positionNo)
            {
                throw new ProcessException("标定反馈的索引或位置信息不一致");
            }
        }
        public void SendMessageToRobot_Standard(AGVBindUnit bind, int positionNo, int index)
        {
            bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.StandardPoint, Bro.Device.AuboRobot.RobotMsgParas.None, positionNo);
            _calibReply.CalibHandle.WaitOne();
            if (_calibReply.CalibPositionNo != positionNo)
            {
                throw new ProcessException("标定反馈的位置信息不一致");
            }
        }
        //PubSubCenter.Subscribe(PubTag.CalibStepDone.ToString(), CalibStepDone);
        //PubSubCenter.Subscribe(PubTag.CalibAllDone.ToString(), CalibAllDone);
        public void SingleStepProcess(CalibrationConfig config, Action<AGVBindUnit, int, int> sendMessageToRobot, AGVBindUnit bind, int positionNo, int index)
        {
            sendMessageToRobot.Invoke(bind, positionNo, index);
            config.CurrentPlatPoint = new CustomizedPoint(_calibReply.RobotPosition.X, _calibReply.RobotPosition.Y);
            using (HObject hImage = CollectHImage(bind.Camera, config.CameraOpConfig, "RobotCalibration"))
            {
                var tool = _halconToolDict[config.CameraOpConfig.AlgorithemPath];
                tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } });
                tool.RunProcedure();
                float x = (float)tool.GetResultTuple("OUTPUT_X").D;
                float y = (float)tool.GetResultTuple("OUTPUT_Y").D;
                float angel = (float)tool.GetResultTuple("OUTPUT_Angle").D;
                if (x < 0 || y < 0)
                {
                    throw new ProcessException("获取点位信息不正确");
                }
                config.ImageMarkPoint = new CustomizedPointWithAngle(x, y, angel);
            }
            PubSubCenter.Publish(PubTag.CalibStepDone.ToString(), index, "", true);
        }
    }
    public class CalibReplyMsg
    {
        public AutoResetEvent CalibHandle { get; set; } = new AutoResetEvent(false);
        public int CalibIndex { get; set; } = 0;
        public int CalibPositionNo { get; set; }
        public CustomizedPoint RobotPosition { get; set; } = new CustomizedPoint();
    }
}
src/A032.Process/ProcessControl_Method.cs
@@ -20,9 +20,49 @@
{
    public partial class ProcessControl
    {
        const int WAITTIME = 5000;
        Dictionary<int, int> machineFullTrayDict = new Dictionary<int, int>();
        Dictionary<int, int> machineEmptyTrayDict = new Dictionary<int, int>();
        List<TaskAssignInfo> taskAssignedList = new List<TaskAssignInfo>();
        #region AGV事件
        private void OnAGVBatterLvlChanged(SeerAGVDriver agv, float batterLvl)
        {
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id);
            SeerAGVInitialConfig iConfig = agv.InitialConfig as SeerAGVInitialConfig;
            if (batterLvl <= iConfig.BatteryLvlToCharge)
            {
                Task.Run(() =>
                {
                    var position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.Charge);
                    if (position == null)
                    {
                        throw new ProcessException("未找到充电地点");
                    }
                    while (bind.UnitStatus != TaskStatus.Available)
                    {
                        if (bind.SetAGVStatus(TaskStatus.Running))
                        {
                            bind.AGV.TaskOrder(position.PositionCode);
                            break;
                        }
                        Thread.Sleep(WAITTIME);
                    }
                });
                return;
            }
            if (batterLvl >= iConfig.BatteryLvlChargeDone)
            {
                bind.SetAGVStatus(TaskStatus.Available);
                return;
            }
        }
        private void OnAGVTaskStatusChanged(SeerAGVDriver agv, AGVTaskStatus taskStatus)
        {
@@ -42,7 +82,7 @@
                //    bind.RobotStatus = TaskStatus.Running;
                //}
                bind.AGVStatus = TaskStatus.Available;
                bind.SetAGVStatus(TaskStatus.Available);
            }
        }
@@ -64,7 +104,7 @@
                //    bind.RobotStatus = TaskStatus.Running;
                //}
                bind.AGVStatus = TaskStatus.Available;
                bind.SetAGVStatus(TaskStatus.Available);
                PathPosition position = Config.PositionCollection.FirstOrDefault(p => p.PositionCode == bind.AGVDest);
                switch (position.Description)
@@ -85,6 +125,7 @@
                }
            }
        }
        #endregion
        private void OnRobotMsgReceived(DateTime dt, AuboRobotDriver robot, RobotMsg msg)
        {
@@ -210,12 +251,17 @@
                    break;
                case RobotMsgAction.Calibration:
                    {
                        _calibReply.CalibIndex = int.Parse(msg.Datas[4]);
                        _calibReply.CalibPositionNo = msg.Para2;
                        _calibReply.RobotPosition = new CustomizedPoint(float.Parse(msg.Datas[0]), float.Parse(msg.Datas[1]));
                        _calibReply.CalibHandle.Set();
                    }
                    break;
                case RobotMsgAction.StandardPoint:
                    {
                        _calibReply.CalibPositionNo = msg.Para2;
                        _calibReply.CalibHandle.Set();
                    }
                    break;
                default:
@@ -309,7 +355,7 @@
                    while (bind.IsEmptyTrayEmpty && !bind.IsEmptyTrayTaskAssigned)
                    {
                        //if (bind.TaskList.Count == 0)
                        if (bind.AGVStatus == TaskStatus.Available && bind.RobotStatus == TaskStatus.Available)
                        if (bind.UnitStatus == TaskStatus.Available)
                        {
                            //List<AGVTaskModel> models = new List<AGVTaskModel>();
                            //models.Add(new AGVTaskModel(TaskAvailableLevel.Both, "AGV_LoadEmptyTray"));
@@ -317,12 +363,14 @@
                            //AddNewTaskToBind(device.Id, models);
                            AGV_LoadEmptyTray(bind.Id);
                            bind.IsEmptyTrayTaskAssigned = true;
                            if (AGV_LoadEmptyTray(bind.Id))
                            {
                                bind.IsEmptyTrayTaskAssigned = true;
                            }
                        }
                        else
                        {
                            Thread.Sleep(500);
                            Thread.Sleep(WAITTIME);
                        }
                    }
                });
@@ -351,7 +399,7 @@
                    while (bind.IsFullTrayFull && !bind.IsFullTrayTaskAssigned)
                    {
                        //if (bind.TaskList.Count == 0)
                        if (bind.AGVStatus == TaskStatus.Available && bind.RobotStatus == TaskStatus.Available)
                        if (bind.UnitStatus == TaskStatus.Available)
                        {
                            //List<AGVTaskModel> models = new List<AGVTaskModel>();
                            //models.Add(new AGVTaskModel(TaskAvailableLevel.Both, "AGV_UnloadFullTray"));
@@ -359,13 +407,14 @@
                            //AddNewTaskToBind(device.Id, models);
                            AGV_UnloadFullTray(bind.Id);
                            bind.IsFullTrayTaskAssigned = true;
                            if (AGV_UnloadFullTray(bind.Id))
                            {
                                bind.IsFullTrayTaskAssigned = true;
                            }
                        }
                        else
                        {
                            Thread.Sleep(500);
                            Thread.Sleep(WAITTIME);
                        }
                    }
                });
@@ -460,7 +509,7 @@
        //    return new ProcessResponse(true);
        //}
        public void AGV_LoadEmptyTray(string bindId)
        public bool AGV_LoadEmptyTray(string bindId)
        {
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId);
            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray);
@@ -470,10 +519,17 @@
                throw new ProcessException("路径配置未设置空Tray上料点");
            }
            bind.AGVDest = position.PositionCode;
            bind.AGV.TaskOrder(position.PositionCode);
            if (bind.SetAGVStatus(TaskStatus.Running))
            {
                bind.AGVDest = position.PositionCode;
                bind.AGV.TaskOrder(position.PositionCode);
            bind.AGVStatus = TaskStatus.Running;
                return true;
            }
            else
            {
                return false;
            }
        }
        //[ProcessMethod("", "AfterEmptyTrayPositionArrived", "到达空Tray上料点", true)]
@@ -589,20 +645,21 @@
                            //bind.AddTask(model_AGV);
                            //bind.AddTask(model_Robot);
                            AGV_UnloadEmptyTray(bind.Id, position);
                            taskStatus.IsTaskAssgined = true;
                            taskStatus.AgvId = bind.AGVId;
                            if (AGV_UnloadEmptyTray(bind.Id, position))
                            {
                                taskStatus.IsTaskAssgined = true;
                                taskStatus.AgvId = bind.AGVId;
                            }
                        }
                    }
                    Thread.Sleep(300);
                    Thread.Sleep(WAITTIME);
                }
            });
        }
        //[ProcessMethod("", "AGV_UnloadEmptyTray", "AGV去往卸载空Tray料位置", true)]
        public void AGV_UnloadEmptyTray(string bindId, PathPosition position)
        public bool AGV_UnloadEmptyTray(string bindId, PathPosition position)
        {
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId);
@@ -611,10 +668,17 @@
                throw new ProcessException("路径配置未设置空Tray下料点");
            }
            bind.AGVDest = position.PositionCode;
            bind.AGV.TaskOrder(position.PositionCode);
            if (bind.SetAGVStatus(TaskStatus.Running))
            {
                bind.AGVDest = position.PositionCode;
                bind.AGV.TaskOrder(position.PositionCode);
            bind.AGVStatus = TaskStatus.Running;
                return true;
            }
            else
            {
                return false;
            }
        }
        //[ProcessMethod("", "Robot_UnloadEmptyTray", "机器人运动至空Tray拍照位置", true)]
@@ -756,7 +820,7 @@
                adjust_X = (float)dx_Robot.D;
                adjust_Y = (float)dy_Robot.D;
                adjust_Angle = angle;
                adjust_Angle = visionConfig.StandardPoint.Angle - angle;
            }
            //bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle });
@@ -831,10 +895,11 @@
                            //bind.AddTask(model_AGV);
                            //bind.AddTask(model_Robot);
                            AGV_LoadFullTray(bind.Id, position);
                            taskStatus.IsTaskAssgined = true;
                            taskStatus.AgvId = bind.AGVId;
                            if (AGV_LoadFullTray(bind.Id, position))
                            {
                                taskStatus.IsTaskAssgined = true;
                                taskStatus.AgvId = bind.AGVId;
                            }
                        }
                    }
@@ -844,7 +909,7 @@
        }
        //[ProcessMethod("", "AGV_LoadFullTray", "AGV去往满Tray上料位置", true)]
        public void AGV_LoadFullTray(string bindId, PathPosition position)
        public bool AGV_LoadFullTray(string bindId, PathPosition position)
        {
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId);
@@ -855,10 +920,17 @@
                throw new ProcessException("路径配置未设置满Tray上料点");
            }
            bind.AGVDest = position.PositionCode;
            bind.AGV.TaskOrder(position.PositionCode);
            if (bind.SetAGVStatus(TaskStatus.Running))
            {
                bind.AGVDest = position.PositionCode;
                bind.AGV.TaskOrder(position.PositionCode);
            bind.AGVStatus = TaskStatus.Running;
                return true;
            }
            else
            {
                return false;
            }
            //return new ProcessResponse(true);
        }
@@ -1026,7 +1098,7 @@
                adjust_X = (float)dx_Robot.D;
                adjust_Y = (float)dy_Robot.D;
                adjust_Angle = angle;
                adjust_Angle = visionConfig.StandardPoint.Angle - angle;
            }
            //bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle });
@@ -1060,7 +1132,7 @@
        //    return new ProcessResponse(true);
        //}
        public void AGV_UnloadFullTray(string bindId)
        public bool AGV_UnloadFullTray(string bindId)
        {
            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId);
            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray);
@@ -1070,10 +1142,17 @@
                throw new ProcessException("路径配置未设置满Tray下料点");
            }
            bind.AGVDest = position.PositionCode;
            bind.AGV.TaskOrder(position.PositionCode);
            if (bind.SetAGVStatus(TaskStatus.Running))
            {
                bind.AGVDest = position.PositionCode;
                bind.AGV.TaskOrder(position.PositionCode);
            bind.AGVStatus = TaskStatus.Running;
                return true;
            }
            else
            {
                return false;
            }
        }
        //[ProcessMethod("", "Robot_UnloadFullTray", "机器人卸载满Tray", true)]
src/Bro.Common.Model/Helper/UIHelper.cs
@@ -14,21 +14,30 @@
        {
            cbo.DataSource = dataSource;
            cbo.DisplayMember = display;
            cbo.ValueMember = value;
            if (!string.IsNullOrWhiteSpace(value))
            {
                cbo.ValueMember = value;
            }
        }
        public static void SetCombo(ToolStripComboBox cbo, object dataSource, string display, string value)
        {
            cbo.ComboBox.DataSource = dataSource;
            cbo.ComboBox.DisplayMember = display;
            cbo.ComboBox.ValueMember = value;
            if (!string.IsNullOrWhiteSpace(value))
            {
                cbo.ComboBox.ValueMember = value;
            }
        }
        public static void SetCombo(DataGridViewComboBoxColumn cbo, object dataSource, string display, string value)
        {
            cbo.DataSource = dataSource;
            cbo.DisplayMember = display;
            cbo.ValueMember = value;
            if (!string.IsNullOrWhiteSpace(value))
            {
                cbo.ValueMember = value;
            }
        }
    }
src/Bro.Common.Model/Interface/IStationConfig.cs
@@ -73,7 +73,7 @@
        /// 操作配置的字典集合
        /// Key:MethodCode,Value:操作配置
        /// </summary>
        //Dictionary<string, IOperationConfig> ProcessOpConfigDict { get; set; }
        Dictionary<string, IOperationConfig> ProcessOpConfigDict { get; set; }
        ///// <summary>
        ///// 仰拍相机标定矩阵
@@ -104,7 +104,7 @@
        ///// 仰拍相机拍摄的夹具工件的旋转中心图像坐标 
        ///// </summary>
        //CustomizedPoint RotationCenter { get; set; }
        /// <summary>
        /// 是否输出CSV文件
        /// </summary>
src/Bro.Device.SeerAGV/SeerAGVConfig.cs
@@ -36,6 +36,38 @@
        [Category("监听配置")]
        [Description("是否采用简单监听模式。true:简单模式,只获取任务状态;false:全部模式,获取任务所有信息")]
        public bool IsSimpleMonitor { get; set; } = true;
        private float batteryLvlToCharge = 0.1f;
        [Category("充电配置")]
        [Description("充电电池容量,电池容量低于该值时需要充电")]
        public float BatteryLvlToCharge
        {
            get => batteryLvlToCharge;
            set
            {
                if (value >= 1 || value <= 0)
                {
                    value = 0.1f;
                }
                batteryLvlToCharge = value;
            }
        }
        private float batteryLvlChargeDone = 0.9f;
        [Category("充电配置")]
        [Description("充电完成电池容量,电池容量高于该值时确认充电完成")]
        public float BatteryLvlChargeDone
        {
            get => batteryLvlChargeDone;
            set
            {
                if (value >= 1 || value <= 0)
                {
                    value = 0.9f;
                }
                batteryLvlChargeDone = value;
            }
        }
    }
    [Device("SeerAGV", "SeerAGV", EnumHelper.DeviceAttributeType.OperationConfig)]
@@ -138,6 +170,8 @@
        CancelTask = 0x0BBB,
        PauseTask = 0x0BB9,
        TaskOrder = 0x0BEB,
        QueryBattery = 0x2AFF,
    }
    public enum AGVTaskStatus
src/Bro.Device.SeerAGV/SeerAGVDriver.cs
@@ -19,6 +19,7 @@
    {
        public Action<SeerAGVDriver, string> OnAGVPositoinChanged;
        public Action<SeerAGVDriver, AGVTaskStatus> OnAGVTaskStatusChanged;
        public Action<SeerAGVDriver, float> OnAGVBatteryLvlChanged;
        SeerAGVInitialConfig IConfig
        {
@@ -64,6 +65,7 @@
        {
            msg_Position = new SeerMessage((int)AGVCode.QueryPosition, SID);
            msg_GuideStatus = new SeerMessage((int)AGVCode.QueryTaskStatus, SID, IConfig.IsSimpleMonitor ? JsonConvert.SerializeObject(new { simple = true }) : "");
            msg_Battery = new SeerMessage((int)AGVCode.QueryBattery, SID, IConfig.IsSimpleMonitor ? JsonConvert.SerializeObject(new { simple = true }) : "");
            Task.Run(() =>
            {
@@ -76,7 +78,7 @@
            if (client_Guide != null && client_Guide.Connected)
            {
                CancelTask();
                client_Guide.Close();
                client_Guide.Close();
                client_Guide = null;
            }
@@ -142,8 +144,23 @@
            }
        }
        float batteryLvl = 0;
        public float BatteryLvl
        {
            get => batteryLvl;
            set
            {
                if (batteryLvl != value)
                {
                    batteryLvl = value;
                    OnAGVBatteryLvlChanged?.Invoke(this, batteryLvl);
                }
            }
        }
        SeerMessage msg_Position = new SeerMessage();
        SeerMessage msg_GuideStatus = new SeerMessage();
        SeerMessage msg_Battery = new SeerMessage();
        private void MonitorAGV()
        {
            while (CurrentState != EnumHelper.DeviceState.DSClose && CurrentState != EnumHelper.DeviceState.DSExcept)
@@ -153,6 +170,8 @@
                    SendMsg(client_State, IConfig.StatusPort, msg_Position);
                    Thread.Sleep(IConfig.ScanInterval);
                    SendMsg(client_State, IConfig.StatusPort, msg_GuideStatus);
                    Thread.Sleep(IConfig.ScanInterval);
                    SendMsg(client_State, IConfig.StatusPort, msg_Battery);
                    Thread.Sleep(IConfig.ScanInterval);
                }
                catch (Exception ex)
@@ -231,6 +250,9 @@
                    case (int)AGVCode.QueryTaskStatus:
                        TaskStatus = (AGVTaskStatus)recMsg.JValues.Value<int>("task_status");
                        break;
                    case (int)AGVCode.QueryBattery:
                        BatteryLvl = recMsg.JValues.Value<float>("battery_level");
                        break;
                    default:
                        break;
                }