public static ScriptingHost MakeBaseScriptingHost(int?seed = null) { ScriptingHost scriptingHost = new ScriptingHost(); scriptingHost.LoadLibrary(SystemBool); scriptingHost.LoadLibrary(SystemInt); scriptingHost.LoadLibrary(SystemDouble); scriptingHost.LoadLibrary(SystemString); scriptingHost.LoadLibrary(StringLibrary); scriptingHost.LoadLibrary(SystemMath); scriptingHost.LoadLibrary(RandomVariateLibrary(seed)); return(scriptingHost); }
private IReadOnlyList <VariableValue> EvaluateParameters(IReadOnlyList <string> parameterExpressions) { if (parameterExpressions is not null && parameterExpressions.Count > 0) { List <VariableValue> values = new List <VariableValue>(parameterExpressions.Count); for (int i = 0; i < parameterExpressions.Count; i++) { values.Add(ScriptingHost.Evaluate(parameterExpressions[i])); } return(values); } return(null); }
private static void WriteGraphCode(StringBuilder sb, Graph graph, IEnumerable <string> traceExpressions, ref int indent) { StartBlock(sb, "enum EventType", ref indent); var eventNames = new Dictionary <Vertex, string>(); var eventParameterTypes = new Dictionary <Vertex, string>(); for (int i = 0; i < graph.Verticies.Count; i++) { Vertex vertex = graph.Verticies[i]; int paramCount = vertex.ParameterNames.Count; if (paramCount > 0 && !eventParameterTypes.ContainsKey(vertex)) { StringBuilder tupleTypesSB = new StringBuilder(); tupleTypesSB.Append("Tuple<"); for (int j = 0; j < paramCount; j++) { if (j > 0) { tupleTypesSB.Append(", "); } tupleTypesSB.Append(GetStateVariableType(graph.GetStateVariable(vertex.ParameterNames[i]))); } tupleTypesSB.Append('>'); eventParameterTypes[vertex] = tupleTypesSB.ToString(); } if (!eventNames.TryGetValue(vertex, out string eventName)) { if (!ScriptingHost.TrySymbolify(vertex.Name, false, out string result)) { result = i.ToString(); } eventName = $"{ EventNamePrefix }{ result }"; eventNames[vertex] = eventName; } WriteCode(sb, $"{ eventName } = { i },", ref indent); } EndBlock(sb, ref indent); // enum EventType sb.AppendLine(); StartBlock(sb, "class Simulation : SimulationBase", ref indent); Vertex startingVertex = graph.StartingVertex; WriteCode(sb, $"protected override EventType StartingEventType => EventType.{ eventNames[startingVertex] };", ref indent); if (graph.StateVariables.Count > 0) { sb.AppendLine(); WriteComment(sb, "State Variables", ref indent); for (int i = 0; i < graph.StateVariables.Count; i++) { StateVariable stateVariable = graph.StateVariables[i]; WriteCode(sb, $"{ GetStateVariableType(stateVariable) } { RewriteExpression(graph, stateVariable.Name) } = default;", ref indent); } } sb.AppendLine(); WriteCode(sb, "public Simulation() { }", ref indent); if (startingVertex.ParameterNames.Count > 0) { sb.AppendLine(); StringBuilder tupleValuesSB = new StringBuilder(); for (int i = 0; i < startingVertex.ParameterNames.Count; i++) { if (i > 0) { tupleValuesSB.Append(", "); } string type = GetStateVariableType(graph.GetStateVariable(startingVertex.ParameterNames[i])); switch (type) { case "bool": case "int": case "double": tupleValuesSB.Append($"{ type }.Parse(startParameters[{ i }])"); break; default: tupleValuesSB.Append($"startParameters[{ i }]"); break; } } WriteCode(sb, $"protected override object ParseStartParameters(string[] startParameters) => Tuple.Create({ tupleValuesSB});", ref indent); } sb.AppendLine(); StartBlock(sb, "protected override void ProcessEvent(EventType eventType, object parameterValues)", ref indent); StartBlock(sb, "switch (eventType)", ref indent); for (int i = 0; i < graph.Verticies.Count; i++) { Vertex vertex = graph.Verticies[i]; StartBlock(sb, $"case EventType.{ eventNames[vertex] }:", ref indent, false); WriteCode(sb, $"{ eventNames[vertex] }({ (vertex.ParameterNames.Count > 0 ? $"({ eventParameterTypes[vertex] })parameterValues" : "") });", ref indent); WriteCode(sb, "break;", ref indent); EndBlock(sb, ref indent, false); // case } EndBlock(sb, ref indent); // switch EndBlock(sb, ref indent); // protected override void ProcessEvent for (int i = 0; i < graph.Verticies.Count; i++) { Vertex vertex = graph.Verticies[i]; sb.AppendLine(); WriteComment(sb, $"Event #{ i }: { vertex.Name }", ref indent); if (!string.IsNullOrWhiteSpace(vertex.Description)) { WriteComment(sb, $"Description: { vertex.Description }", ref indent); } int paramCount = vertex.ParameterNames.Count; StartBlock(sb, $"private void { eventNames[vertex] }({ (paramCount > 0 ? $"{ eventParameterTypes[vertex] } parameterValues" : "") })", ref indent); bool addSpacing = false; if (paramCount > 0) { WriteComment(sb, "Parameters", ref indent); for (int j = 0; j < paramCount; j++) { WriteCode(sb, $"{ RewriteExpression(graph, vertex.ParameterNames[j]) } = parameterValues.Item{ j + 1 };", ref indent); } addSpacing = true; } string[] code = vertex.Code; if (code is not null && code.Length > 0) { if (addSpacing) { sb.AppendLine(); } WriteComment(sb, "Event Code", ref indent); for (int j = 0; j < code.Length; j++) { WriteCode(sb, $"{ RewriteExpression(graph, code[j]) };", ref indent); } addSpacing = true; } for (int j = 0; j < graph.Edges.Count; j++) { Edge edge = graph.Edges[j]; if (vertex == edge.Source) { if (addSpacing) { sb.AppendLine(); WriteComment(sb, $"Edge #{ j }: { edge.Action} { edge.Source.Name } to { edge.Target.Name }", ref indent); if (!string.IsNullOrWhiteSpace(edge.Description)) { WriteComment(sb, $"Description: { edge.Description }", ref indent); } } bool hasCondition = !string.IsNullOrEmpty(edge.Condition); if (hasCondition) { StartBlock(sb, $"if ({ RewriteExpression(graph, edge.Condition) })", ref indent); } StringBuilder parameterValuesSB = new StringBuilder(); if (edge.ParameterExpressions.Count == 0) { parameterValuesSB.Append("null"); } else { parameterValuesSB.Append($"new { eventParameterTypes[vertex] }("); for (int k = 0; k < edge.ParameterExpressions.Count; k++) { if (k > 0) { parameterValuesSB.Append(", "); } parameterValuesSB.Append(RewriteExpression(graph, edge.ParameterExpressions[k])); } parameterValuesSB.Append(')'); } switch (edge.Action) { case EdgeAction.Schedule: WriteCode(sb, $"ScheduleEvent(EventType.{ eventNames[edge.Target] }, { (string.IsNullOrEmpty(edge.Delay) ? "0" : RewriteExpression(graph, edge.Delay)) }, { (string.IsNullOrEmpty(edge.Priority) ? "0" : RewriteExpression(graph, edge.Priority)) }, { parameterValuesSB});", ref indent); break; case EdgeAction.CancelNext: WriteCode(sb, $"CancelNextEvent(EventType.{ eventNames[edge.Target] }, { parameterValuesSB});", ref indent); break; case EdgeAction.CancelAll: WriteCode(sb, $"CancelAllEvents(EventType.{ eventNames[edge.Target] }, { parameterValuesSB});", ref indent); break; } if (hasCondition) { EndBlock(sb, ref indent); // if condition } addSpacing = true; } } EndBlock(sb, ref indent); // private void Event } sb.AppendLine(); StartBlock(sb, "protected override string GetEventName(EventType eventType)", ref indent); StartBlock(sb, "switch (eventType)", ref indent); for (int i = 0; i < graph.Verticies.Count; i++) { Vertex vertex = graph.Verticies[i]; StartBlock(sb, $"case EventType.{ eventNames[vertex] }:", ref indent, false); WriteCode(sb, $"return \"{ eventNames[vertex].Substring(EventNamePrefix.Length) }\";", ref indent); EndBlock(sb, ref indent, false); // case } EndBlock(sb, ref indent); // switch WriteCode(sb, $"return \"\";", ref indent); EndBlock(sb, ref indent); // protected override string GetEventName sb.AppendLine(); StartBlock(sb, "protected override void TraceExpressionHeaders(bool traceToConsole, StreamWriter outputWriter)", ref indent); if (traceExpressions is not null) { foreach (string traceExpression in traceExpressions) { WriteCode(sb, $"Trace(traceToConsole, outputWriter, \"\\t{ RewriteExpression(graph, traceExpression, false) }\");", ref indent); } } EndBlock(sb, ref indent); // protected override void TraceExpressionHeaders sb.AppendLine(); StartBlock(sb, "protected override void TraceExpressionValues(bool traceToConsole, StreamWriter outputWriter)", ref indent); if (traceExpressions is not null) { foreach (string traceExpression in traceExpressions) { WriteCode(sb, $"Trace(traceToConsole, outputWriter, $\"\\t{{ { RewriteExpression(graph, traceExpression) } }}\");", ref indent); } } EndBlock(sb, ref indent); // protected override void TraceExpressionValues EndBlock(sb, ref indent); // class Simulation }
public static IReadOnlyList <ValidationError> Validate(Graph graph) { if (graph is null) { throw new ArgumentNullException(nameof(graph)); } var scriptingHost = MakeValidationScriptingHost(); List <ValidationError> errors = new List <ValidationError>(); // Verify state variables Dictionary <string, List <StateVariable> > uniqueStateVariableNames = new Dictionary <string, List <StateVariable> >(); HashSet <StateVariable> badStateVariables = new HashSet <StateVariable>(); foreach (StateVariable sv in graph.StateVariables) { // Verify name if (string.IsNullOrWhiteSpace(sv.Name)) { errors.Add(new BlankStateVariableNameValidationError(graph, sv)); badStateVariables.Add(sv); } else { if (!uniqueStateVariableNames.ContainsKey(sv.Name)) { uniqueStateVariableNames[sv.Name] = new List <StateVariable>(); } uniqueStateVariableNames[sv.Name].Add(sv); if (ScriptingHost.ReservedKeywords.Contains(sv.Name)) { errors.Add(new ReservedKeywordStateVariableValidationError(graph, sv)); badStateVariables.Add(sv); } else if (!ScriptingHost.IsValidSymbolName(sv.Name, false)) { errors.Add(new InvalidStateVariableNameValidationError(graph, sv)); badStateVariables.Add(sv); } } } foreach (var kvp in uniqueStateVariableNames) { if (kvp.Value.Count > 1) { errors.Add(new DuplicateStateVariableNamesValidationError(graph, kvp.Value)); foreach (StateVariable sv in kvp.Value) { badStateVariables.Add(sv); } } } foreach (StateVariable sv in graph.StateVariables) { if (!badStateVariables.Contains(sv)) { scriptingHost.Create(sv); } } // Verify verticies List <Vertex> startingVerticies = new List <Vertex>(); Dictionary <string, List <Vertex> > uniqueVertexNames = new Dictionary <string, List <Vertex> >(); foreach (Vertex v in graph.Verticies) { if (v.IsStartingVertex) { startingVerticies.Add(v); } // Verify name if (string.IsNullOrWhiteSpace(v.Name)) { errors.Add(new BlankVertexNameValidationError(graph, v)); } else { if (!uniqueVertexNames.ContainsKey(v.Name)) { uniqueVertexNames[v.Name] = new List <Vertex>(); } uniqueVertexNames[v.Name].Add(v); } // Verify parameters IReadOnlyList <string> parameterNames = v.ParameterNames; if (parameterNames is not null) { foreach (string parameterName in parameterNames) { if (!graph.HasStateVariable(parameterName)) { errors.Add(new InvalidParameterNameVertexValidationError(graph, v, parameterName)); } } } // Verify code string[] code = v.Code; if (code is not null && code.Length > 0) { for (int i = 0; i < code.Length; i++) { try { scriptingHost.Execute(code[i]); } catch (Exception ex) { errors.Add(new InvalidCodeVertexValidationError(graph, v, code[i], ex.Message)); } } } } if (startingVerticies.Count == 0) { errors.Add(new NoStartingVertexValidationError(graph)); } else if (startingVerticies.Count > 1) { errors.Add(new MultipleStartingVertexValidationError(graph, startingVerticies)); } foreach (var kvp in uniqueVertexNames) { if (kvp.Value.Count > 1) { errors.Add(new DuplicateVertexNamesValidationError(graph, kvp.Value)); } } // Verify edges foreach (Edge e in graph.Edges) { bool validVerticies = true; if (e.Source is null) { errors.Add(new SourceMissingEdgeValidationError(graph, e)); validVerticies = false; } if (e.Target is null) { errors.Add(new TargetMissingEdgeValidationError(graph, e)); validVerticies = false; } if (validVerticies) { if (e.Action == EdgeAction.Schedule) { var parameterNames = e.Target.ParameterNames; var parameterExpressions = e.ParameterExpressions; if (parameterNames.Count > 0 && parameterExpressions.Count == 0) { errors.Add(new ParametersRequiredEdgeValidationError(graph, e)); } else if (parameterExpressions.Count != parameterNames.Count) { errors.Add(new InvalidParametersEdgeValidationError(graph, e)); } if (parameterExpressions.Count > 0) { for (int i = 0; i < parameterExpressions.Count; i++) { try { var result = scriptingHost.Evaluate(parameterExpressions[i]); scriptingHost.SetVariable(parameterNames[i], result); } catch (Exception ex) { errors.Add(new InvalidParameterEdgeValidationError(graph, e, parameterExpressions[i], ex.Message)); } } } } // Verify condition try { scriptingHost.Evaluate(e.Condition, VariableValue.True).AsBoolean(); } catch (Exception ex) { errors.Add(new InvalidConditionEdgeValidationError(graph, e, ex.Message)); } // Verify delay try { scriptingHost.Evaluate(e.Delay, new VariableValue(Schedule.DefaultDelay)).AsNumber(); } catch (Exception ex) { errors.Add(new InvalidDelayEdgeValidationError(graph, e, ex.Message)); } // Verify priority try { scriptingHost.Evaluate(e.Priority, new VariableValue(Schedule.DefaultPriority)).AsNumber(); } catch (Exception ex) { errors.Add(new InvalidPriorityEdgeValidationError(graph, e, ex.Message)); } } } return(errors); }
public override void Evaluate(ScriptingHost scriptingHost) { Value = scriptingHost.Get(StateVariable); }
public abstract void Evaluate(ScriptingHost scriptingHost);
private void InternalStep() { // Get next event ScheduledEvent nextEvent = Schedule.GetNext(); OnVertexFiring(nextEvent.Target); // Update internal variables Clock = nextEvent.Time; EventCount[Graph.Verticies.IndexOf(nextEvent.Target)]++; // Assign parameters AssignParameters(nextEvent.Target, nextEvent.ParameterValues); // Execute event ScriptingHost.Execute(nextEvent.Target.Code); // Evaluate trace variables EvaluateTraces(); OnVertexFired(nextEvent.Target); // Evaluate edges for (int i = 0; i < Graph.Edges.Count; i++) { if (Graph.Edges[i].Source == nextEvent.Target) { Edge edge = Graph.Edges[i]; // Check condition if (ScriptingHost.Evaluate(edge.Condition, VariableValue.True).AsBoolean()) { OnEdgeFiring(edge); // Evaluate parameters IReadOnlyList <VariableValue> parameterValues = EvaluateParameters(edge.ParameterExpressions); switch (edge.Action) { case EdgeAction.Schedule: double time = Clock + ScriptingHost.Evaluate(edge.Delay, new VariableValue(Schedule.DefaultDelay)).AsNumber(); double priority = ScriptingHost.Evaluate(edge.Priority, new VariableValue(Schedule.DefaultPriority)).AsNumber(); Schedule.Insert(edge.Target, time, priority, parameterValues); break; case EdgeAction.CancelNext: Schedule.CancelNext(edge.Target, parameterValues); break; case EdgeAction.CancelAll: Schedule.CancelAll(edge.Target, parameterValues); break; } OnEdgeFired(edge); } } } if (!KeepGoing()) { State = SimulationState.Complete; } }