/// <summary> /// Get the value of a variable or model. /// </summary> /// <param name="namePath">The name of the object to return</param> /// <param name="relativeTo">The model calling this method</param> /// <param name="ignoreCase">If true, ignore case when searching for the object or property</param> /// <returns>The found object or null if not found</returns> public IVariable GetInternal(string namePath, Model relativeTo, bool ignoreCase = false) { Model relativeToModel = relativeTo; string cacheKey = namePath; StringComparison compareType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; // Look in cache first. object value = GetFromCache(cacheKey, relativeToModel); if (value != null) { return(value as IVariable); } IVariable returnVariable = null; if (namePath == null || namePath.Length == 0) { return(null); } else if (namePath[0] != '.' && namePath.Replace("()", "").IndexOfAny("(+*/".ToCharArray()) != -1) { // expression - need a better way of detecting an expression returnVariable = new VariableExpression(namePath, relativeToModel); } else { // Remove a square bracketed model name and change our relativeTo model to // the referenced model. if (namePath.StartsWith("[")) { int posCloseBracket = namePath.IndexOf(']'); if (posCloseBracket == -1) { return(null); } string modelName = namePath.Substring(1, posCloseBracket - 1); namePath = namePath.Remove(0, posCloseBracket + 1); Model foundModel = Apsim.Find(relativeToModel, modelName) as Model; if (foundModel == null) { // Didn't find a model with a name matching the square bracketed string so // now try and look for a model with a type matching the square bracketed string. Type[] modelTypes = GetTypeWithoutNameSpace(modelName); if (modelTypes.Length == 1) { foundModel = Apsim.Find(relativeToModel, modelTypes[0]) as Model; } } if (foundModel == null) { return(null); } else { relativeToModel = foundModel; } } else if (namePath.StartsWith(".")) { // Absolute path Model root = relativeToModel; while (root.Parent != null) { root = root.Parent as Model; } relativeToModel = root; int posPeriod = namePath.IndexOf('.', 1); if (posPeriod == -1) { // Path starts with a . and only contains a single period. // If name matches then no problem. Otherwise we need to return null. posPeriod = namePath.IndexOf('.'); if (namePath.Remove(0, posPeriod) == relativeToModel.Name) { posPeriod = namePath.Length; } } namePath = namePath.Remove(0, posPeriod); if (namePath.StartsWith(".")) { namePath = namePath.Remove(0, 1); } } // Now walk the series of '.' separated path bits, assuming the path bits // are child models. Stop when we can't find the child model. string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); int i; for (i = 0; i < namePathBits.Length; i++) { IModel localModel = relativeToModel.Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType)); if (localModel == null) { // Allow for the possibility that the first path element may point to // the starting parent model, rather than to a child within that model if ((i == 0) && relativeToModel.Name.Equals(namePathBits[i], compareType)) { continue; } else { break; } } else { relativeToModel = localModel as Model; } } // At this point there are only 2 possibilities. We have encountered a // PropertyInfo or the path is invalid. // We now need to loop through the remaining path bits and keep track of each // section of the path as each section will have to be evaulated everytime a // a get is done for this path. // The variable 'i' will point to the name path that cannot be found as a model. object relativeToObject = relativeToModel; List <IVariable> properties = new List <IVariable>(); properties.Add(new VariableObject(relativeToModel)); for (int j = i; j < namePathBits.Length; j++) { // look for an array specifier e.g. sw[2] string arraySpecifier = null; if (namePathBits[j].Contains("[")) { arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']'); } // Look for either a property or a child model. IModel localModel = null; if (relativeToObject == null) { return(null); } PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]); if (propertyInfo == null && ignoreCase) // If not found, try using a case-insensitive search { propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j], BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase); } if (relativeToObject is IFunction && namePathBits[j] == "Value()") { MethodInfo method = relativeToModel.GetType().GetMethod("Value"); properties.Add(new VariableMethod(relativeToModel, method)); } else if (propertyInfo == null && relativeToObject is Model) { // Not a property, may be a child model. localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType)); if (localModel == null) { return(null); } properties.Add(new VariableObject(localModel)); relativeToObject = localModel; } else if (propertyInfo != null) { VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier); properties.Add(property); relativeToObject = property.Value; } else if (relativeToObject is IList) { // Special case: we are trying to get a property of an array(IList). In this case // we want to return the property value for all items in the array. VariableProperty property = new VariableProperty(relativeToObject, namePathBits[j], arraySpecifier); properties.Add(property); relativeToObject = property.Value; } else { return(null); } } // We now have a list of IVariable instances that can be evaluated to // produce a return value for the given path. Wrap the list into an IVariable. returnVariable = new VariableComposite(namePath, properties); } // Add variable to cache. AddToCache(cacheKey, relativeTo, returnVariable); return(returnVariable); }
/// <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) { // 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) { Services = new List <object>(); IDataStore storage = Apsim.Find(this, typeof(IDataStore)) as IDataStore; if (storage != null) { Services.Add(Apsim.Find(this, typeof(IDataStore))); } } 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); } }
/// <summary>Documents the specified model.</summary> /// <param name="modelNameToDocument">The model name to document.</param> /// <param name="tags">The auto doc tags.</param> /// <param name="headingLevel">The starting heading level.</param> public void DocumentModel(string modelNameToDocument, List <AutoDocumentation.ITag> tags, int headingLevel) { Simulation simulation = Apsim.Find(this, typeof(Simulation)) as Simulation; if (simulation != null) { // Find the model of the right name. IModel modelToDocument = Apsim.Find(simulation, modelNameToDocument); // If not found then find a model of the specified type. if (modelToDocument == null) { modelToDocument = Apsim.Get(simulation, "[" + modelNameToDocument + "]") as IModel; } // If the simulation has the same name as the model we want to document, dig a bit deeper if (modelToDocument == simulation) { modelToDocument = Apsim.ChildrenRecursivelyVisible(simulation).FirstOrDefault(m => m.Name.Equals(modelNameToDocument, StringComparison.OrdinalIgnoreCase)); } // If still not found throw an error. if (modelToDocument != null) { // Get the path of the model (relative to parentSimulation) to document so that // when replacements happen below we will point to the replacement model not the // one passed into this method. string pathOfSimulation = Apsim.FullPath(simulation) + "."; string pathOfModelToDocument = Apsim.FullPath(modelToDocument).Replace(pathOfSimulation, ""); // Clone the simulation Simulation clonedSimulation = Apsim.Clone(simulation) as Simulation; // Make any substitutions. MakeSubstitutions(clonedSimulation); // Now use the path to get the model we want to document. modelToDocument = Apsim.Get(clonedSimulation, pathOfModelToDocument) as IModel; if (modelToDocument == null) { throw new Exception("Cannot find model to document: " + modelNameToDocument); } // resolve all links in cloned simulation. Links.Resolve(clonedSimulation); modelToDocument.IncludeInDocumentation = true; foreach (IModel child in Apsim.ChildrenRecursively(modelToDocument)) { child.IncludeInDocumentation = true; } // Document the model. AutoDocumentation.DocumentModel(modelToDocument, tags, headingLevel, 0, documentAllChildren: true); // Unresolve links. Links.Unresolve(clonedSimulation); } } }
/// <summary>Gets a model in scope of the specified type</summary> /// <param name="typeToMatch">The type of the model to return</param> /// <returns>The found model or null if not found</returns> public IModel Get(Type typeToMatch) { return(Apsim.Find(relativeToModel, typeToMatch)); }
/// <summary> /// Get the value of a variable or model. /// </summary> /// <param name="namePath">The name of the object to return</param> /// <param name="ignoreCase">If true, ignore case when searching for the object or property</param> /// <returns>The found object or null if not found</returns> private IVariable GetInternal(string namePath, bool ignoreCase = true) { IModel relativeTo = relativeToModel; string cacheKey = namePath; StringComparison compareType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; // Look in cache first. object value = null; if (cache.ContainsKey(cacheKey)) { value = cache[cacheKey]; } if (value != null) { return(value as IVariable); } IVariable returnVariable = null; if (namePath == null || namePath.Length == 0) { return(null); } else if (namePath[0] != '.' && (namePath.Replace("()", "").IndexOfAny("+*/".ToCharArray()) != -1 | (namePath.IndexOfAny("(".ToCharArray()) >= 0 && namePath.Substring(0, (namePath.IndexOf('(') >= 0? namePath.IndexOf('(') : 0)).IndexOfAny("[.".ToCharArray()) == -1))) { // expression - need a better way of detecting an expression returnVariable = new VariableExpression(namePath, relativeTo as Model); } else { namePath = namePath.Replace("Value()", "Value()."); // Remove a square bracketed model name and change our relativeTo model to // the referenced model. if (namePath.StartsWith("[")) { int posCloseBracket = namePath.IndexOf(']'); if (posCloseBracket == -1) { return(null); } string modelName = namePath.Substring(1, posCloseBracket - 1); namePath = namePath.Remove(0, posCloseBracket + 1); Model foundModel = Apsim.Find(relativeTo, modelName) as Model; if (foundModel == null) { // Didn't find a model with a name matching the square bracketed string so // now try and look for a model with a type matching the square bracketed string. Type[] modelTypes = GetTypeWithoutNameSpace(modelName); if (modelTypes.Length == 1) { foundModel = Apsim.Find(relativeTo, modelTypes[0]) as Model; } } if (foundModel == null) { return(null); } else { relativeTo = foundModel; } } else if (namePath.StartsWith(".")) { // Absolute path IModel root = relativeTo; while (root.Parent != null) { root = root.Parent as Model; } relativeTo = root; int posPeriod = namePath.IndexOf('.', 1); if (posPeriod == -1) { posPeriod = namePath.Length; } namePath = namePath.Remove(0, posPeriod); if (namePath.StartsWith(".")) { namePath.Remove(1); } } // Now walk the series of '.' separated path bits, assuming the path bits // are child models. Stop when we can't find the child model. string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); int i; for (i = 0; i < namePathBits.Length; i++) { IModel localModel = relativeTo.Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType) && m.Enabled); if (localModel == null) { break; } else { relativeTo = localModel as Model; } } // At this point there are only 2 possibilities. We have encountered a // PropertyInfo/MethodInfo or the path is invalid. // We now need to loop through the remaining path bits and keep track of each // section of the path as each section will have to be evaulated everytime a // a get is done for this path. // The variable 'i' will point to the name path that cannot be found as a model. object relativeToObject = relativeTo; List <IVariable> properties = new List <IVariable>(); properties.Add(new VariableObject(relativeTo)); for (int j = i; j < namePathBits.Length; j++) { // look for an array specifier e.g. sw[2] string arraySpecifier = null; if (namePathBits[j].Contains("[")) { arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']'); } // Look for either a property or a child model. IModel localModel = null; if (relativeToObject == null) { return(null); } // Check property info PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]); if (propertyInfo == null && ignoreCase) // If not found, try using a case-insensitive search { propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j], BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase); } // If not property info // Check method info MethodInfo methodInfo = null; List <object> argumentsList = null; if (propertyInfo == null) { if (namePathBits[j].IndexOf('(') > 0) { relativeToObject.GetType().GetMethod(namePathBits[j].Substring(0, namePathBits[j].IndexOf('('))); if (methodInfo == null && ignoreCase) // If not found, try using a case-insensitive search { methodInfo = relativeToObject.GetType().GetMethod(namePathBits[j].Substring(0, namePathBits[j].IndexOf('(')), BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase); } } if (methodInfo != null) { // get arguments and store in VariableMethod string args = namePathBits[j].Substring(namePathBits[j].IndexOf('(')); args = args.Substring(0, args.IndexOf(')')); args = args.Replace("(", "").Replace(")", ""); if (args.Length > 0) { argumentsList = new List <object>(); args = args.Trim('(').Trim(')'); var argList = args.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); // get arguments ParameterInfo[] pars = methodInfo.GetParameters(); for (int argid = 0; argid < argList.Length; argid++) { var cleanArg = argList[argid].Trim(' ').Trim(new char[] { '(', ')' }).Trim(' ').Trim('"'); switch (Type.GetTypeCode(pars[argid].ParameterType)) { case TypeCode.Double: argumentsList.Add(Convert.ToDouble(cleanArg, CultureInfo.InvariantCulture)); break; case TypeCode.Int32: argumentsList.Add(Convert.ToInt32(cleanArg, CultureInfo.InvariantCulture)); break; case TypeCode.String: argumentsList.Add(cleanArg); break; default: break; } } } } } //if (relativeToObject is IFunction && namePathBits[j] == "Value()") //{ // MethodInfo method = relativeTo.GetType().GetMethod("Value"); // properties.Add(new VariableMethod(relativeTo, method)); //} // else if (propertyInfo == null && methodInfo == null && relativeToObject is Model) if (propertyInfo == null && methodInfo == null && relativeToObject is Model) { // Not a property, may be an unchecked method or a child model. localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name.Equals(namePathBits[j], compareType)); if (localModel == null) { // Not a model return(null); } else { properties.Add(new VariableObject(localModel)); relativeToObject = localModel; } } else if (propertyInfo != null) { VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier); properties.Add(property); relativeToObject = property.Value; } else if (methodInfo != null) { VariableMethod method = null; if (argumentsList != null) { method = new VariableMethod(relativeToObject, methodInfo, argumentsList.ToArray <object>()); } else { method = new VariableMethod(relativeToObject, methodInfo, null); } // VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier); properties.Add(method); relativeToObject = method.Value; } else if (relativeToObject is IList) { // Special case: we are trying to get a property of an array(IList). In this case // we want to return the property value for all items in the array. VariableProperty property = new VariableProperty(relativeToObject, namePathBits[j]); properties.Add(property); relativeToObject = property.Value; } else { return(null); } } // We now have a list of IVariable instances that can be evaluated to // produce a return value for the given path. Wrap the list into an IVariable. returnVariable = new VariableComposite(namePath, properties); } // Add variable to cache. cache.Add(cacheKey, returnVariable); return(returnVariable); }
/// <summary>Run the simulation. Will throw on error.</summary> /// <param name="sender"></param> /// <param name="e"></param> /// <exception cref="System.Exception"> /// </exception> public void Run(object sender, System.ComponentModel.DoWorkEventArgs e) { try { StartRun(); DoRun(sender); CleanupRun(null); } catch (ApsimXException err) { DateTime errorDate = Clock.Today; string Msg = "ERROR in file: " + FileName + "\r\n" + "Simulation name: " + Name + "\r\n"; Msg += err.Message; if (err.InnerException != null) { Msg += "\r\n" + err.InnerException.Message + "\r\n" + err.InnerException.StackTrace; } else { Msg += "\r\n" + err.StackTrace; } string modelFullPath = string.Empty; if (err.model != null) { modelFullPath = Apsim.FullPath(err.model); } ErrorMessage = Msg; ISummary summary = Apsim.Find(this, typeof(Summary)) as ISummary; summary.WriteMessage(this, Msg); CleanupRun(Msg); throw new Exception(Msg); } catch (Exception err) { DateTime errorDate = Clock.Today; string Msg = "ERROR in file: " + FileName + "\r\n" + "Simulation name: " + Name + "\r\n"; Msg += err.Message; if (err.InnerException != null) { Msg += "\r\n" + err.InnerException.Message + "\r\n" + err.InnerException.StackTrace; } else { Msg += "\r\n" + err.StackTrace; } ErrorMessage = Msg; ISummary summary = Apsim.Find(this, typeof(Summary)) as ISummary; summary.WriteMessage(this, Msg); CleanupRun(Msg); throw new Exception(Msg); } if (e != null) { e.Result = this; } }