/// <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
		}
Exemple #2
0
		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;
		}
Exemple #9
0
		public void ScheduleProduction(Recipe recipe)
		{
			ObserverController.ScheduleConfiguration(recipe);
		}
Exemple #10
0
		/// <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);
		}
Exemple #11
0
		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;
		}