/// <summary> /// Runs the simulation on the current thread and waits for the simulation /// to complete before returning to caller. Simulation is NOT cloned before /// running. Use instance of Runner to get more options for running a /// simulation or groups of simulations. /// </summary> /// <param name="cancelToken">Is cancellation pending?</param> public void Run(CancellationTokenSource cancelToken = null) { IsRunning = true; // If the cancelToken is null then give it a default one. This can happen // when called from the unit tests. if (cancelToken == null) { cancelToken = new CancellationTokenSource(); } // Remove disabled models. RemoveDisabledModels(this); // If this simulation was not created from deserialisation then we need // to parent all child models correctly and call OnCreated for each model. bool hasBeenDeserialised = Children.Count > 0 && Children[0].Parent == this; if (!hasBeenDeserialised) { // Parent all models. Apsim.ParentAllChildren(this); // Call OnCreated in all models. Apsim.ChildrenRecursively(this).ForEach(m => m.OnCreated()); } if (Services == null || Services.Count < 1) { var simulations = Apsim.Parent(this, typeof(Simulations)) as Simulations; if (simulations != null) { Services = simulations.GetServices(); } else { Services = new List <object>(); IDataStore storage = Apsim.Find(this, typeof(IDataStore)) as IDataStore; if (storage != null) { Services.Add(Apsim.Find(this, typeof(IDataStore))); } Services.Add(new ScriptCompiler()); } } var links = new Links(Services); var events = new Events(this); try { // Connect all events. events.ConnectEvents(); // Resolve all links links.Resolve(this, true); // Invoke our commencing event to let all models know we're about to start. Commencing?.Invoke(this, new EventArgs()); // Begin running the simulation. DoCommence?.Invoke(this, new CommenceArgs() { CancelToken = cancelToken }); } catch (Exception err) { // Exception occurred. Write error to summary. string errorMessage = "ERROR in file: " + FileName + Environment.NewLine + "Simulation name: " + Name + Environment.NewLine; errorMessage += err.ToString(); summary?.WriteError(this, errorMessage); // Rethrow exception throw new Exception(errorMessage, err); } finally { // Signal that the simulation is complete. Completed?.Invoke(this, new EventArgs()); // Disconnect our events. events.DisconnectEvents(); // Unresolve all links. links.Unresolve(this, true); IsRunning = false; } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> /// <param name="allModels">A collection of all model wrappers</param> private void ResolveInternal(object obj, List <ModelWrapper> allModels) { // Go looking for [Link]s foreach (IVariable field in GetAllDeclarations(GetModel(obj), GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { // Get the field type or the array element if it is an array field. Type fieldType = field.DataType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { fieldType = field.DataType.GenericTypeArguments[0]; } // Try and get a match from our services first. List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); // If no match on services then try other options. if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = Apsim.Parent(obj as IModel, typeof(Simulation)) as Simulation; if (fieldType.IsAssignableFrom(typeof(ILocator)) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (fieldType.IsAssignableFrom(typeof(IEvent)) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } // If no match on services then try other options. if (matches.Count == 0) { // Get a list of models that could possibly match. if (link is ParentLinkAttribute) { matches = new List <object>(); matches.Add(GetParent(obj, fieldType)); } else if (link is LinkByPathAttribute) { object match = Apsim.Get(obj as IModel, (link as LinkByPathAttribute).Path); if (match != null) { matches.Add(match); } } else if (link.IsScoped(field)) { matches = GetModelsInScope(obj, allModels); } else { matches = GetChildren(obj); } } // Filter possible matches to those of the correct type. matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); // If we should use name to match then filter matches to those with a matching name. if (link.UseNameToMatch(field)) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.DataType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.Value = array; } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.Value = array; } else if (matches.Count == 0) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && !link.IsScoped(field)) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.Value = GetModel(matches[0]); } } } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> /// <param name="scope">The scoping rules to use to resolve links.</param> private void ResolveInternal(object obj, ScopingRules scope) { foreach (IVariable field in GetAllDeclarations(GetModel(obj), GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public, allLinks: true)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { Type fieldType = field.DataType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { fieldType = field.DataType.GenericTypeArguments[0]; } List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = Apsim.Parent(obj as IModel, typeof(Simulation)) as Simulation; if (fieldType.IsAssignableFrom(typeof(ILocator)) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (fieldType.IsAssignableFrom(typeof(IEvent)) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } if (matches.Count == 0) { if (link.Type == LinkType.Ancestor) { matches = new List <object>(); matches.Add(GetParent(obj, fieldType)); } else if (link.Type == LinkType.Path) { var locater = new Locater(); object match = locater.Get(link.Path, obj as Model); if (match != null) { matches.Add(match); } } else if (link.Type == LinkType.Scoped) { matches = scope.FindAll(obj as IModel).Cast <object>().ToList(); } else { matches = GetChildren(obj); } } matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); if (link.ByName) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.DataType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.Value = array; } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.Value = array; } else if (matches.Count == 0) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && link.Type != LinkType.Scoped) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.Value = GetModel(matches[0]); } } } }