Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        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
        }
Example #4
0
        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);
        }
Example #5
0
 public override void Evaluate(ScriptingHost scriptingHost)
 {
     Value = scriptingHost.Get(StateVariable);
 }
Example #6
0
 public abstract void Evaluate(ScriptingHost scriptingHost);
Example #7
0
        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;
            }
        }