How-tos Overview
Here are some how-tos that, due to the current lack of a more complete manual, will hopefully provide help building React.NET simulations.
Simulation
Tasks & Processes
- Start a task running
- Immediately cancel a running task
- Schedule a task cancellation at some future time
- Have one task block (wait) on another task
- Re-activate one blocked task
- Re-activate all blocked tasks
- Create a process using delegation
- Create a process using derivation
Resources & Consumables
- Create an anonymous resource
- Create a tracked resource
- Obtain the number of free and in-use items
- Change the capacity of a resource
- Acquire a resource item from a resource pool
- Release a resource item back into its resource pool
- Obtain the tracked resource item acquired (allocated)
- Create a consumable
- Add items/units to a consumable
Random Numbers
- Create a uniformly distributed random number generator
- Configure a non-uniform variate to use a user-supplied uniform generator
Developer
Simulation
1. Create a Simulation
Creating a simulation involves two steps: (1) first you must instantiate a Simulation instance; and (2) you must supply the newly created Simulation with one or more generator tasks. Instantiating the simulation is simple...
Simulation sim = new Simulation();
Creating the generator tasks may be somewhat more involved as you need
to write some code to get the generators tasks to actually do something
when they're executed by the simulation. Once you have such a task,
it may be activated prior to calling the Simulation.Run
method or it may be passed to the Run
method.
Method 1
Activate a single generator task of type MyTask
prior to calling the Simulation.Run()
method.
Task task = new MyTask(sim); task.Activate(); sim.Run();
Method 2
Pass a single generator task of type MyTask
to the Simulation.Run(Task)
method.
Task task = new MyTask(sim); sim.Run(task);
Method 3
Pass a multiple generator tasks of type MyTaskA
and MyTaskB to the Simulation.Run(Task[])
method.
Task [] tasks = new Task [2]; tasks[0] = MyTaskA(sim); tasks[1] = MyTaskB(sim); sim.Run(tasks);
Note that Method 1 may be used in conjunction with Method 2 or Method 3. Method 2 and Method 3 cannot be used to run the same simulation.
2. Stop a Simulation
There are a few different ways to stop a running simulation. It mainly depends upon whether: you want to stop the simulation immediately; or want to have to option to cancel the stoppage if it's scheduled to occur at some time in the future.
If you don't need to be able to cancel the stoppage, the easiest way
to stop the simulation is to use one of the Stop
methods
of the Simulation class.
public void Stop(); public virtual void Stop(long absTime);
The first version, Stop()
simply stops the simulation
immediately. The second version stops the simulation at some
absolute simulation time. The first version simply calls
the second version with an argument of Now
(the current
simulation time).
Once a stop time is set by calling Stop
is possible to
issue a different stop time, but not to recind the stoppage alltogether.
If you need to be able to cancel a stoppage, you can use a
StopSimulation task.
Simulation sim = /* get the running Simulation instance. */ long stopTime = sim.Now + 1000L; Task stopTask = new React.Tasking.StopSimulation(sim); stopTask.Activate(stopTime);
To cancel stopTask
simply invoke its Cancel()
method prior to its execution.
stopTask.Cancel();
Tasks & Processes
IMPORTANT: Always bear in mind that a Process is a Task. Most of the tips below that apply to Task instances apply equally to Process instances.
1. Start a task running
To start a task running, it must be activated. The Task class contains a number of methods to activate an instance.
public void Activate(object activator); public void Activate(object activator, long relTime); public void Activate(object activator, long relTime, int priority); public void Activate(object activator, long relTime, object data); public virtual void Activate(object activator, long relTime, object data, int priority);
The meaning of the parameters are identical for each version of
the Activate
method. All versions delegate to the
final version (the one marked virtual
).
- activator
-
The object that is activating the Task. This
may be
null
in which case the activation is said to be anonymous. - relTime
- The time, relative to the current simulation time, when the Task should be activated.
- data
-
Client data to pass to the Task when it is
activated. This object is available to Process
instances as the
ActivationData
property. It is available to Task instances by querying the triggering ActivationEvent. - priority
- The priority of the Task. This value is used to order task execution when two or more tasks occur at the exact same time. Higher values indicate higher priority. The default task priority is zero (0).
2. Immediately cancel a running task
If a Task is scheduled, that is, it has been
activated using one of the methods described previously, it may
be cancelled by invoking its Cancel()
method.
task.Cancel();
3. Schedule a task cancellation at some future time
A Task can be cancelled at some future time in the running Simulation by using a CancellationTask.
Task target = /* get the Task to cancel */ Task cancelTask = new React.Tasking.CancellationTask(sim, target); cancelTask.Activate(null, 5000L);
The above would cancel target
in 5000 time units from the
current simulation time.
Cancellation in this manner is one way to implement reneging; however using an interrupt is probably a better approach.
4. Have one task block (wait) on another task
One Task can be made to block on another using one of two techniques (Process instance offer one addition method). The technique to choose depends upon whether or not you want to immediately activate the blocking task.
In the following examples, let blocker be the blocking Task and waiting be the Task being blocked (i.e. that task that's waiting on blocker).
Block with immediate activation of blocker
If you want to activate blocker immediately, call
waiting's WaitOnTask
method.
waiting.WaitOnTask(blocker);
The task, waiting will block on blocker and blocker will be activated.
Block with deferred activation of blocker
If you want to defer activation of blocker use the
Block(Task)
method of blocker.
blocker.Activate(null, 500); blocker.Block(waiting);
In the above example, blocker will activate 500 time units from the current time. It also is blocking the task, waiting.
Have a process block on a task
Process instances provide another means of blocking
on other Tasks. They simply need to return the
blocking Task from the GetProcessSteps
method. Remember, GetProcessSteps
is an iterator method so
you must to a yield return
.
// Block on the task 'blocker' yield return blocker;
Note that blocker is activated immediately.
5. Re-activate one blocked task
A Task that is blocking one or more
Tasks can re-activate one of the
blocked Tasks by invoking the
ResumeNext
method. There are three versions
of ResumeNext
available.
// Resume the next blocked task. The blocked task is re-activated with the // blocking task as its activator. ResumeNext(); // Resume the next blocked task specifying some activation data. The blocked // task is re-activated with the blocking task as its activator. ResumeNext(thing); // Resume the next blocked task specifiying both the activator and activation // data. ResumeNext(activator, thing);
6. Re-activate all blocked tasks
A Task that is blocking one or more
Tasks can re-activate all of the
blocked Tasks by invoking the
ResumeAll
method. There are three versions
of ResumeAll
available. The arguments to
ResumeAll
are used identically to those of
ResumeNext
(see previous how-to).
7. Create a process using delegation
A Process can be created using a
ProcessSteps
delegate defined as follows
public delegate IEnumerator<Task> ProcessSteps(Process process, object data);
To create a Process in this manner, you must
first write the method to be used by the ProcessSteps
delegate. Then pass the delegate to the appropriate constructor.
// The method usable as a ProcessSteps delegate. private IEnumerator<Task> DoProcess(Process process, object data) { // ... do your processing ... // Doesn't hurt to always include this at the end. yield break; } public static void Main(string [] args) { Thing thing = new Thing(); Simulation sim = new Simulation(); // Create a new Process using the delegate method. Process p1 = new Process(sim, DoProcess); // Create a new Process using the delegate method and passing it // some initial data. Process p2 = new Process(sim, DoProcess, thing); // Run the simulation. p1 and p2 are the generator tasks. sim.Run(new Task[] {p1, p2}); }
8. Create a process using derivation
To create a Process using derivation,
you must override the GetProcessSteps
method.
public class MyProcess : Process { public MyProcess(Simulation sim) : base(sim) {} protected override IEnumerator<Task>GetProcessSteps() { . . . yield break; } }
When creating a new class this way, you do not need to invoke
the base class version of GetProcessSteps
.
Resources & Consumables
1. Create an anonymous resource
An anonymous resource, implemented by the AnonymousResource class, can be created by directly instantiating an AnonymousResource instance or by using a Resource factory method. Using direct instantiation allows you to supply a name for the resource at the time of its creation.
Direct Instantiation
Create a new AnonymousResource instance by using one of the three available constructors.
// Create with an capacity of one (1). IResource r1 = new AnonymousResource(); // Create with the specified capacity (e.g. 5). IResource r2 = new AnonymousResource(5); // Create with specified name and capacity. IResource r3 = new AnonymousResource("Printers", 4);
Factory Method
Create a new AnonymousResource instance by
using the Resource.Create(int)
or
Resource.Create(int,int)
factory methods.
// Create an AnonymousResource with a capacity of 10. IResource r4 = Resource.Create(10); // Create an AnonymousResource with a capacity of 10, 4 of which are // out of service. IResource r5 = Resource.Create(6, 4);
In the first Create
call, all ten resource items
are in-service. In the second Create
call, six items are in-service and four items are out-of-service.
Both result in an AnonymousResource with
a capacity of ten (e.g. the resource's Count
property
equals 10).
2. Create a tracked resource
A tracked resource, implemented by the TrackedResource class, can be created by directly instantiating an TrackedResource instance or by using a Resource factory method. Using direct instantiation allows you to supply a name for the resource at the time of its creation.
Direct Instantiation
Create a new TrackedResource instance by using one of the two available constructors.
// Load an IEnumerable with the objects the TrackedResource will contain. // In this example a simple array is used. object [] things = new object [5]; things[0] = new Thing(); things[1] = new Thing(); . . . // Create an unnamed TrackedResource. IResource r1 = new TrackedResource(things); // Create an named TrackedResource. IResource r2 = new TrackedResource("Stuff", things);
Note that while the above example shows the objects in the things
array being placed into two different TrackedResource
instance, this is not a good idea and should be avoided. An object
should never be contained by more than one TrackedResource.
Factory Method
Create a new TrackedResource instance by
using the Resource.Create(IEnumerable)
factory method.
// Create and load an IEnumerable... IList list = new ArrayList(); list.add(new Thing()); list.add(new Thing()); // Create an TrackedResource containing the elements of 'list'. IResource r3 = Resource.Create(list);
All the resource items in a newly created TrackedResource are in-service.
3. Obtain the number of free and in-use items
To query an IResource for the number
of items that are free and the number of items that are
in-use, query the Free
and InUse
properties.
// Get the number of free (available) items in resource 'res'. int nFree = res.Free; // Get the number of in-use items in resource 'res'. int nUsed = res.InUse;
4. Change the capacity of a resource
Once created, it's actually not possible to change the capacity
of a resource. That is, the Count
property
remains fixed. It is, however, possible to alter the number of
resource items are are out-of-service. An item that is
out-of-service is considered unusable and therefore cannot be made
available by the IResource.
To change the number of out-of-service items simply change the
OutOfService
property.
// Decrease the number of in-service items in the resource 'res' by two. res.OutOfService += 2;
One major implication of the above is that you must remember to create your resources with a capacity that will accomodate the maximum desired in-service count.
5. Acquire a resource item from a resource pool
A Process can acquire a resource item
from a IResource using the Acquire
method.
IResource res = /* get the resource you want to acquire from */ yield return res.Acquire(this); // When the process resumes, we have the resource item.
Note that this example applies only to Process
instances. The above code is only valid in an overridden
GetProcessSteps
method or a ProcessSteps
delegate method.
6. Release a resource item back into its resource pool
A Process can release a previously acquired resource
item to a IResource using the Release
method.
IResource res = /* get the resource you want to release to */ yield return res.Release(this); // When the process resumes, we will have released the resource item.
Note that this example applies only to Process
instances. The above code is only valid in an overridden
GetProcessSteps
method or a ProcessSteps
delegate method.
7. Obtain the tracked resource item acquired (allocated)
When using a TrackedResource the
acquiring Task is passed the allocated
resource item as the data
parameter of the
ExecuteTask
method.
For Process instances, obtaining the
allocated resource item is a bit different because normally,
you are not overriding ExecuteTask
. In this case,
the resource item is available in the ActivationData
property.
8. Create a consumable
Create a new Consumable instance using one of four constructors. Two of the constructor create an empty consumable, while the other two create a consumable with a specified initial quantity of consumable items (units).
Create an empty consumable
The following two constructors create empty Consumable instances.
// Create an unnamed, empty consumable. IConsumable c1 = new Consumable(); // Create a named, empty consumable. IConsumable c2 = new Consumable("Fuel Tank");
Create a consumable with an initial quantity
The following two constructors create Consumable instances having the specified initial quantity of consumable items or units.
// Create an unnamed consumable with initial quantity of 2500 items/units. IConsumable c3 = new Consumable(2500); // Create an named consumable with initial quantity of 500 items/units. IConsumable c4 = new Consumable("Water Tank", 500);
9. Add items/units to a consumable
Adding items or units to a IConsumable
is called re-supplying. After it's initial creation
a consumable can only be re-supplied during the simulation run.
Re-supplying is accomplished by the Task
returned by the Resupply(Task task, int quantity)
blocking method of IConsumable.
The example code shown below illustrates performing a re-supply from a Process.
protected override IEnumerator<Task> GetProcessSteps() { // ... perform some processing ... // Get ahold of an IConsumable instance IConsumable c = GetWaterTank(); // Now resupply it with 1000 items/units. yield return c.Resupply(this, 1000); // ... perform some more processing ... }
Random Numbers
1. Create a uniformly distributed random number generator
The React.Distribution.Uniform class includes two static methods that return a new Uniform instance.
public static Uniform Create(); public static Uniform Create(long seed);
In the first case, the resulting Uniform is seeded using the current system time. In the second case, the seed must be specified.
Both of the above Create
methods, return a
Uniform derivitive that uses
System.Random to do the actual random number
generation.
2. Configure a non-uniform variate to use a user-supplied uniform generator
Non-uniform random number generators or non-uniform variates can be created to use a uniform generator from the system-wide set of uniform random number streams or they can use a user-supplied random number generator.
All the React.NET non-uniform variates have constructors that take a IUniformSource interface. To specify the IUniformSource for non-uniform variate use one of the constructors that accept a IUniformSource parameter. For example, to create a Weibull instance that uses its own uniform generator, you can do the following.
// Create a new IUniformSource instance seeded from the system time. IUniformSource rngSource = Uniform.Create(); // Create a new Weibull instance that obtains a uniform generator from rngSource. Weibull weibull = new Weibull(rngSource);
Note that in the example above, weibull
will use the
Weibull class's default shape and scale.
Developer
1. Check out the latest sources
React.NET is currently developed using the Subversion version control system. SourceForge began supporting Subversion in February 2006. It is possible to check out the React.NET source code as follows (if you're using the Subversion command-line client).
svn co https://svn.sourceforge.net/svnroot/reactnet/trunk reactnet
The above will check out the main development line (the trunk) into a directory named reactnet.
For more infomation on using Subversion with SourceForge, see the documents