The simulator¶
At the heart of Turberfield is a simulator. It keeps the world ticking along. It looks for messages which are due for attention and dispatches them to where they are to be handled.
The main job of the simulator is to run tasks. These tasks are supplied by you.
Here’s how you would set up and run a simulation:
# The asyncio module is standard in Python 3.4 and above
loop = asyncio.get_event_loop()
# Create a generator for the simulation 'tick-stream'
ticks = Simulation.ticks(0, 10, 1)
# More about the JobQueue later...
jq = JobQueue(loop=loop)
# Simulation will run only a single task; 'agent', defined below.
tasks = [Simulation.task(loop, agent, jq)]
# Initialise the simulation object
sim = Simulation(tasks, jq)
# Run the simulation until it finishes or times out.
loop.run_until_complete(asyncio.wait_for(
sim.dispatch(ticks), loop=loop, timeout=1))
What is a task? It’s a function or method which listens for the passage of time and which can ask for things to happen in the future. Here’s what the task agent might look like:
@asyncio.coroutine
def agent(q, jq, priority):
tick = None
while type(tick) is not Stop:
tick = yield from q.get()
# Do stuff...
msg = "For the attention of..."
yield from jq.put(tick.t + 1, priority, msg)
else:
return tick
Agent is a coroutine. To work with the Turberfield simulator, it must have these three positional parameters; q, jq, and priority:
Job Queue¶
Ticks¶
- class turberfield.dynamics.types.Tick¶
A named tuple of (‘t’, ‘priority’) to specify the time and priority order of a message.
Messages¶
You can send any objects you like to the job queue. To define how they are processed when their time arrives, you should register a handler according to their type.
- turberfield.dynamics.simulation.message_handler(msg, *args, **kwargs)[source]¶
The simulation calls this function, passing to it every message which pops up in the job queue.
This function has been wrapped with the functools.singledispatch decorator.
So, to register your own supplied function as a handler for a particular class of message, make a call like the following:
message_handler.register(type(msg), custom_handler)
Simulation¶
- class turberfield.dynamics.simulation.Simulation(tasks, jq=None, buf=None)[source]¶
The simulator in Turberfield is a shareable object. You need to define it once in your main module:
sim = Simulation(tasks, jq, buf)
Elsewhere, you can grab a readable copy like this:
sim = Simulation(None)
- tasks
- A list of task objects. Each should be the result of a call to Simulation.task.
- jq (optional)
- A JobQueue object. The simulation will get its messages here.
- buf (optional)
- A container which will record jobs as they are processed by the simulation. This can be useful for debugging purposes. If you omit this argument, the Simulation object will create its own (small) buffer.
- static task(loop, fn, jq, priority=1, *args, **kwargs)[source]¶
Create a asyncio.Task for fn (which must be a coroutine). The returned object is decorated with attributes necessary for use with a Simulation object. jq should be a JobQueue object common to all the tasks in your simulation.