From 4e8bf084f8a04617a9f542099183b8d829fa7c4b Mon Sep 17 00:00:00 2001
From: patrick <patrick.xu@broconcentric.com>
Date: 星期五, 18 十月 2019 19:07:51 +0800
Subject: [PATCH] 1. 修改机器人通信协议及相关流程触发和操作

---
 src/A032.Process/ProcessControl_Method.cs | 1064 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,053 insertions(+), 11 deletions(-)

diff --git a/src/A032.Process/ProcessControl_Method.cs b/src/A032.Process/ProcessControl_Method.cs
index 1ff4d8d..61fb662 100644
--- a/src/A032.Process/ProcessControl_Method.cs
+++ b/src/A032.Process/ProcessControl_Method.cs
@@ -1,7 +1,10 @@
-锘縰sing Bro.Common.Helper;
+锘縰sing Bro.Common.Base;
+using Bro.Common.Helper;
 using Bro.Common.Interface;
 using Bro.Common.Model;
+using Bro.Device.AuboRobot;
 using Bro.Device.HikCamera;
+using Bro.Device.SeerAGV;
 using HalconDotNet;
 using System;
 using System.Collections.Generic;
@@ -14,24 +17,1063 @@
 
 namespace A032.Process
 {
-    public enum ResultState
-    {
-        OK = 1,
-        NG = -1,
-        Undetermined = -2,
-    }
-
     public partial class ProcessControl
     {
+        Dictionary<int, int> machineFullTrayDict = new Dictionary<int, int>();
+        Dictionary<int, int> machineEmptyTrayDict = new Dictionary<int, int>();
+        List<TaskAssignInfo> taskAssignedList = new List<TaskAssignInfo>();
+
+        private void OnAGVTaskStatusChanged(SeerAGVDriver agv, AGVTaskStatus taskStatus)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id);
+
+            if (bind == null)
+            {
+                throw new ProcessException("鏈兘鏍规嵁AGV淇℃伅鑾峰彇缁戝畾璁惧淇℃伅", null);
+            }
+
+            if (bind.AGVDest == agv.CurrentPosition && taskStatus == AGVTaskStatus.Completed)
+            {
+                //PathPosition loadEmptyTrayPosition = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray);
+
+                //if (bind.AGVDest == loadEmptyTrayPosition.PositionCode)
+                //{
+                //    bind.RobotStatus = TaskStatus.Running;
+                //}
+
+                bind.AGVStatus = TaskStatus.Available;
+            }
+        }
+
+        private void OnAGVPositionChanged(SeerAGVDriver agv, string positionCode)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id);
+
+            if (bind == null)
+            {
+                throw new ProcessException("鏈兘鏍规嵁AGV淇℃伅鑾峰彇缁戝畾璁惧淇℃伅", null);
+            }
+
+            if (bind.AGVDest == positionCode && agv.TaskStatus == AGVTaskStatus.Completed)
+            {
+                //PathPosition loadEmptyTrayPosition = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray);
+
+                //if (bind.AGVDest == loadEmptyTrayPosition.PositionCode)
+                //{
+                //    bind.RobotStatus = TaskStatus.Running;
+                //}
+
+                bind.AGVStatus = TaskStatus.Available;
+            }
+        }
+
+        private void OnRobotMsgReceived(DateTime dt, AuboRobotDriver robot, RobotMsg msg)
+        {
+            LogAsync(dt, robot.Name + "鎺ユ敹淇℃伅", msg.GetDisplayText());
+
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robot.Id);
+
+            if (bind == null)
+            {
+                throw new ProcessException("鏈兘鏍规嵁鏈哄櫒浜轰俊鎭幏鍙栫粦瀹氳澶囦俊鎭�", null);
+            }
+
+            List<AGVTaskModel> models = new List<AGVTaskModel>();
+
+            switch (msg.Action)
+            {
+                case RobotMsgAction.Load:
+                    {
+                        switch (msg.Para1)
+                        {
+                            case RobotMsgParas.EmptyTray:
+                                {
+                                    bind.RobotStatus = bind.AGVStatus = TaskStatus.Available;
+                                }
+                                break;
+                            case RobotMsgParas.FullTray:
+                                {
+                                    machineFullTrayDict[msg.Para2]--;
+                                    bind.CurrentFullTray = int.Parse(msg.Datas[1]);
+
+                                    if (machineFullTrayDict[msg.Para2] > 0 && !bind.IsFullTrayFull)
+                                    {
+                                        RobotMsg_UnloadEmptyTray.Para2 = msg.Para2;
+                                        robot.SendMsg(RobotMsg_UnloadEmptyTray, true);
+                                    }
+                                    else
+                                    {
+                                        bind.RobotStatus = TaskStatus.Available;
+                                    }
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                case RobotMsgAction.Unload:
+                    {
+                        switch (msg.Para1)
+                        {
+                            case RobotMsgParas.EmptyTray:
+                                {
+                                    machineEmptyTrayDict[msg.Para2]++;
+                                    bind.CurrentEmptyTray = int.Parse(msg.Datas[0]);
+
+                                    if (machineEmptyTrayDict[msg.Para2] < Config.Machine_EmptyTrayNum)
+                                    {
+                                        bind.RobotIOHandle.Reset();
+                                        bind.RobotIOHandle.WaitOne();
+
+                                        if (!bind.IsFullTrayFull)
+                                        {
+                                            RobotMsg_UnloadEmptyTray.Para2 = msg.Para2;
+                                            robot.SendMsg(RobotMsg_UnloadEmptyTray, true);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        bind.RobotStatus = TaskStatus.Available;
+                                    }
+                                }
+                                break;
+                            case RobotMsgParas.FullTray:
+                                {
+                                    bind.CurrentFullTray = int.Parse(msg.Datas[1]);
+
+                                    bind.RobotIOHandle.Reset();
+                                    bind.RobotIOHandle.WaitOne();
+
+                                    if (!bind.IsFullTrayEmpty)
+                                    {
+                                        Camera_UnloadFullTray(robot.Id, msg.Para2);
+                                    }
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                case RobotMsgAction.Move:
+                    {
+                        switch (msg.Para1)
+                        {
+                            case RobotMsgParas.LineSnap:
+                                {
+                                    Camera_UnloadFullTray(robot.Id, msg.Para2);
+                                }
+                                break;
+                            case RobotMsgParas.LoadFullTraySnap:
+                                {
+                                    Camera_LoadFullTray(robot.Id, msg.Para2);
+                                }
+                                break;
+                            case RobotMsgParas.UnloadEmptyTraySnap:
+                                {
+                                    Camera_UnloadEmptyTray(robot.Id, msg.Para2);
+                                }
+                                break;
+                            case RobotMsgParas.Home:
+                                {
+                                    bind.RobotStatus = TaskStatus.Available;
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                case RobotMsgAction.Calibration:
+                    {
+
+                    }
+                    break;
+                case RobotMsgAction.StandardPoint:
+                    {
+
+                    }
+                    break;
+                default:
+                    break;
+            }
+
+            if (models.Count > 0)
+            {
+                models.ForEach(model =>
+                {
+                    if (!bind.TaskList.Any(t => t.MethodFunc.Method.Name == model.MethodFunc.Method.Name))
+                    {
+                        model.OpConfig = new AGVBindOpConfig(bind.Id);
+                        bind.TaskList.Add(model);
+                    }
+                });
+            }
+        }
+
         public void QueryRobotIO()
         {
-
+            RobotDict.Values.ToList().ForEach(r =>
+            {
+                r.SendMsg(RobotMsgAction.IO, RobotMsgParas.Query, 0);
+            });
         }
 
-        [ProcessMethod("HikCamera", "RobotCorrection", "鎷嶆憚锛岀‘璁ゆ満鍣ㄤ汉璋冩暣浣嶇疆", true)]
-        public ProcessResponse RobotCorrection(IOperationConfig config)
+        private void OnBindUnitTaskInvoke(AGVBindUnit bind)
         {
+            var task = bind.TaskList[0];
+            var response = task.MethodFunc.Invoke(task.OpConfig, task.Device);
+        }
+
+        #region Robot鐩戝惉浜嬩欢
+        private void AddNewTaskToBind(string robotId, List<AGVTaskModel> models)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId);
+
+            if (bind == null)
+            {
+                throw new ProcessException("鏈兘鏍规嵁鏈哄櫒浜轰俊鎭幏鍙栫粦瀹氳澶囦俊鎭�", null);
+            }
+
+            AddNewTaskToBind(bind, models);
+        }
+
+        private void AddNewTaskToBind(AGVBindUnit bind, List<AGVTaskModel> models)
+        {
+            if (models.Count > 0)
+            {
+                models.ForEach(model =>
+                {
+                    if (!bind.TaskList.Any(t => t.MethodFunc.Method.Name == model.MethodFunc.Method.Name))
+                    {
+                        model.OpConfig = new AGVBindOpConfig(bind.Id);
+                        bind.TaskList.Add(model);
+                    }
+                });
+            }
+        }
+
+        [ProcessMethod("", "Robot_Monitor_Alarm", "鏈哄櫒浜虹洃鍚簨浠�-鎶ヨ", true)]
+        public ProcessResponse Robot_Monitor_Alarm(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id);
+
+            if (bind == null)
+            {
+                throw new ProcessException("鏈兘鏍规嵁鏈哄櫒浜轰俊鎭幏鍙栫粦瀹氳澶囦俊鎭�", null);
+            }
+
+            bind.AGV.PauseTask();
+            bind.RobotStatus = TaskStatus.Warning;
+
             return new ProcessResponse(true);
         }
+
+        [ProcessMethod("", "Robot_Monitor_EmptyTrayEmpty", "鏈哄櫒浜虹洃鍚簨浠�-绌篢ray鍖哄煙娓呯┖", true)]
+        public ProcessResponse Robot_Monitor_EmptyTrayEmpty(IOperationConfig config, IDevice device)
+        {
+            bool isEmptyTrayEmpty = config.InputPara[0] == 1;
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id);
+            if (isEmptyTrayEmpty)
+            {
+                bind.IsEmptyTrayEmpty = true;
+
+                Task.Run(() =>
+                {
+                    Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_LoadEmptyTray;
+                    while (bind.IsEmptyTrayEmpty && bind.TaskList.Count == 0 && !bind.TaskList.Any(u => u.MethodFunc.Method.Name == action.Method.Name))
+                    {
+                        if (bind.TaskList.Count == 0)
+                        {
+                            List<AGVTaskModel> models = new List<AGVTaskModel>();
+                            models.Add(new AGVTaskModel(TaskAvailableLevel.Both, AGV_LoadEmptyTray));
+                            models.Add(new AGVTaskModel(TaskAvailableLevel.AGV, AfterEmptyTrayPositionArrived));
+
+                            AddNewTaskToBind(device.Id, models);
+                        }
+                        else
+                        {
+                            Thread.Sleep(500);
+                        }
+                    }
+                });
+
+            }
+            else
+            {
+                bind.IsEmptyTrayEmpty = false;
+            }
+
+            return new ProcessResponse(true);
+        }
+
+        [ProcessMethod("", "Robot_Monitor_EmptyTrayEmpty", "鏈哄櫒浜虹洃鍚簨浠�-婊ray鍖哄煙鏀炬弧", true)]
+        public ProcessResponse Robot_Monitor_FullTrayFull(IOperationConfig config, IDevice device)
+        {
+            bool isFullTrayFull = config.InputPara[0] == 1;
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id);
+            if (isFullTrayFull)
+            {
+                bind.IsFullTrayFull = true;
+
+                Task.Run(() =>
+                {
+                    Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_UnloadFullTray;
+                    while (bind.IsFullTrayFull && !bind.TaskList.Any(u => u.MethodFunc.Method.Name == action.Method.Name))
+                    {
+                        if (bind.TaskList.Count == 0)
+                        {
+                            List<AGVTaskModel> models = new List<AGVTaskModel>();
+                            models.Add(new AGVTaskModel(TaskAvailableLevel.Both, AGV_UnloadFullTray));
+                            models.Add(new AGVTaskModel(TaskAvailableLevel.AGV, Robot_UnloadFullTray));
+
+                            AddNewTaskToBind(device.Id, models);
+                        }
+                        else
+                        {
+                            Thread.Sleep(500);
+                        }
+                    }
+                });
+            }
+            else
+            {
+                bind.IsFullTrayFull = false;
+            }
+
+            bind.RobotIOHandle.Set();
+
+            return new ProcessResponse(true);
+        }
+
+        [ProcessMethod("", "Robot_Monitor_FullTrayEmpty", "鏈哄櫒浜虹洃鍚簨浠�-婊ray鍖哄煙娓呯┖", true)]
+        public ProcessResponse Robot_Monitor_FullTrayEmpty(IOperationConfig config, IDevice device)
+        {
+            bool isFullTrayEmpty = config.InputPara[0] == 1;
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id);
+            bind.IsFullTrayEmpty = isFullTrayEmpty;
+
+            bind.RobotIOHandle.Set();
+
+            return new ProcessResponse(true);
+        }
+
+        //[ProcessMethod("", "Robot_Monitor_LoadEmptyTrayReady", "鏈哄櫒浜虹洃鍚簨浠�-杞藉叆绌篢ray灏辩华", true)]
+        //public ProcessResponse Robot_Monitor_LoadEmptyTrayReady(IOperationConfig config, IDevice device)
+        //{
+        //    var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id);
+
+        //    if (bind == null)
+        //    {
+        //        throw new ProcessException("鏈兘鏍规嵁鏈哄櫒浜轰俊鎭幏鍙栫粦瀹氳澶囦俊鎭�", null);
+        //    }
+
+        //    PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray);
+        //    if (bind.AGV.CurrentPosition != position.PositionCode)
+        //    {
+        //        new ProcessException("涓婄┖Tray瀹屾垚淇″彿浠呭湪涓婄┖Tray鍦扮偣鏈夋晥", null);
+        //        return new ProcessResponse(true);
+        //    }
+
+        //    List<AGVTaskModel> models = new List<AGVTaskModel>();
+        //    models.Add(new AGVTaskModel(TaskAvailableLevel.AGV, EmptyTrayReady));
+        //    AddNewTaskToBind(bind, models);
+
+        //    return new ProcessResponse(true);
+        //}
+
+        [ProcessMethod("", "Robot_Monitor_Reset", "鏈哄櫒浜虹洃鍚簨浠�-Reset澶嶄綅", true)]
+        public ProcessResponse Robot_Monitor_Reset(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id);
+
+            if (bind == null)
+            {
+                throw new ProcessException("鏈兘鏍规嵁鏈哄櫒浜轰俊鎭幏鍙栫粦瀹氳澶囦俊鎭�", null);
+            }
+            bind.AGV.CancelTask();
+
+            //isEmptyTrayTaskAssigned = false;
+            //isFullTrayTaskAssigned = false;
+
+            taskAssignedList.RemoveAll(u => u.AgvId == device.Id);
+
+            bind.TaskList.Clear();
+            bind.RobotStatus = bind.AGVStatus = TaskStatus.Available;
+
+            return new ProcessResponse(true);
+        }
+        #endregion
+
+        #region 绌篢ray涓婃枡
+        [ProcessMethod("", "AGV_LoadEmptyTray", "AGV鍘诲線绌篢ray涓婃枡", true)]
+        public ProcessResponse AGV_LoadEmptyTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃┖Tray涓婃枡鐐�");
+            }
+
+            bind.AGVDest = position.PositionCode;
+            bind.AGV.TaskOrder(position.PositionCode);
+
+            bind.AGVStatus = TaskStatus.Running;
+
+            return new ProcessResponse(true);
+        }
+
+        [ProcessMethod("", "AfterEmptyTrayPositionArrived", "鍒拌揪绌篢ray涓婃枡鐐�", true)]
+        public ProcessResponse AfterEmptyTrayPositionArrived(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃┖Tray涓婃枡鐐�", null);
+            }
+
+            if (bind.AGV.CurrentPosition != position.PositionCode)
+            {
+                throw new ProcessException("AGV灏氭湭鍒拌揪绌篢ray涓婃枡鐐�", null);
+            }
+
+            bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.EmptyTray, 0);
+            bind.RobotStatus = TaskStatus.Running;
+
+            return new ProcessResponse(true);
+        }
+
+        //[ProcessMethod("", "EmptyTrayReady", "绌篢ray涓婃枡瀹屾垚", true)]
+        //public ProcessResponse EmptyTrayReady(IOperationConfig config, IDevice device)
+        //{
+        //    var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+        //    bind.RobotStatus = bind.AGVStatus = TaskStatus.Available;
+
+        //    return new ProcessResponse(true);
+        //}
+        #endregion
+
+        #region 绌篢ray寰�鏈哄彴涓嬫枡
+        //bool isEmptyTrayNeed = false;
+        //bool isEmptyTrayTaskAssigned = false;
+        RobotMsg RobotMsg_UnloadEmptyTray = new RobotMsg();
+
+        [ProcessMethod("", "PLC_NoticeEmptyTray", "PLC閫氱煡闇�瑕佷笂绌篢ray", true)]
+        public ProcessResponse PLC_NoticeEmptyTray(IOperationConfig config, IDevice device)
+        {
+            if (config.InputPara == null || config.InputPara.Count == 0)
+            {
+                throw new ProcessException("涓婄┖Tray鏂规硶鏈厤缃緭鍏ュ弬鏁�", null);
+            }
+
+            bool isEmptyTrayNeed = config.InputPara[0] == 1;
+
+            if (isEmptyTrayNeed)
+            {
+                var position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray && u.DeviceOwner == device.Id);
+
+                machineEmptyTrayDict[position.PositionNo] = 0;
+
+                if (!taskAssignedList.Any(u => u.PositionNo == position.PositionNo))
+                {
+                    taskAssignedList.Add(new TaskAssignInfo()
+                    {
+                        IsTaskNeed = true,
+                        IsTaskAssgined = false,
+                        PositionNo = position.PositionNo,
+                    });
+                }
+
+                CheckEmptyTrayTask(position.PositionNo);
+            }
+
+            return new ProcessResponse(true);
+        }
+
+        private async void CheckEmptyTrayTask(int positionNo)
+        {
+            await Task.Run(() =>
+            {
+                var taskStatus = taskAssignedList.FirstOrDefault(u => u.PositionNo == positionNo);
+
+                if (taskStatus == null)
+                    return;
+
+                while (taskStatus.IsTaskNeed && !taskStatus.IsTaskAssgined)
+                {
+                    Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_UnloadEmptyTray;
+
+                    //if (!Config.AGVBindCollection.Any(b => b.TaskList.Any(t => t.MethodFunc.Method.Name == action.Method.Name)))
+                    {
+                        var bind = Config.AGVBindCollection.FirstOrDefault(u => u.UnitStatus == TaskStatus.Available);
+                        if (bind != null)
+                        {
+                            var position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNo);
+                            AGVTaskModel model_AGV = new AGVTaskModel(TaskAvailableLevel.Both, action, new AGVBindOpConfig(bind.Id, position));
+                            AGVTaskModel model_Robot = new AGVTaskModel(TaskAvailableLevel.AGV, Robot_UnloadEmptyTray, new AGVBindOpConfig(bind.Id));
+
+                            bind.TaskList.Add(model_AGV);
+                            bind.TaskList.Add(model_Robot);
+
+                            taskStatus.IsTaskAssgined = true;
+                            taskStatus.AgvId = bind.AGVId;
+                        }
+                    }
+
+                    Thread.Sleep(300);
+                }
+            });
+        }
+
+        //[ProcessMethod("", "AGV_UnloadEmptyTray", "AGV鍘诲線鍗歌浇绌篢ray鏂欎綅缃�", true)]
+        public ProcessResponse AGV_UnloadEmptyTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+
+            PathPosition position = (config as AGVBindOpConfig).Position;
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃┖Tray涓嬫枡鐐�");
+            }
+
+            bind.AGVDest = position.PositionCode;
+            bind.AGV.TaskOrder(position.PositionCode);
+
+            bind.AGVStatus = TaskStatus.Running;
+
+            return new ProcessResponse(true);
+        }
+
+        //[ProcessMethod("", "Robot_UnloadEmptyTray", "鏈哄櫒浜鸿繍鍔ㄨ嚦绌篢ray鎷嶇収浣嶇疆", true)]
+        public ProcessResponse Robot_UnloadEmptyTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray && u.PositionCode == bind.AGV.CurrentPosition);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃┖Tray涓嬫枡鐐�");
+            }
+
+            taskAssignedList.RemoveAll(u => u.AgvId == bind.AGVId && u.PositionNo == position.PositionNo);
+
+            bind.RobotStatus = TaskStatus.Running;
+            bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.UnloadEmptyTraySnap, position.PositionNo);
+
+            return new ProcessResponse(true);
+        }
+
+        //[ProcessMethod("", "Camera_UnloadEmptyTray", "鐩告満纭绌篢ray鍗歌浇鏈哄櫒浜轰綅缃皟鏁�", true)]
+        //public ProcessResponse Camera_UnloadEmptyTray(IOperationConfig config, IDevice device)
+        //{
+        //    var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+
+        //    PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray);
+
+        //    if (position == null)
+        //    {
+        //        throw new ProcessException("璺緞閰嶇疆鏈缃┖Tray涓嬫枡鐐�");
+        //    }
+
+        //    if (bind.AGV.CurrentPosition != position.PositionCode)
+        //    {
+        //        throw new ProcessException("AGV褰撳墠鏈浜庣┖Tray涓嬫枡鐐�");
+        //    }
+
+        //    PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId);
+
+        //    if (visionConfig == null)
+        //    {
+        //        throw new ProcessException("鏈厤缃鐩告満鐨勭┖Tray涓嬫枡鐐圭殑瑙嗚鎿嶄綔閰嶇疆");
+        //    }
+
+        //    float x = 0;
+        //    float y = 0;
+        //    float angle = 0;
+
+        //    using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadEmptyTray"))
+        //    {
+        //        string toolPath = visionConfig.CameraOpConfig.AlgorithemPath;
+        //        if (!_halconToolDict.ContainsKey(toolPath))
+        //        {
+        //            throw new ProcessException($"鏈厤缃瓹amera_UnloadEmptyTray鐨勮瑙夌畻娉曡矾寰�");
+        //        }
+
+        //        var tool = _halconToolDict[toolPath];
+        //        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();
+
+        //        x = (float)tool.GetResultTuple("OUTPUT_X").D;
+        //        y = (float)tool.GetResultTuple("OUTPUT_Y").D;
+        //        angle = (float)tool.GetResultTuple("OUTPUT_Angle").D;
+        //    }
+
+        //    if (x <= 0 || y <= 0)
+        //    {
+        //        throw new ProcessException("Camera_UnloadEmptyTray瑙嗚璁$畻鑾峰彇鐐逛綅涓嶅彲灏忎簬0");
+        //    }
+
+        //    float dx = visionConfig.StandardPoint.X - x;
+        //    float dy = visionConfig.StandardPoint.Y - y;
+
+        //    HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot);
+
+        //    bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle });
+
+        //    return new ProcessResponse(true);
+        //}
+
+        public ProcessResponse Camera_UnloadEmptyTray(string robotId, int positionNum)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId);
+
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNum);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃┖Tray涓嬫枡鐐�");
+            }
+
+            if (bind.AGV.CurrentPosition != position.PositionCode)
+            {
+                throw new ProcessException("AGV褰撳墠鏈浜庣┖Tray涓嬫枡鐐�");
+            }
+
+            PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId);
+
+            if (visionConfig == null)
+            {
+                throw new ProcessException("鏈厤缃鐩告満鐨勭┖Tray涓嬫枡鐐圭殑瑙嗚鎿嶄綔閰嶇疆");
+            }
+
+            float x = 0;
+            float y = 0;
+            float angle = 0;
+
+            using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadEmptyTray"))
+            {
+                string toolPath = visionConfig.CameraOpConfig.AlgorithemPath;
+                if (!_halconToolDict.ContainsKey(toolPath))
+                {
+                    throw new ProcessException($"鏈厤缃瓹amera_UnloadEmptyTray鐨勮瑙夌畻娉曡矾寰�");
+                }
+
+                var tool = _halconToolDict[toolPath];
+                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();
+
+                x = (float)tool.GetResultTuple("OUTPUT_X").D;
+                y = (float)tool.GetResultTuple("OUTPUT_Y").D;
+                angle = (float)tool.GetResultTuple("OUTPUT_Angle").D;
+            }
+
+            if (x <= 0 || y <= 0)
+            {
+                throw new ProcessException("Camera_UnloadEmptyTray瑙嗚璁$畻鑾峰彇鐐逛綅涓嶅彲灏忎簬0");
+            }
+
+            float dx = visionConfig.StandardPoint.X - x;
+            float dy = visionConfig.StandardPoint.Y - y;
+
+            HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot);
+
+            //bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle });
+            RobotMsg_UnloadEmptyTray.Action = RobotMsgAction.Unload;
+            RobotMsg_UnloadEmptyTray.Para1 = RobotMsgParas.EmptyTray;
+            RobotMsg_UnloadEmptyTray.Para2 = position.PositionNo;
+            RobotMsg_UnloadEmptyTray.Datas = new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }.ConvertAll(s => s.ToString()).ToList();
+            bind.Robot.SendMsg(RobotMsg_UnloadEmptyTray, true);
+
+            return new ProcessResponse(true);
+        }
+        #endregion
+
+        #region 浠庢満鍙颁笂婊ray
+        //bool isFullTrayNeed = false;
+        //bool isFullTrayTaskAssigned = false;
+        RobotMsg RobotMsg_LoadFullTray = new RobotMsg();
+
+        [ProcessMethod("", "PLC_NoticeFullTray", "PLC閫氱煡婊ray闇�瑕佸彇璧�", true)]
+        public ProcessResponse PLC_NoticeFullTray(IOperationConfig config, IDevice device)
+        {
+            if (config.InputPara == null || config.InputPara.Count == 0)
+            {
+                throw new ProcessException("涓婄┖Tray鏂规硶鏈厤缃緭鍏ュ弬鏁�", null);
+            }
+
+            bool isFullTrayNeed = config.InputPara[0] == 1;
+            if (isFullTrayNeed)
+            {
+                var position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.DeviceOwner == device.Id);
+
+                machineFullTrayDict[position.PositionNo] = Config.Machine_FullTrayNum;
+
+                if (!taskAssignedList.Any(u => u.PositionNo == position.PositionNo))
+                {
+                    taskAssignedList.Add(new TaskAssignInfo()
+                    {
+                        IsTaskNeed = true,
+                        IsTaskAssgined = false,
+                        PositionNo = position.PositionNo,
+                    });
+                }
+
+                CheckFullTrayTask(position.PositionNo);
+            }
+
+            return new ProcessResponse(true);
+        }
+
+        private async void CheckFullTrayTask(int positionNo)
+        {
+            await Task.Run(() =>
+            {
+                var taskStatus = taskAssignedList.FirstOrDefault(u => u.PositionNo == positionNo);
+
+                if (taskStatus == null)
+                    return;
+
+                while (taskStatus.IsTaskNeed && !taskStatus.IsTaskAssgined)
+                {
+                    Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_LoadFullTray;
+
+                    //if (!Config.AGVBindCollection.Any(b => b.TaskList.Any(t => t.MethodFunc.Method.Name == action.Method.Name)))
+                    {
+                        var bind = Config.AGVBindCollection.FirstOrDefault(u => u.UnitStatus == TaskStatus.Available);
+                        if (bind != null)
+                        {
+                            var position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNo);
+                            AGVTaskModel model_AGV = new AGVTaskModel(TaskAvailableLevel.Both, action, new AGVBindOpConfig(bind.Id, position));
+                            AGVTaskModel model_Robot = new AGVTaskModel(TaskAvailableLevel.AGV, Robot_LoadFullTray, new AGVBindOpConfig(bind.Id));
+
+                            bind.TaskList.Add(model_AGV);
+                            bind.TaskList.Add(model_Robot);
+
+                            taskStatus.IsTaskAssgined = true;
+                            taskStatus.AgvId = bind.AGVId;
+                        }
+                    }
+
+                    Thread.Sleep(300);
+                }
+            });
+        }
+
+        [ProcessMethod("", "AGV_LoadFullTray", "AGV鍘诲線婊ray涓婃枡浣嶇疆", true)]
+        public ProcessResponse AGV_LoadFullTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+
+            PathPosition position = (config as AGVBindOpConfig).Position;
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓婃枡鐐�");
+            }
+
+            bind.AGVDest = position.PositionCode;
+            bind.AGV.TaskOrder(position.PositionCode);
+
+            bind.AGVStatus = TaskStatus.Running;
+
+            return new ProcessResponse(true);
+        }
+
+        [ProcessMethod("", "Robot_LoadFullTray", "鏈哄櫒浜鸿繍鍔ㄨ嚦婊ray鎷嶇収浣嶇疆", true)]
+        public ProcessResponse Robot_LoadFullTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.PositionCode == bind.AGV.CurrentPosition);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓婃枡鐐�");
+            }
+
+            //if (bind.AGV.CurrentPosition != position.PositionCode)
+            //{
+            //    throw new ProcessException("AGV褰撳墠鏈浜庢弧Tray涓婃枡鐐�");
+            //}
+
+            bind.RobotStatus = TaskStatus.Running;
+            bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LoadFullTraySnap, position.PositionNo);
+
+            return new ProcessResponse(true);
+        }
+
+        //[ProcessMethod("", "Camera_LoadFullTray", "鐩告満纭婊ray涓婃枡鏈哄櫒浜轰綅缃皟鏁�", true)]
+        //public ProcessResponse Camera_LoadFullTray(IOperationConfig config, IDevice device)
+        //{
+        //    var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+
+        //    PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray);
+
+        //    if (position == null)
+        //    {
+        //        throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓婃枡鐐�");
+        //    }
+
+        //    if (bind.AGV.CurrentPosition != position.PositionCode)
+        //    {
+        //        throw new ProcessException("AGV褰撳墠鏈浜庢弧Tray涓婃枡鐐�");
+        //    }
+
+        //    PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId);
+
+        //    if (visionConfig == null)
+        //    {
+        //        throw new ProcessException("鏈厤缃鐩告満鐨勬弧Tray涓婃枡鐐圭殑瑙嗚鎿嶄綔閰嶇疆");
+        //    }
+
+        //    float x = 0;
+        //    float y = 0;
+        //    float angle = 0;
+
+        //    using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_LoadFullTray"))
+        //    {
+        //        string toolPath = visionConfig.CameraOpConfig.AlgorithemPath;
+        //        if (!_halconToolDict.ContainsKey(toolPath))
+        //        {
+        //            throw new ProcessException($"鏈厤缃瓹amera_LoadFullTray鐨勮瑙夌畻娉曡矾寰�");
+        //        }
+
+        //        var tool = _halconToolDict[toolPath];
+        //        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();
+
+        //        x = (float)tool.GetResultTuple("OUTPUT_X").D;
+        //        y = (float)tool.GetResultTuple("OUTPUT_Y").D;
+        //        angle = (float)tool.GetResultTuple("OUTPUT_Angle").D;
+        //    }
+
+        //    if (x <= 0 || y <= 0)
+        //    {
+        //        throw new ProcessException("Camera_LoadFullTray瑙嗚璁$畻鑾峰彇鐐逛綅涓嶅彲灏忎簬0");
+        //    }
+
+        //    float dx = visionConfig.StandardPoint.X - x;
+        //    float dy = visionConfig.StandardPoint.Y - y;
+
+        //    HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot);
+
+        //    bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle });
+
+        //    return new ProcessResponse(true);
+        //}
+
+        public ProcessResponse Camera_LoadFullTray(string robotId, int positionNum)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId);
+
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNum);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓婃枡鐐�");
+            }
+
+            if (bind.AGV.CurrentPosition != position.PositionCode)
+            {
+                throw new ProcessException("AGV褰撳墠鏈浜庢弧Tray涓婃枡鐐�");
+            }
+
+            PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId);
+
+            if (visionConfig == null)
+            {
+                throw new ProcessException("鏈厤缃鐩告満鐨勬弧Tray涓婃枡鐐圭殑瑙嗚鎿嶄綔閰嶇疆");
+            }
+
+            float x = 0;
+            float y = 0;
+            float angle = 0;
+
+            using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_LoadFullTray"))
+            {
+                string toolPath = visionConfig.CameraOpConfig.AlgorithemPath;
+                if (!_halconToolDict.ContainsKey(toolPath))
+                {
+                    throw new ProcessException($"鏈厤缃瓹amera_LoadFullTray鐨勮瑙夌畻娉曡矾寰�");
+                }
+
+                var tool = _halconToolDict[toolPath];
+                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();
+
+                x = (float)tool.GetResultTuple("OUTPUT_X").D;
+                y = (float)tool.GetResultTuple("OUTPUT_Y").D;
+                angle = (float)tool.GetResultTuple("OUTPUT_Angle").D;
+            }
+
+            if (x <= 0 || y <= 0)
+            {
+                throw new ProcessException("Camera_LoadFullTray瑙嗚璁$畻鑾峰彇鐐逛綅涓嶅彲灏忎簬0");
+            }
+
+            float dx = visionConfig.StandardPoint.X - x;
+            float dy = visionConfig.StandardPoint.Y - y;
+
+            HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot);
+
+            //bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle });
+            RobotMsg_LoadFullTray.Action = RobotMsgAction.Load;
+            RobotMsg_LoadFullTray.Para1 = RobotMsgParas.FullTray;
+            RobotMsg_LoadFullTray.Para2 = position.PositionNo;
+            RobotMsg_LoadFullTray.Datas = new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }.ConvertAll(s => s.ToString()).ToList();
+            bind.Robot.SendMsg(RobotMsg_LoadFullTray, true);
+
+            return new ProcessResponse(true);
+        }
+        #endregion
+
+        #region 婊ray浜х嚎涓嬫枡
+        [ProcessMethod("", "AGV_UnloadFullTray", "AGV鍘诲線鍗歌浇婊ray鏂�", true)]
+        public ProcessResponse AGV_UnloadFullTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓嬫枡鐐�");
+            }
+
+            bind.AGVDest = position.PositionCode;
+            bind.AGV.TaskOrder(position.PositionCode);
+
+            bind.AGVStatus = TaskStatus.Running;
+
+            return new ProcessResponse(true);
+        }
+
+        [ProcessMethod("", "Robot_UnloadFullTray", "鏈哄櫒浜哄嵏杞芥弧Tray", true)]
+        public ProcessResponse Robot_UnloadFullTray(IOperationConfig config, IDevice device)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓嬫枡鐐�");
+            }
+
+            if (bind.AGV.CurrentPosition != position.PositionCode)
+            {
+                throw new ProcessException("AGV褰撳墠鏈浜庢弧Tray涓嬫枡鐐�");
+            }
+
+            bind.RobotStatus = TaskStatus.Running;
+            bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LineSnap, position.PositionNo);
+
+            return new ProcessResponse(true);
+        }
+
+        //[ProcessMethod("", "Camera_UnloadFullTray", "鐩告満鎿嶄綔鍗歌浇婊ray", true)]
+        //public ProcessResponse Camera_UnloadFullTray(IOperationConfig config, IDevice device)
+        public ProcessResponse Camera_UnloadFullTray(string robotId, int positionNum)
+        {
+            var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId);
+            PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray && u.PositionNo == positionNum);
+
+            if (position == null)
+            {
+                throw new ProcessException("璺緞閰嶇疆鏈缃弧Tray涓嬫枡鐐�");
+            }
+
+            if (bind.AGV.CurrentPosition != position.PositionCode)
+            {
+                throw new ProcessException("AGV褰撳墠鏈浜庢弧Tray涓嬫枡鐐�");
+            }
+
+            PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId);
+
+            if (visionConfig == null)
+            {
+                throw new ProcessException("鏈厤缃鐩告満鐨勬弧Tray涓嬫枡鐐圭殑瑙嗚鎿嶄綔閰嶇疆");
+            }
+
+            bool isLineReady = false;
+            int reTryTime = Config.LineBusyRetryTimes;
+
+            do
+            {
+                using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadFullTray"))
+                {
+                    string toolPath = visionConfig.CameraOpConfig.AlgorithemPath;
+                    if (!_halconToolDict.ContainsKey(toolPath))
+                    {
+                        throw new ProcessException($"鏈厤缃瓹amera_UnloadFullTray鐨勮瑙夌畻娉曡矾寰�");
+                    }
+
+                    _halconToolDict[toolPath].SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_Result", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } });
+                    _halconToolDict[toolPath].RunProcedure();
+
+                    isLineReady = _halconToolDict[toolPath].GetResultTuple("OUTPUT_Result").I == 1;
+                }
+
+                if (!isLineReady)
+                {
+                    Thread.Sleep(Config.LineBusyWaitInterval * 1000);
+                    reTryTime--;
+                }
+                else
+                {
+                    reTryTime = 0;
+                }
+            } while (reTryTime > 0);
+
+            //if (!isLineReady)
+            //{
+            //    bind.Robot.SendMsg(RobotMsgType.Send, -1, true, RobotMsgAction.State, RobotMsgParas.LineSnap, new List<string>() { "-1" });
+            //    throw new ProcessException("浜х嚎蹇欙紝绛夊緟瓒呮椂");
+            //}
+            //else
+            //{
+            //    bind.Robot.SendMsg(RobotMsgType.Send, -1, true, RobotMsgAction.State, RobotMsgParas.LineSnap, new List<string>() { "1" });
+            //}
+
+            if (isLineReady)
+            {
+                bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.FullTray, position.PositionNo);
+            }
+            else
+            {
+                bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.Home, position.PositionNo);
+            }
+
+            return new ProcessResponse(true);
+        }
+        #endregion
+    }
+
+    public class AGVBindOpConfig : OperationConfigBase
+    {
+        public string BindId { get; set; }
+        public PathPosition Position { get; set; }
+
+        public AGVBindOpConfig() { }
+
+        public AGVBindOpConfig(string bindId, PathPosition position = null)
+        {
+            BindId = bindId;
+            Position = position;
+        }
+    }
+
+    public class TaskAssignInfo
+    {
+        public int PositionNo { get; set; }
+
+        public bool IsTaskNeed { get; set; } = false;
+
+        public bool IsTaskAssgined { get; set; } = false;
+
+        public string AgvId { get; set; }
     }
 }

--
Gitblit v1.8.0