Ejemplo n.º 1
0
        private void cbbPickNode_DropDownClosed(object sender, EventArgs e)
        {
            // If task type is Input, or type is Output and use palletcode to pick, do notthing
            if (cbbType.Text == "Input")
            {
                return;
            }
            if (cbbType.Text == "Output" && checkBox.Checked == true)
            {
                return;
            }

            List <RackColumn> listColumn = new List <RackColumn>();

            switch (Display.Mode)
            {
            case "Real Time": listColumn = RackColumn.ListColumn;
                break;

            case "Simulation": listColumn = RackColumn.SimListColumn;
                break;
            }

            // If task type is Output, get palletCode and put it in txbPalletCode
            string[] textArr = cbbPickNode.Text.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
            if (textArr.Length == 0)
            {
                return;
            }

            RackColumn column = listColumn.Find(c => c.AtNode.ToString() == textArr[0]); // textArr[0] is node

            txbPalletCode.Text = column.PalletCodes[Convert.ToInt16(textArr[1]) - 1];    // textArr[1] is level
        }
Ejemplo n.º 2
0
        private void rdbtnSimulation_CheckedChanged(object sender, EventArgs e)
        {
            if (!rdbtnSimulation.Checked)
            {
                return;
            }
            rdbtnSimulation.ForeColor = Color.DarkRed;
            rdbtnRealTime.ForeColor   = SystemColors.ActiveCaptionText;
            rdbtnSimulation.BackColor = Color.MintCream;
            rdbtnRealTime.BackColor   = Color.Lavender;
            btnPauseRun.Visible       = true;
            lbModeStatus.Text         = "     Simulation mode is running.";

            Display.Mode           = "Simulation";
            timerComStatus.Enabled = false;

            // Add label of columns at node
            RackColumn.ListColumn.ForEach(col => RackColumn.RemoveLabelColumn(pnFloor, col));
            RackColumn.SimListColumn.ForEach(col => pnFloor.Controls.Add(col.ColumnLabel));


            // Remove real time label, add simulation label
            AGV.ListAGV.ForEach(agv => Display.RemoveLabelAGV(pnFloor, agv.ID));
            foreach (AGV agv in AGV.SimListAGV)
            {
                Display.AddLabelAGV(pnFloor, agv.ID, agv.ExitNode, agv.Orientation, agv.DistanceToExitNode);
            }

            // Clear draw path in panel
            Display.Points = new Point[] { new Point(), new Point() };
            pnFloor.Refresh();
        }
Ejemplo n.º 3
0
        private void HomeScreenForm_Load(object sender, EventArgs e)
        {
            // Update existing pallets in stock and add to RackColumn
            Pallet.ListPallet = DBUtility.GetPalletInfoFromDB <List <Pallet> >("PalletInfoTable");
            RackColumn.InitializePallet(RackColumn.ListColumn, Pallet.ListPallet);

            Pallet.SimListPallet = DBUtility.GetPalletInfoFromDB <List <Pallet> >("SimPalletInfoTable");
            RackColumn.InitializePallet(RackColumn.SimListColumn, Pallet.SimListPallet);
        }
        public static void RemoveLabelColumn(Panel panel, RackColumn col)
        {
            var label = panel.Controls.OfType <Label>().FirstOrDefault(lb => lb.Name == "ColumnAtNode" + col.AtNode.ToString());

            if (label != null)
            {
                panel.Controls.Remove(label);
            }
        }
        // Auto select drop node and rack level, return {0, 0} when warwhouse is full
        private static int[] AutoSelectDropNode(List <Task> listTask, List <RackColumn> listColumn)
        {
            int[] dropNode = new int[2]; // dropNode[0] is drop node, dropNode[1] is drop level

            // select block has the least number of pallet
            string selectedBlock;
            int    blockA_PalletCount = PalletCountInBlock(listTask, listColumn, "A");
            int    blockB_PalletCount = PalletCountInBlock(listTask, listColumn, "B");
            int    blockC_PalletCount = PalletCountInBlock(listTask, listColumn, "C");
            int    blockD_PalletCount = PalletCountInBlock(listTask, listColumn, "D");

            if (blockA_PalletCount < 6 * 3)
            {
                selectedBlock = "A";                             // 6 column and 3 rack level in each block -> 18 pallet max
            }
            else if (blockB_PalletCount < 6 * 3)
            {
                selectedBlock = "B";
            }
            else if (blockC_PalletCount < 6 * 3)
            {
                selectedBlock = "C";
            }
            else if (blockD_PalletCount < 6 * 3)
            {
                selectedBlock = "D";
            }
            else
            {
                return new int[2] {
                           0, 0
                }
            };                               // warehouse is full

            // find column number has the least pallet count
            int columnNumber = ColumnHasLeastPalletCount(listTask, listColumn, selectedBlock);

            // find node base on selected block & column number
            RackColumn col = listColumn.Find(c => c.Block == selectedBlock && c.Number == columnNumber);

            dropNode[0] = col.AtNode;

            // select level of that node
            for (int i = 0; i < col.PalletCodes.Length; i++)
            {
                List <int> dropLevelOnGoing = PalletOnGoing <List <int> >(col, listTask);

                // select level not have pallet code and no exist in dropLevelOnGoing
                if (col.PalletCodes[i] == null && dropLevelOnGoing.Contains(i + 1) == false)
                {
                    dropNode[1] = i + 1;
                    break;
                }
            }

            return(dropNode);
        }
        private void WarehouseDataForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Clear all list pallet and column for update
            Pallet.ListPallet.Clear();
            Pallet.SimListPallet.Clear();
            RackColumn.ListColumn.ForEach(c => Array.Clear(c.PalletCodes, 0, 3));
            RackColumn.SimListColumn.ForEach(c => Array.Clear(c.PalletCodes, 0, 3));

            // Update existing pallets in stock and add to RackColumn
            Pallet.ListPallet = DBUtility.GetPalletInfoFromDB <List <Pallet> >("PalletInfoTable");
            RackColumn.InitializePallet(RackColumn.ListColumn, Pallet.ListPallet);

            Pallet.SimListPallet = DBUtility.GetPalletInfoFromDB <List <Pallet> >("SimPalletInfoTable");
            RackColumn.InitializePallet(RackColumn.SimListColumn, Pallet.SimListPallet);
        }
        public static void InitializePallet(List <RackColumn> listColumn, List <Pallet> listPallet)
        {
            foreach (Pallet pallet in listPallet)
            {
                // find location of this pallet
                RackColumn col = listColumn.Find(c => (c.Block == pallet.AtBlock) && (c.Number == pallet.AtColumn));
                if (col == null)
                {
                    continue;
                }

                // add to level at this column
                col.PalletCodes[pallet.AtLevel - 1] = pallet.Code;
            }
        }
        // Update position AGV icon in simulation mode (speed: cm/s)
        public static Point UpdatePositionAGV(int agvID)
        {
            // Find AGV in ListAGV
            var   index    = AGV.ListAGV.FindIndex(a => a.ID == agvID);
            AGV   agv      = AGV.ListAGV[index];
            Point position = new Point();

            // If this node is pick node, remove pallet code that was picked and save this time
            if (agv.Tasks.Count != 0 && agv.ExitNode == agv.Tasks[0].PickNode)
            {
                RackColumn column = RackColumn.ListColumn.Find(c => c.AtNode == agv.ExitNode);

                Pallet.SaveDeliveryTime(column.PalletCodes[agv.Tasks[0].PickLevel - 1], Pallet.ListPallet);
                DBUtility.UpdatePalletDB("PalletInfoTable", column.PalletCodes[agv.Tasks[0].PickLevel - 1], false,
                                         DateTime.Now.ToString("dddd, MMMM dd, yyyy  h:mm:ss tt"), Pallet.ListPallet);

                column.PalletCodes[agv.Tasks[0].PickLevel - 1] = null;
            }

            // Update label position
            List <Node> Nodes         = Node.ListNode;
            int         pixelDistance = (int)Math.Round(agv.DistanceToExitNode * Display.Scale);
            int         x             = Nodes[agv.ExitNode].X - Display.LabelAGV[agvID].Size.Width / 2;
            int         y             = Nodes[agv.ExitNode].Y - Display.LabelAGV[agvID].Size.Height / 2;

            switch (agv.Orientation)
            {
            case 'E':
                position.X = x + pixelDistance; position.Y = y;
                break;

            case 'W':
                position.X = x - pixelDistance; position.Y = y;
                break;

            case 'N':
                position.X = x; position.Y = y - pixelDistance;
                break;

            case 'S':
                position.X = x; position.Y = y + pixelDistance;
                break;
            }
            return(position);
        }
Ejemplo n.º 9
0
        private void rdbtnRealTime_CheckedChanged(object sender, EventArgs e)
        {
            if (!rdbtnRealTime.Checked)
            {
                return;
            }
            rdbtnRealTime.ForeColor   = Color.DarkRed;
            rdbtnSimulation.ForeColor = SystemColors.ActiveCaptionText;
            rdbtnRealTime.BackColor   = Color.MintCream;
            rdbtnSimulation.BackColor = Color.Lavender;
            btnPauseRun.Visible       = false;
            if (Communicator.SerialPort.IsOpen)
            {
                lbModeStatus.Text = "     Connection: " + Communicator.SerialPort.PortName + " ["
                                    + Communicator.SerialPort.BaudRate.ToString() + "-"
                                    + Communicator.SerialPort.Parity + "-"
                                    + Communicator.SerialPort.DataBits.ToString() + "-"
                                    + Communicator.SerialPort.StopBits + "]";
            }
            else
            {
                lbModeStatus.Text = "     Please set communication to run.";
            }

            Display.Mode           = "Real Time";
            timerComStatus.Enabled = true;

            // Add label of columns at node
            RackColumn.SimListColumn.ForEach(col => RackColumn.RemoveLabelColumn(pnFloor, col));
            RackColumn.ListColumn.ForEach(col => pnFloor.Controls.Add(col.ColumnLabel));

            // Remove simulation label, add real time label
            AGV.SimListAGV.ForEach(agv => Display.RemoveLabelAGV(pnFloor, agv.ID));
            foreach (AGV agv in AGV.ListAGV)
            {
                Display.AddLabelAGV(pnFloor, agv.ID, agv.ExitNode, agv.Orientation, agv.DistanceToExitNode);
            }

            // Clear draw path in panel
            Display.Points = new Point[] { new Point(), new Point() };
            pnFloor.Refresh();
        }
        public static void OutputAutoAdd(string palletCode, List <Task> listTaskToAdd, List <AGV> listAGV, List <RackColumn> listColumn)
        {
            // auto select agv
            if (listAGV.Count == 0)
            {
                return;
            }
            int agvID = AutoSelectAGV(listAGV);

            // find pick node & level
            RackColumn col       = listColumn.Find(c => c.PalletCodes.Contains(palletCode));
            int        pickNode  = col.AtNode;
            int        pickLevel = Array.IndexOf(col.PalletCodes, palletCode) + 1;

            // select drop node (output1 or output2)
            int dropNode = agvID % 2 == 1 ? 51 : 52;

            Task newTask = new Task("Auto " + palletCode, "Output", palletCode, agvID, pickNode, dropNode, pickLevel, 1, "Waiting");

            listTaskToAdd.Add(newTask);
        }
        public static dynamic PalletOnGoing <T>(RackColumn column, List <Task> listTask)
        {
            int        count         = 0;
            List <int> listDropLevel = new List <int>();

            List <Task> listTaskOnGoing = listTask.FindAll(t => t.DropNode == column.AtNode);

            count = listTaskOnGoing.Count;
            listTaskOnGoing.ForEach(t => listDropLevel.Add(t.DropLevel));

            if (typeof(T) == typeof(int))
            {
                return(count);
            }
            else if (typeof(T) == typeof(List <int>))
            {
                return(listDropLevel);
            }
            else
            {
                return(null);
            }
        }
        // Get list of rack column
        private static List <RackColumn> GetRackColums()
        {
            List <RackColumn> listCol = new List <RackColumn>();

            foreach (Node n in Node.ListNode)
            {
                if (n.LocationCode.Length == 0 || n.ID == 55 || n.ID == 56)
                {
                    continue;
                }

                RackColumn col = new RackColumn(n.ID);

                if (col.Number == 1 || col.Number == 2 || col.Number == 3)
                {
                    col.ColumnLabel.BackColor = SystemColors.ControlLight;
                    col.ColumnLabel.Size      = new Size(60 - 1, 35 - 2);
                    col.ColumnLabel.Name      = "ColumnAtNode" + n.ID.ToString();
                    int x = n.X - col.ColumnLabel.Size.Width / 2;
                    int y = n.Y + 35 + 3;
                    col.ColumnLabel.Location = new Point(x, y);
                }
                else if (col.Number == 4 || col.Number == 5 || col.Number == 6)
                {
                    col.ColumnLabel.BackColor = SystemColors.ControlLight;
                    col.ColumnLabel.Size      = new Size(60 - 1, 35 - 2);
                    col.ColumnLabel.Name      = "ColumnAtNode" + n.ID.ToString();
                    int x = n.X - col.ColumnLabel.Size.Width / 2;
                    int y = n.Y - 35 - 3 - col.ColumnLabel.Size.Height;
                    col.ColumnLabel.Location = new Point(x, y);
                }

                listCol.Add(col);
            }
            return(listCol);
        }
Ejemplo n.º 13
0
        private void timerGUI_Tick(object sender, EventArgs e)
        {
            // Show time now
            DateTime time = DateTime.Now;

            lbTime.Text = time.ToString("dddd, MMMM dd, yyyy     h:mm:ss tt");

            if (Display.Mode == "Real Time")
            {
                // Update data in listView AGVs
                Display.UpdateListViewAGVs(listViewAGVs, AGV.ListAGV);

                // Update data in listView Tasks
                Display.UpdateListViewTasks(listViewTasks, Task.ListTask);

                // Update column color of SimListColumn
                RackColumn.UpdateColumnColor(RackColumn.ListColumn);

                // Update location of AGV icon (label)
                foreach (AGV agv in AGV.ListAGV)
                {
                    Display.LabelAGV[agv.ID].Location = Display.UpdatePositionAGV(agv.ID);

                    //  Display agv carrying pallet
                    if (agv.Tasks.Count != 0 && agv.Tasks[0].Status == "Doing")
                    {
                        Display.LabelAGV[agv.ID].BackColor = Color.CornflowerBlue;
                    }
                    else
                    {
                        Display.LabelAGV[agv.ID].BackColor = Color.Silver;
                    }

                    // If goal was reached, remove old path, get next path (if exist)
                    if (agv.Path.Count == 0)
                    {
                        continue;
                    }
                    if (agv.ExitNode == agv.Path.LastOrDefault())
                    {
                        Task.AddNextPathOfAGV(agv);
                    }
                }

                // Update button add pallet
                if (isPickPalletInput1 == true)
                {
                    btnAddPallet1.Text      = "Add";
                    btnAddPallet1.BackColor = Color.WhiteSmoke;
                }
                if (isPickPalletInput2 == true)
                {
                    btnAddPallet2.Text      = "Add";
                    btnAddPallet2.BackColor = Color.WhiteSmoke;
                }
            }
            else if (Display.Mode == "Simulation")
            {
                // Update data in listView AGVs
                Display.UpdateListViewAGVs(listViewAGVs, AGV.SimListAGV);

                // Update data in listView Tasks
                Display.UpdateListViewTasks(listViewTasks, Task.SimListTask);

                // Update column color of SimListColumn
                RackColumn.UpdateColumnColor(RackColumn.SimListColumn);

                // If pause simulation is selected
                if (Display.SimPause == true)
                {
                    return;
                }

                // Detect collision
                Collision.Detect(AGV.SimListAGV);

                // Update position of AGV icon (label)
                foreach (AGV agv in AGV.SimListAGV)
                {
                    if (agv.Path.Count != 0)
                    {
                        agv.Velocity = AGV.SimSpeed;
                    }
                    else
                    {
                        agv.Velocity = 0f;
                    }
                    Display.SimLabelAGV[agv.ID].Location = Display.SimUpdatePositionAGV(agv.ID, agv.Velocity);

                    //  Display agv carrying pallet
                    if (agv.Tasks.Count != 0 && agv.Tasks[0].Status == "Doing")
                    {
                        Display.SimLabelAGV[agv.ID].BackColor = Color.CornflowerBlue;
                    }
                    else
                    {
                        Display.SimLabelAGV[agv.ID].BackColor = Color.Silver;
                    }
                }

                // Update button add pallet
                if (isPickSimPalletInput1 == true)
                {
                    btnAddPallet1.Text      = "Add";
                    btnAddPallet1.BackColor = Color.WhiteSmoke;
                }
                if (isPickSimPalletInput2 == true)
                {
                    btnAddPallet2.Text      = "Add";
                    btnAddPallet2.BackColor = Color.WhiteSmoke;
                }
            }
        }
        // Update position AGV icon in simulation mode (speed: cm/s)
        public static Point SimUpdatePositionAGV(int agvID, float speed)
        {
            // Find AGV in SimListAGV, get current point
            var   index    = AGV.SimListAGV.FindIndex(a => a.ID == agvID);
            AGV   agv      = AGV.SimListAGV[index];
            Point position = Display.SimLabelAGV[agvID].Location;

            // Handle (waiting method) collision if it happens
            CollisionStatus status = Collision.SimHandle(agv, Collision.SimListCollision);

            if (status == CollisionStatus.Handling)
            {
                // Update agv status and velocity
                agv.Velocity = 0;
                agv.Status   = "Stop";

                return(position);
            }

            // return old point when agv has no path
            if (agv.Path.Count == 0)
            {
                return(position);
            }

            // Get navigation frame array. Note: string is a reference type,
            // so any change in navigationArr is also in AGV.SimListAGV[index].navigationArr
            string[] navigationArr = agv.navigationArr;

            // Check whether current point is a node or not
            // Note: shift position of label to center (+LabelAGV[].Width/2, +LabelAGV[].Height/2)
            var node = Node.ListNode.FirstOrDefault(n =>
            {
                return((n.X == position.X + SimLabelAGV[agvID].Width / 2) &&
                       (n.Y == position.Y + SimLabelAGV[agvID].Height / 2));
            });

            // Current point is not a node and current position is start node,
            // it means initDistance to start node != 0, so go backward once then keep go ahead
            char orient = new char();

            if (node == null && agv.ExitNode == agv.Path[0])
            {
                switch (navigationArr[0])
                {
                case "A":
                    orient = UpdateOrient(agv.Orientation, "A");
                    break;

                case "B":
                    orient           = UpdateOrient(agv.Orientation, "B");
                    navigationArr[0] = "A";
                    break;
                }
            }
            // Current point is not a node and current position is not start node,
            // so keep go ahead
            else if (node == null)
            {
                orient = UpdateOrient(agv.Orientation, "A");
            }
            // If goal was reached, no update position, remove old path, get next path (if exist)
            else if (node.ID == agv.Path.LastOrDefault())
            {
                // Update AGV information
                agv.ExitNode           = node.ID; // Update ExitNode
                orient                 = UpdateOrient(agv.Orientation, "A");
                agv.Orientation        = orient;  // Update Orientation
                agv.DistanceToExitNode = 0f;      // Update Distance to ExitNode
                agv.Status             = "Stop";  // Update Status
                agv.Velocity           = 0;       // Update Velocity

                // Add next path
                Task.AddNextPathOfSimAGV(agv);

                return(position);
            }
            // Current point is at start node and initDistance to start node == 0
            // Turn direction once then keep go ahead
            else if (node.ID == agv.Path[0] && agv.DistanceToExitNode == 0f)
            {
                switch (navigationArr[0])
                {
                case "A":
                    orient = UpdateOrient(agv.Orientation, "A");
                    break;

                case "B":
                    orient           = UpdateOrient(agv.Orientation, "B");
                    navigationArr[0] = "A";
                    break;

                case "L":
                    orient           = UpdateOrient(agv.Orientation, "L");
                    navigationArr[0] = "A";
                    break;

                case "R":
                    orient           = UpdateOrient(agv.Orientation, "R");
                    navigationArr[0] = "A";
                    break;
                }

                // If this node is pick node, remove pallet code that was picked and save this time
                if (agv.Tasks.Count != 0 && node.ID == agv.Tasks[0].PickNode)
                {
                    RackColumn column = RackColumn.SimListColumn.Find(c => c.AtNode == agv.ExitNode);

                    Pallet.SaveDeliveryTime(column.PalletCodes[agv.Tasks[0].PickLevel - 1], Pallet.SimListPallet);
                    DBUtility.UpdatePalletDB("SimPalletInfoTable", column.PalletCodes[agv.Tasks[0].PickLevel - 1], false,
                                             DateTime.Now.ToString("dddd, MMMM dd, yyyy  h:mm:ss tt"), Pallet.SimListPallet);

                    column.PalletCodes[agv.Tasks[0].PickLevel - 1] = null;
                }
            }
            // Current point is a node but start node
            else
            {
                int    idx = Array.IndexOf(navigationArr, node.ID.ToString());
                string dir = navigationArr[idx + 1];
                orient = UpdateOrient(agv.Orientation, dir);
            }

            // Modify speed to make sure AGV icon can reach next node
            int i        = agv.Path.IndexOf(agv.ExitNode);
            int nextNode = agv.Path[i + 1];
            int dnx      = (position.X + SimLabelAGV[agvID].Width / 2) - Node.ListNode[nextNode].X;
            int dny      = (position.Y + SimLabelAGV[agvID].Height / 2) - Node.ListNode[nextNode].Y;
            int nd       = (int)Math.Sqrt(dnx * dnx + dny * dny);

            if (agv.ExitNode == agv.Path[0])
            {
                // At first node of path, having 4 cases of agv position
                // (dnx * dny != 0) for 2 cases and (dnx * dnx0 > 0 || dny * dny0 > 0) for the others
                int dnx0 = (position.X + SimLabelAGV[agvID].Width / 2) - Node.ListNode[agv.Path[0]].X;
                int dny0 = (position.Y + SimLabelAGV[agvID].Height / 2) - Node.ListNode[agv.Path[0]].Y;
                int nd0  = (int)Math.Sqrt(dnx0 * dnx0 + dny0 * dny0);
                if (dnx * dny != 0 || dnx * dnx0 > 0 || dny * dny0 > 0)
                {
                    nextNode = agv.Path[0];
                    nd       = nd0;
                }
            }
            int sp   = (int)Math.Round(speed * Display.Scale * (100.0 / 1000)); // timer1.Interval = 100ms
            int step = (nd % sp == 0) ? sp : (nd % sp);

            // Update AGV information before update position
            if (node != null)
            {
                agv.ExitNode = node.ID; // Update ExitNode
            }
            agv.Orientation = orient;   // Update Orientation
            int exitNode = agv.ExitNode;
            int dx       = (position.X + SimLabelAGV[agvID].Width / 2) - Node.ListNode[exitNode].X;
            int dy       = (position.Y + SimLabelAGV[agvID].Height / 2) - Node.ListNode[exitNode].Y;

            agv.DistanceToExitNode = (float)Math.Sqrt(dx * dx + dy * dy) / Display.Scale; // Update Distance to ExitNode
            agv.Status             = "Running";                                           // Update Status

            // Update next position of AGV icon in panel
            switch (orient)
            {
            case 'E':
                position = new Point(position.X + step, position.Y);
                break;

            case 'W':
                position = new Point(position.X - step, position.Y);
                break;

            case 'S':
                position = new Point(position.X, position.Y + step);
                break;

            case 'N':
                position = new Point(position.X, position.Y - step);
                break;
            }

            return(position);
        }
        // Add next path to agv when previous path reach goal (agv.ExitNode is being goal) (Simulation Mode)
        public static void AddNextPathOfSimAGV(AGV agv)
        {
            // Clear old path
            agv.Path.Clear();

            // Remove old task
            if (agv.Tasks.Count != 0 && agv.ExitNode == agv.Tasks[0].DropNode)
            {
                // Store pallet code to SimListColumn at this goal node
                RackColumn column = RackColumn.SimListColumn.Find(c => c.AtNode == agv.ExitNode);
                column.PalletCodes[agv.Tasks[0].DropLevel - 1] = agv.Tasks[0].PalletCode;
                if (column.AtNode != 51 & column.AtNode != 52)
                {
                    Pallet pallet = new Pallet(agv.Tasks[0].PalletCode, true,
                                               DateTime.Now.ToString("dddd, MMMM dd, yyyy  h:mm:ss tt"),
                                               column.Block, column.Number, agv.Tasks[0].DropLevel);
                    Pallet.SimListPallet.Add(pallet);
                    DBUtility.InsertNewPalletToDB("SimPalletInfoTable", pallet.Code, pallet.InStock, pallet.StoreTime,
                                                  pallet.AtBlock, pallet.AtColumn, pallet.AtLevel);
                }

                // Note: remove task in agv.Tasks and also in Task.SimListTask
                Task.SimListTask.Remove(Task.SimListTask.Find(a => a.Name == agv.Tasks[0].Name));
                agv.Tasks.RemoveAt(0);
            }

            // Add path to parking when don't have any task
            if (agv.Tasks.Count == 0)
            {
                // find all parking node
                List <int> parkingNode = new List <int>();
                foreach (Node n in Node.ListNode)
                {
                    if (n.LocationCode.Length == 0)
                    {
                        continue;
                    }
                    if (n.LocationCode[0] == 'P')
                    {
                        parkingNode.Add(n.ID);
                    }
                }
                // If current node is parking node or don't have parking node for this agv, do nothing,
                // otherwise, add path to park agv (agv will park at location in order of index)
                if (parkingNode.Contains(agv.ExitNode))
                {
                    return;
                }
                int parkAtNode = parkingNode.Find(n => parkingNode.IndexOf(n) == AGV.SimListAGV.IndexOf(agv));
                if (parkAtNode == 0)
                {
                    return;                  // be careful: in my node definition, parking nodes don't have node 0
                }
                agv.Path = Algorithm.A_starFindPath(agv.ExitNode, parkAtNode);

                // Add navigation frame of parking path
                string fr = Navigation.GetNavigationFrame(agv.Path, agv.Orientation, agv.DistanceToExitNode);
                agv.navigationArr = fr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                return;
            }

            // Add next path
            if (agv.ExitNode != agv.Tasks[0].PickNode)
            {
                agv.Path = Algorithm.A_starFindPath(agv.ExitNode, agv.Tasks[0].PickNode);
            }
            else
            {
                agv.Path            = Algorithm.A_starFindPath(agv.Tasks[0].PickNode, agv.Tasks[0].DropNode);
                agv.Tasks[0].Status = "Doing";

                // update button add pallet
                if (agv.Tasks[0].PickNode == 53)
                {
                    HomeScreenForm.isPickSimPalletInput1 = true;
                }
                else if (agv.Tasks[0].PickNode == 54)
                {
                    HomeScreenForm.isPickSimPalletInput2 = true;
                }
            }

            // Add next navigation frame of next path
            string frame = Navigation.GetNavigationFrame(agv.Path, agv.Orientation, agv.DistanceToExitNode);

            agv.navigationArr = frame.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        }