Dec. 5th Press Release : Sage Open-source Release

The Sage Tutorial for components takes the form of sample code to read and run, with an explanation of what the code is doing, and why the developer might be interested in this functionality. Please see the Solution "Sage4 - Sample Code", available with the Sage libraries for the code that runs these demos.
You should also download or open the help file, and follow along there as well.
The demos are divided into functionalities such as the Executive, State Management, and Resources, with sub-functionalities under each, such as with the executive, synchronous and asynchronous events. The demos are all named and have a provided description such as HelloWorld or TaskGraphDemo.

NOTE: The tutorials are an evolving work - if there are features or components you'd like to see tutorials on, please contact us.

Executive

SynchronousEvents

HelloWorld

This demo simply creates an executive, adds an event to it, to be fired at a specific simulation time, and runs the executive, firing the event at the requested time.

TwoCallbacksOutOfSequence

This demo creates an executive, adds two events to it to be fired at specified simulation times that are in the opposite order to their having been added to the executive, and then runs the executive. It demonstrates the time- ordered nature of callback execution.

CallbacksWithPriorities

This demo creates an executive, adds two events to it, to be fired at the same simulation time, but with different priorities that are in the opposite order to their having been added to the executive, and then runs the executive. It demonstrates the priority-ordered nature of callback execution.

UserData_FollowOn_SelfImposedDelay

This demo creates an executive and submits an event to it at a specified time. User data for that event is a queue of two strings, "Hello" and "World." Upon execution of that event, the handler method looks for a word in the user data object, and if the queue is not empty, prints the word, requests a future event, and provides the (now shorter) queue of words to it as user data. The executive keeps calling the event until the queue is empty, at which time the handler does not request another event be served.

This demo shows the use of the UserData parameter to the RequestEvent method, and also shows an event handler requesting a further, future, event service.

ExecCatchesRuntimeExceptionFromSynchronousEvent

This demo creates an executive and submits an event to it and runs the executive. In the service of the event, an InvalidCastException is fired. The executive runs, and catches and stores the exception for post-execution analysis.

RescindingSynchEvent

This demo creates an executive and submits an event to it ("WriteIt()"). However, it also submits another event, to be serviced five minutes prior to the "WriteIt()" event ("RescindIt()") and runs the executive. In the service of the earlier event, the later event is rescinded, and therefore is never serviced.

MoreRescindingPlusAgentBased

This demo creates an executive and a number of domain agents, rastro, (a dog) and fifteen dog agents and fifteen cat agents. Then, for each agent, an event is created which will cause them to speak at a specified time. These are ten minutes apart. This would make for 50 minutes of speaking agents, except that two more events are requested - one, 45 minutes before the end of the 50 minutes, whose effect is to rescind all Speak events for rastro, and another, 35 minutes before the end of the 50 minutes, whose effect is to rescind all events targeted to objects of type "Cat".

This demonstrates some more advanced capabilities of event rescinding.

DetachableEvents

BasicWithSuspends

This demo creates an executive and submits a synchronous event ("WriteIt()") for service at a specified time. It also submits a detachable event ("DoSomething()") that is to be served beforehand. Then the executive is started. "DoSomething()" is called, begins running and then suspends itself. Before it resumes, "WriteIt()" is called, and runs. Then, the "DoSomething()" event resumes and completes.

This demonstrates how two things may be in process at the same time.

SuspendsWithMixedModeAgents

This demo creates an executive and defines two agent types, plumber and electrician. A detachable event request is submitted for a specified time for a specific plumber to come fix a sink. The executive is started, and at the specified time, the plumber announces that he is starting to fix the sink, and works on it for ten minutes. At that time, he discovers that the disposal needs rewiring, so he calls a plumber, and suspends his work.

The electrician, when he completes his work, will tell the plumber to resume. Note that FixTheSink is a complex and time-consuming activity, but it is all described in one method. For the electrician, rewiring the disposal will take 45 minutes.

Besides demonstrating cooperating agents, this demonstrates one agent calling a suspension to its activity, and relying on another agent to resume it.

UsesJoining

This demo takes a questionable approach to making dinner. It creates an executive, and submits a call to "CookDinner" at a specified time, and starts the executive. In servicing the "CookDinner" event, three events are requested, "MakeTurkey", "MakeGravy", and "MakeStuffing" - all three to start immediately, and take different amounts of time. The call to exec.Join(...) suspends execution on this thread until all three activities have completed, and then resumes, announcing dinner.

RescindMultipleDetachables

This demo creates an executive and a number of domain agents, rastro, (a specific dog) and fifteen dog agents and fifteen cat agents. Then, for each agent, an event is created which will cause them to speak at a specified time. These are ten minutes apart. This would make for 50 minutes of speaking agents, except that two more events are requested - one, 35 minutes before the end of the 50 minutes, whose effect is to rescind all remaining Speak events for rastro, and another, 25 minutes before the end of the 50 minutes, whose effect is to rescind all remaining events targeted to objects of type "Cat".

This demonstrates some more advanced capabilities of event rescinding, and is identical to the demo shown before, except in that it is executed on detachable events.

AdvancedTopics

Metronomes

A metronome is useful when one or more elements of a simulation are to be called at a fixed periodicity. This demo creates an executive and adds a metronome to it with a period of 9000 minutes. Every 9000 minutes, the "TickEvent" event fires, until the specified end time.

PauseAndResume

This demo exhibits the pause-and-resume behavior of the executive that could, for example, be tied to a button-press in the GUI.

UseExecController

This demo shows the capabilities of the ExecController, which can be used to drive scaled-rate animation. The ExecController's two relevant properties to this are the "Scale" and the "FrameRate" properties. "Scale" determines the optimal rate at which the simulation clock runs, relative to the wall clock, and "Frame Rate" determines how often the "Render" event fires. We set up an executive to run for ten minutes' simulation time, paced to run at ten times the wall clock rate, that is, for one minute of wall clock time. The model updates its internal state every five milliseconds of simulation time and fires a "Render" event ten times per second of wall-clock time.

ExecEventModelAndStates

This demo shows how, if one wants to run the same simulation multiple times, with a reset and restart each time, the "ExecutiveStarted_SingleShot" event can be used to implement initial setup, and the "ExecutiveStarted" event can be used to perform subsequent runs' initializations.

DaemonEvents

Normally, the executive runs until all registered events have been served or an explicitly-supplied completion time is reached. This demo describes "Daemon Event" registrations, through which an event can be requested for service at a future time, but unlike a standard non-daemon event, does not serve to keep the simulation alive by virtue of its existence.

StateManagement

InAgents

This demonstration shows the maintenance of simulation state in agents, in this case, the agent is a tank, and the state it maintains is the position of a fill valve, expressed in fill rate, the level and capacity of the tank, and management of an event that will notify it when it is full. This demo also shows lazy evaluation of the Level parameter, as well as rescinding the TankFull event if some part of its state (such as valve position) changes.

We start with the tank 10% full and the inlet closed. After a minute, the inlet is opened, and the tank starts to fill, registering an event for its expected time of being full. After a while, we close the fill valve, then a little later we reopen it to a lesser fill rate. The tank manages its level and its events to keep its state correct from the perspective of external actors.

InUserData

This demonstration shows the maintenance of simulation state in the UserData object that is maintained with each event request. 100 tokens are created with state consisting of a name and a random start time in the first 100 minutes of the simulation, and a processing duration between 0 and 1000 minutes. The event, when served, retrieves the name of the token and the duration of service of the token. It then requests a future event to signify completion of the token's processing, and passes the event registration userData that is the name of the token. When that even is serviced, the token's processing is indicated to have been completed.

OnTheStackFrame

This demonstration shows how, by declaring event handlers as local anonymous methods, the stack frame (i.e. locally declared variables) of the method that requests the events can be used to hold values that will be of interest to all of the handlers.

RandomServer

SimpleDefaultServer

This demonstration shows the default use of a RandomServer. It obtains, for each of five test case seed-and-buffer-size combinations, an instance of IRandomChannel from the GlobalRandomServer, and outputs the first ten values from that random channel. The demonstration calls "NextDouble()", but there are a number of other familiar methods for getting randoms as well.

DecorrellatedActivities

This demonstration shows the random server's ability to maintain two separate streams of random numbers that do not affect each other. The demo uses two instances of class "Activity" as agents in the simulation. The demo occurs in three parts.

In the first part, we create a random server with one hyperseed, hs1, and then create two activities with different seeds, s1 and s2. The activities generate different outputs from each other.

In the second part, we create a random server with a second hyperseed, hs2, and then create two activities with the same seeds, s1 and s2, as before. The activities generate different outputs from each other, and from their outputs in the first part, demonstrating that a model can have fixed seeds for elements within it, and achieve variability by altering the hyperseed only.

In the third part, we essentially replicate the second part, but this time, we impose a change of behavior on the second instance of "Activity." The behavior of the first instance of "Activity" remains identical to its behavior in the second part of the demo, illustrating the decorrellated nature of random channels. They can be used to hold one part of a simulation constant and allow another to change.

StateMachine

Basic

Default

This demonstration shows the default configuration of a model state machine. It's pretty simple - the model is either idle, or running. If it's idle, calling "Start()" transitions it to running, in which state the executive processes all of its events. After running, it returns to idle.

SimpleCustomWithInitialization

This demonstration shows a simple custom configuration of a model state machine. We will add a state, "Initialize" that, upon invocation of the model's "Start()" method, performs setup.

SimpleEnumStateMachine

This demo shows the utility of a different kind of state machine. It is not usable to control model state, but is demonstrated here to hopefully avoid confusion as to its use. It manages the state of Model Objects (such as agents) and tracks the time each agent spends in each state. Again, this conceptually-different state machine is demonstrated here to try to avoid confusion.

Model

DefaultModel

This demonstration shows the default configuration of a model. It's pretty simple - no services are provided, and the model is either idle, or running. If it's idle, calling "Start()" transitions it to running, in which state the executive processes all of its events. After running, it returns to idle.

SimpleCustomWithInitialization

This demonstration shows a simple custom configuration of a model state machine. We will add a state, "Initialize" that, upon invocation of the model's "Start()" method, performs setup.

Resources

ServicePoolExample

This demo shows how a bank teller simulation might treat tellers as a resource. A customer generation method registers events to creates customers with a certain interarrival time. The customer is created on its own thread (see the ExecEventType.Detachable parameter to RequestEvent) and immediately requests a teller from the teller pool. If a teller is unavailable, the thread blocks, awaiting an available teller. When the teller is granted, it declares itself busy, services the customer for the next random service time, and declares itself idle. When the service is complete, the customer releases the resource, and is finished.

Note that we show the use of setting the start time in the executive, and make use of the EnumStateMachine to track states for tellers. Also, we do not make Customers implement IModelObject. No one will need to find them, a unique Guid is unnecessary, and they do not need names. Tellers derive from Resource, which is an IModelObject.

This demo declares the following classes: DemoModel : Simple Model class without services. Creates and populates TellerPool, and registers events to create customers. Customer : On creation, acquires teller, calls teller.Service(this) and subsequently releases the teller. Teller : Manages a state machine to track its utilization fraction, and services the customer, with a state change before and after. TellerState : An enum, Idle and Busy. TellerPool : A plain old Highpoint.Sage.Resources.ResourceManager. That's it. TellerRequest : A resource request that asks for one teller, any teller.

ServicePoolExampleWithSynchronousEvents

This demo is identical to the ServicePoolExample demo, except that rather than using Detachable events, it accomplishes the same scenario with Synchronous events. The difference between the two approaches is that the former makes model code much simpler, and logic easier to follow, but at the expense of possibly maintaining a larger number of threads (one per running-or-paused entity.) Note that only one thread actually runs at a time, with each thread explicitly yielding or being resumed, so the typical sort of non-deterministic issues of multithreading do not apply here.

Advanced

OptimalResourceAcquisition

This demo shows how a bank teller simulation might treat tellers as a resource. A customer generation method registers events to creates customers with a certain interarrival time. The customer is created on its own thread (see the ExecEventType.Detachable parameter to RequestEvent) and immediately requests a teller from the teller pool. If a teller is unavailable, the thread blocks, awaiting an available teller. When the teller is granted, it declares itself busy, services the customer for the next random service time, and declares itself idle. When the service is complete, the customer releases the resource, and is finished.

Note that we show the use of setting the start time in the executive, and make use of the EnumStateMachine to track states for tellers. Also, we do not make Customers implement IModelObject. No one will need to find them, a unique Guid is unnecessary, and they do not need names. Tellers derive from Resource, which is an IModelObject.

This demo declares the following classes: DemoModel : Simple Model class without services. Creates and populates TellerPool, and registers events to create customers. Customer : On creation, acquires teller, calls teller.Service(this) and subsequently releases the teller. Teller : Manages a state machine to track its utilization fraction, and services the customer, with a state change before and after. TellerState : An enum, Idle and Busy. TellerPool : A plain old Highpoint.Sage.Resources.ResourceManager. That's it. TellerRequest : A resource request that asks for one teller, any teller.

SequenceControl

Basic

TaskGraphDemo

This demo shows the modeling of a hierarchical process, in this case, that of making brownies. There are tasks that can happen in parallel, and tasks that must happen serially. The top level task is "Make Brownies" and it has three subtasks, "Prepare Oven", "Prepare Pan", and "Assemble Brownies". Under "Prepare Pan", there are two serial tasks, "Acquire Pan", and "Grease Pan". Likewise, "Assemble Brownies" has serial subtasks, as does "Bake Brownies". We show how to explicitly establish a successor/follower role between "Assemble Brownies" and "Bake Brownies," and we can see that reflected in the output.

The task graph runs on Synchronous events, and each task signals its completion when finished.

There are many more capabilities of the Task Graph construct, including hierarchical composite validity management (if there's no grease, you can't prepare the pan, and therefore you can't make the brownies), post-mortem analysis (how long did we spend greasing the pan?), and simultaneous multiple instance execution (let's make three batches of brownies.)