// 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);
        }
        // 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);
        }