/// <summary> /// Tells the container it was loaded on the conveyor belt. /// </summary> /// <param name="recipe">The recipe according to which it will henceforth be processed.</param> public void OnLoaded(Recipe recipe) { if (Recipe != null) throw new InvalidOperationException("Container already belongs to a recipe"); Recipe = recipe; _statePrefixLength++; // first capability will always be ProduceCapability }
public static Model NoRedundancyCircularModel() { // create 3 stations var dispenser = new ParticulateDispenser(); var stations = new Station[] { new ContainerLoader(), dispenser, new PalletisationStation() }; dispenser.SetStoredAmount(IngredientType.BlueParticulate, 50u); dispenser.SetStoredAmount(IngredientType.RedParticulate, 50u); dispenser.SetStoredAmount(IngredientType.YellowParticulate, 50u); // connect them to a circle for (var i = 0; i < stations.Length; ++i) { var next = stations[(i + 1) % stations.Length]; stations[i].Outputs.Add(next); next.Inputs.Add(stations[i]); } var model = new Model(stations, new FastObserverController(stations)); var recipe = new Recipe(ingredients: new[] { new Ingredient(IngredientType.BlueParticulate, 12), new Ingredient(IngredientType.RedParticulate, 4), new Ingredient(IngredientType.YellowParticulate, 5) }, amount: 3); model.ScheduleProduction(recipe); return model; }
private void ConfigureInternal(Recipe recipe) { RemoveObsoleteConfiguration(recipe); // find optimal path that satisfies the required capabilities var path = FindStationPath(recipe); if (path == null) Unsatisfiable = true; else ApplyConfiguration(recipe, path); }
public override void Configure(Recipe recipe) { Configure(new[] { recipe }); }
/// <summary> /// Configures the <see cref="_availableStations" /> to produce resource for the <paramref name="recipe" />. /// </summary> private void ApplyConfiguration(Recipe recipe, int[] path) { Station lastStation = null; Role lastRole = default(Role); for (var i = 0; i < path.Length; ++i) { var station = _availableStations[path[i]]; var role = lastRole; if (station != lastStation) { if (lastStation != null) // configure connection between stations { var connection = GetShortestPath(path[i - 1], path[i]).ToArray(); // for each station on connection, except lastStation and station: for (var j = 1; j < connection.Length - 1; ++j) { var link = _availableStations[connection[j]]; lastRole.PostCondition.Port = link; // connect to previous var linkRole = GetRole(recipe, lastStation, lastRole.PostCondition); link.AllocatedRoles.Add(linkRole); // add empty (transport) role lastStation = link; lastRole = linkRole; } lastRole.PostCondition.Port = station; // finish connection } // configure station itself role = GetRole(recipe, lastStation, lastStation == null ? (Condition?)null : lastRole.PostCondition); station.AllocatedRoles.Add(role); } var capability = recipe.RequiredCapabilities[i]; role.AddCapabilityToApply(capability); role.PostCondition.AppendToState(capability); lastStation = station; lastRole = role; } }
/// <summary> /// Checks if the given station can satisfy all the demanded capabilities. /// </summary> /// <param name="recipe">The recipe for which a path is searched.</param> /// <param name="path">The current path.</param> /// <param name="prefixLength">The length of the path prefix that should be considered valid.</param> /// <param name="station">The station which should be next on the path.</param> /// <returns>True if choosing station as next path entry would not exceed its capabilities.</returns> private bool CanSatisfyNext(Recipe recipe, int[] path, int prefixLength, int station) { var capabilities = from index in Enumerable.Range(0, prefixLength + 1) where index == prefixLength || path[index] == station select recipe.RequiredCapabilities[index]; return Capability.IsSatisfiable(capabilities.ToArray(), _availableStations[station].AvailableCapabilities); }
/// <summary> /// Recursively checks if there is a valid path with the given prefix for the recipe. /// If so, returns true and /// <param name="path" /> /// contains the path. Otherwise, returns false. /// </summary> private bool FindStationPath(Recipe recipe, int[] path, int prefixLength) { // termination case: the path is already complete if (prefixLength == recipe.RequiredCapabilities.Length) return true; var last = path[prefixLength - 1]; // special handling: see if the last station can't do the next capability as well if (CanSatisfyNext(recipe, path, prefixLength, last)) { path[prefixLength] = last; if (FindStationPath(recipe, path, prefixLength + 1)) return true; } else // otherwise check connected stations { for (int next = 0; next < _availableStations.Length; ++next) // go through all stations { // if connected to last station and can fulfill next capability if (_pathMatrix[last, next] != -1 && CanSatisfyNext(recipe, path, prefixLength, next) && next != last) { path[prefixLength] = next; // try a path over next if (FindStationPath(recipe, path, prefixLength + 1)) // if there is such a path, return true return true; } } } return false; // there is no valid path with the given prefix }
/// <summary> /// Finds a sequence of connected stations that are able to fulfill the /// <param name="recipe" /> /// 's capabilities. /// </summary> /// <returns> /// An array of station identifiers, one for each capability. This array does not include stations /// that only transport a resource from one to the next. /// </returns> private int[] FindStationPath(Recipe recipe) { var path = new int[recipe.RequiredCapabilities.Length]; for (var first = 0; first < _availableStations.Length; ++first) { if (Capability.IsSatisfiable(new[] { recipe.RequiredCapabilities[0] }, _availableStations[first].AvailableCapabilities)) { path[0] = first; if (FindStationPath(recipe, path, 1)) return path; } } return null; }
public void ScheduleProduction(Recipe recipe) { ObserverController.ScheduleConfiguration(recipe); }
/// <summary> /// Removes all configuration related to a recipe and propagates /// this change to neighbouring stations. /// </summary> /// <param name="recipe"></param> protected void RemoveRecipeConfigurations(Recipe recipe) { var obsoleteRoles = (from role in AllocatedRoles where role.Recipe == recipe select role) .ToArray(); // collect roles before underlying collection is modified var affectedNeighbours = (from role in obsoleteRoles select role.PreCondition.Port) .Concat(from role in obsoleteRoles select role.PostCondition.Port) .Distinct() .Where(neighbour => neighbour != null); foreach (var role in obsoleteRoles) AllocatedRoles.Remove(role); foreach (var neighbour in affectedNeighbours) neighbour.RemoveRecipeConfigurations(recipe); }
public void BeforeReconfiguration(Recipe recipe) { // if the current resource's recipe was reconfigured, drop it from production // // TODO: try to fix it, i.e. check if the reconfiguration even affected // currentRole.PostCondition.Port or removed currentRole, and if so, // where to send the resource next if (Container != null && Container.Recipe == recipe) { Container.Recipe.DropContainer(Container); Container = null; } _resourceRequests.RemoveAll(request => request.Condition.Recipe == recipe); }
/// <summary> /// Removes all configuration for the given <paramref name="recipe" /> from all stations /// under this instance's control. /// </summary> protected void RemoveObsoleteConfiguration(Recipe recipe) { foreach (var station in AvailableStations) { var obsoleteRoles = (from role in station.AllocatedRoles where role.Recipe == recipe select role) .ToArray(); foreach (var role in obsoleteRoles) station.AllocatedRoles.Remove(role); station.BeforeReconfiguration(recipe); } }
/// <summary> /// (Re-)Configures a <paramref name="recipe" />. /// </summary> public abstract void Configure(Recipe recipe);
/// <summary> /// Schedules a <paramref name="recipe" /> for initial configuration once simulation /// or model checking starts. Typically called during model setup. /// </summary> public void ScheduleConfiguration(Recipe recipe) { _scheduledRecipes.Add(recipe); }
/// <summary> /// Get a fresh role from the <see cref="RolePool" />. /// </summary> /// <param name="recipe">The recipe the role will belong to.</param> /// <param name="input">The station the role declares as input port. May be null.</param> /// <param name="previous">The previous condition (postcondition of the previous role). May be null.</param> /// <returns>A <see cref="Role" /> instance with the given data.</returns> protected Role GetRole(Recipe recipe, Station input, Condition? previous) { var role = new Role(); // update precondition role.PreCondition.Recipe = recipe; role.PreCondition.Port = input; role.PreCondition.ResetState(); if (previous != null) role.PreCondition.CopyStateFrom(previous.Value); // update postcondition role.PostCondition.Recipe = recipe; role.PostCondition.Port = null; role.PostCondition.ResetState(); role.PostCondition.CopyStateFrom(role.PreCondition); role.ResetCapabilitiesToApply(); return role; }