public void TestGraphSmallerWeightsUponDecay() { Graph g = new Graph(1); Node a = new Node("a"); Node b = new Node("b"); Edge ab = new Edge(2) { Visited = 20, Blocked = 1 }; g.AddNode(a); g.AddNode(b); g.AddUndirectedEdge(new Tuple<Node, int>(a, 1), new Tuple<Node, int>(b, 3), ab); g.Decay(0.5); double actual = ab.Weight; const double expected = 2.05; Assert.IsTrue(Math.Abs(actual - expected) < 0.01); }
// Consumes one package private void ConsumePackage(byte[] package) { // Allocates data from the package byte[] data = new byte[package.Length - 3]; // Finds the type of pakcage byte byteType = package[1]; // Fills the data array with the corresponding data for (int i = 2, j = 0; i < package.Length - 1; i++, j++) { data[j] = package[i]; } // Convert the data to a string string dataString = Encoding.UTF8.GetString(data, 0, data.Length); // Find the correct method to consume the package switch (byteType) { case TYPE_UPDATE_COLOR: // Tell the user that the color is being fetched // dataString contains the id of the color ConsoleHandler.AddMessage(MessageType.BLUETOOTH, "Sending color #" + dataString + "..."); // Get the color from the database Color requestedColor = Database.Instance.Data.Colors.FirstOrDefault(i => i.Value.Identifier == int.Parse(dataString)).Value; // Convert the RGB-values of the color to byte array byte[] returnData = requestedColor.ToRGBBytes(); // Send the package back to the NXT SendPackageBT(TYPE_FETCHED_COLOR, returnData); break; case TYPE_SAVE_COLOR: // Split the data into to values that needs to be saved int colorId = int.Parse(dataString.Substring(0, 3)); // Use the first char as color identifer int red = int.Parse(dataString.Substring(3, 3)); // Use the first char as red value int green = int.Parse(dataString.Substring(6, 3)); // Use the first char as green value int blue = int.Parse(dataString.Substring(9, 3)); // Use the first char as blue value // Tell the user that the color is being saved ConsoleHandler.AddMessage(MessageType.BLUETOOTH, "Received color #" + colorId + ". (" + red + ", " + green + ", " + blue + ")"); // Find the old color Color color = Database.Instance.Data.Colors.FirstOrDefault(i => i.Value.Identifier == colorId).Value; // Update the values color.Red = red; color.Green = green; color.Blue = blue; // Save the updated color Database.Instance.Data.AddColor(color); break; case TYPE_REPORT_OBSTACLE: ConsoleHandler.AddMessage(MessageType.ERROR, "Obstacle encountered!"); int directionsIndex = int.Parse(dataString); if (!(CurrentJob is TurnJob) && !(CurrentJob is DebugJob)) { if (CurrentJob != null) { Path currentPath = CurrentJob.GetPath(); String directions = new ForkliftPath(currentPath, forklift.RearNode).getDirections(); if (CurrentJob is PalletJob) { PalletJob pj = (PalletJob)CurrentJob; if (pj.Type == PalletJobType.fetch) { directions = directions.Insert(0, "TUND"); } else if (pj.Type == PalletJobType.deliver) { directions = directions.Insert(0, "TUBDN"); } } int max = directions.Length; int i = directionsIndex; if (directions[directions.Length - 1] == 'L' || directions[directions.Length - 1] == 'R') { max--; } double nodeRearIndex = (max - 1.0 - i) / 2.0; if (Math.Abs(nodeRearIndex % 1 - 0.5) < 0.4) { ConsoleHandler.AddMessage(MessageType.ERROR, "Currently inside intersection - aborting all jobs"); forklift.FrontNode = null; forklift.FrontNode = null; // Inform the user to update the position ConsoleHandler.AddMessage(MessageType.ERROR, "Please update the position of the forklift..."); while (forklift.FrontNode == null || forklift.RearNode == null) { ; } } else { Node newRearNode = null; Node newFrontNode = null; int roundedIndex = (int)nodeRearIndex; if (roundedIndex == 0) { newFrontNode = forklift.FrontNode; newRearNode = forklift.RearNode; } else { newFrontNode = currentPath.Nodes[roundedIndex]; newRearNode = currentPath.Nodes[roundedIndex - 1]; } // Update the visitied count on edges Node previousNode = null; foreach (Node n in currentPath.Nodes) { if (previousNode == null) { previousNode = n; continue; } // Find edge to the previous node and increment the visitied count KeyValuePair <Node, Edge> nodeEdgePair = previousNode.Neighbours.Single(x => x.Key != null && x.Key.Equals(n)); nodeEdgePair.Value.Visited++; if (n.Equals(newFrontNode)) { break; } previousNode = n; } // Update forklift nodes forklift.FrontNode = newFrontNode; forklift.RearNode = newRearNode; // Update edge the NXT is standing on Database.Instance.Data.Graphs.FirstOrDefault().Value.BlockEdge(newFrontNode, newRearNode); } } else { Commands.PrintError("No current job - Continuing..."); } } else if (CurrentJob is TurnJob) { ConsoleHandler.AddMessage(MessageType.REGULAR, "Already performing turn-job... (ignoreing)"); } else if (CurrentJob is DebugJob) { ConsoleHandler.AddMessage(MessageType.REGULAR, "Performing debug-job... (ignoreing debugjobs)"); } break; // Check if the NXT updated its status case TYPE_UPDATE_STATUS: { if (dataString[0] != GetStatusByte(forklift)) { // Tell the user what the status the NXT updated to ConsoleHandler.AddMessage(MessageType.BLUETOOTH, "Received " + ConsoleHandler.DNS + "-status: " + dataString); } // Check what status the NXT told us switch (dataString[0]) { // The NXT was idle case STATUS_IDLE: // Check if the nxt just completed a job if (forklift.Status == Status.BUSY && CurrentJob != null) { Path p = CurrentJob.GetPath(); // Update the visited count on traversed edges Node previousNode = null; foreach (Node n in p.Nodes) { if (previousNode == null) { previousNode = n; continue; } // Find edge to the previous node and increment the visitied count KeyValuePair <Node, Edge> nodeEdgePair = previousNode.Neighbours.Single(x => x.Key != null && x.Key.Equals(n)); nodeEdgePair.Value.Visited++; previousNode = n; } // Update the position of the forklift if (p.Nodes.Count >= 2) { Node frontNode = p.Nodes.ElementAt(p.Nodes.Count - 1); Node rearNode = p.Nodes.ElementAt(p.Nodes.Count - 2); Forklift f = Database.Instance.Data.Forklifts.FirstOrDefault().Value; f.UpdateNodes(frontNode, rearNode); } if (!(CurrentJob is TurnJob)) { Graph g = Database.Instance.Data.Graphs.FirstOrDefault().Value; // Unblock all edges for (int i = g.BlockedEdges.Count - 1; i >= 0; i--) { Tuple <Node, Node> edge = g.BlockedEdges[i]; g.UnblockEdge(edge.Item1, edge.Item2); } // Decay the graph g.Decay(0.90); } if (CurrentJob is PalletJob) { PalletJob job = (PalletJob)CurrentJob; // Check if the forklift just finished a deliver pallet job if (job.Type == PalletJobType.deliver) { Pallet pallet = forklift.Payload; // Update the location of the pallet pallet.Location = p.Nodes.Last(); // Update the payload of the forklift forklift.Payload = null; } else if (job.Type == PalletJobType.fetch) { Node n = p.Nodes.Last(); // Update the payload (Which will also update the location of the pallet) forklift.Payload = n.Pallet; } // Swap front and rear node forklift.UpdateNodes(forklift.RearNode, forklift.FrontNode); } CurrentJob = null; } // Update the internal status forklift.Status = Status.IDLE; // Check if PALL-E should get another job if (CurrentJob == null && forklift.Status == Status.IDLE) { Job nextJob = null; while ((nextJob == null || nextJob.GetPath() != null) && JobList.Count > 0) { // Get the next job (job with lowest id) nextJob = JobList.Aggregate((l, r) => l.Key < r.Key ? l : r).Value; try { nextJob.GetPath(); break; } catch (PathException e) { Commands.PrintError("Remove job #" + nextJob.ID() + ": '" + e.Message + "'"); } catch (JobException e) { Commands.PrintError("Remove job #" + nextJob.ID() + ": '" + e.Message + "'"); } Database.Instance.Data.RemoveJob(nextJob); nextJob = null; } // We removed all the jobs, nothing left to do! if (JobList.Count < 1) { break; } try { // Test if the path can be generated nextJob.GetPath(); // Tell the user what job was sent Commands.PrintSuccess("Sending Job #" + nextJob.ID() + " to PALL-E. " + (JobList.Count - 1) + " jobs left"); // Send the job to the NXT SendPackageBT(nextJob.GetJobTypeBytes(), nextJob.GetBytes()); } catch (JobException e) { Commands.PrintError("Job #" + nextJob.ID() + " cancelled: '" + e.Message + "'"); Database.Instance.Data.RemoveJob(nextJob); } } break; // The NXT was busy case STATUS_BUSY: // If the NXT was just idle, you know you have given it a job if (forklift.Status == Status.IDLE) { // Debug jobs has highe priority if (JobList.Count > 0) { // Remove the job that is being executed currently CurrentJob = JobList.Aggregate((l, r) => l.Key < r.Key ? l : r).Value; Database.Instance.Data.Jobs.Remove(CurrentJob.Identifier); } } // Update the internal status forklift.Status = Status.BUSY; break; case STATUS_OBSTACLE: if (CurrentJob != null) { if (!(CurrentJob is DebugJob)) { if (!(CurrentJob is TurnJob)) { ConsoleHandler.AddMessage(MessageType.REGULAR, "Calculating alternative path..."); Database.Instance.Data.AddJob(CurrentJob); CurrentJob = new TurnJob(Database.Instance.Data.Jobs.Keys.Min() - 1); Node oldFrontNode = forklift.FrontNode; Node oldRearNode = forklift.RearNode; forklift.FrontNode = oldRearNode; forklift.RearNode = oldFrontNode; ConsoleHandler.AddMessage(MessageType.REGULAR, "Sending turn-job to PALL-E..."); } else { ConsoleHandler.AddMessage(MessageType.REGULAR, "Resending turn-job to " + ConsoleHandler.DNS + "..."); } // Send turn job SendPackageBT(CurrentJob.GetJobTypeBytes(), CurrentJob.GetBytes()); } else { // Send empty package (so that obstacle status will be avoided) DebugJob dj = new DebugJob(-1, ""); SendPackageBT(dj.GetJobTypeBytes(), dj.GetBytes()); } } else { ConsoleHandler.AddMessage(MessageType.ERROR, "Tried to recover from obstacle, but there are no jobs left"); // throw new Exception("No current jobs"); } // The status should not be updated if the current job is a debugjob if (!(CurrentJob is DebugJob)) { // Update the internal status forklift.Status = Status.OBSTACLE; } break; // The NXT encoutered an error case STATUS_ERROR: // Update the internal status forklift.Status = Status.ERROR; // Tell the user that the NXT encountered an error ConsoleHandler.AddMessage(MessageType.ERROR, ConsoleHandler.DNS + " reported an error"); break; default: forklift.Status = Status.UNKNOWN; break; } break; } } return; }