August'24: Kamaelia is in maintenance mode and will recieve periodic updates, about twice a year, primarily targeted around Python 3 and ecosystem compatibility. PRs are always welcome. Latest Release: 1.14.32 (2024/3/24)
A component is a microprocess with a microthread of control and input/output queues (inboxes and outboxes) which can be connected by linkages to other components. Ie. it has code that runs whilst the component is active, and mechanisms for sending and receiving data to and from other components.
There are other variants on the basic component:
If your component needs to block - eg. wait on a system call; then make it a 'threaded' component. If it needs to change what inboxes or outboxes it has at runtime, then make it an 'adaptive' component.
Here's a simple example:
class MyComponent(Axon.Component.component):
Inboxes = { "inbox" : "Send the FOO objects to here",
"control" : "NOT USED",
}
Outboxes = { "outbox" : "Emits BAA objects from here",
"signal" : "NOT USED",
}
def main(self):
while 1:
if self.dataReady("inbox"):
msg = self.recv("inbox")
result = ... do something to msg ...
self.send(result, "outbox")
yield 1
Or, more specifically:
Subclass the component class. Don't forget to call the superclass initializer if you write your own __init__ method:
class MyComponent(Axon.Component.component):
def __init__(self, myArgument, ...):
super(MyComponent, self).__init__()
...
Declare any inboxes or outboxes you expect it to have - do this as static members of the class called "Inbox" and "Outbox". They should be dictionaries where the key is the box name and the value serves as documentation describing what the box is for:
Inboxes = { "inbox" : "Send the FOO objects to here",
"control" : "NOT USED",
}
Outboxes = { "outbox" : "Emits BAA objects from here",
"signal" : "NOT USED",
}
You can also do this as lists, but doing it as a dictionary with documentation values is much more useful to people wanting to use your component.
If you don't specify any then you get the default "inbox" and "control" inboxes and "outbox" and "signal" outboxes.
Write the main() method - it must be a generator - namely just like an ordinary method or function, but with 'yield' statements regularly interspersed through it. This is the thread of execution in the component - just use yield statements regularly so other components get time to execute.
If you need to know more, these might help you:
Once you have written your component class; simply create instances and activate them; then start the scheduler to begin execution:
x = MyComponent()
y = MyComponent()
z = AnotherComponent()
x.activate()
y.activate()
z.activate()
scheduler.run.runThreads()
If the scheduler is already running, then simply activating a component will start it executing.
Components have inboxes and outboxes. The main() thread of execution in a component communicates with others by picking up messages that arrive in its inboxes and sending out messages to its outboxes. For example here is a simple block of code for the main() method of a component that echoes anything it receives to the console and also sends it on:
if self.dataReady("inbox"):
msg = self.recv("inbox")
print str(msg)
self.send(msg,"outbox")
yield 1
Use the dataReady() method to find out if there is (one or more items of) data waiting at the inbox you name. recv() lets you collect a message from an inbox. Use the send() method to send a message to the outbox you name. There is also an anyReady() method that will tell you if any inbox has data waiting in it.
A message gets from one component's outbox to another one's inbox if there is a linkage between them (going from the outbox to the inbox). A component can create and destroy linkages by using the link() and unlink() methods.
For example, we could create a linkage from a component's outbox called "outbox" to a different component's inbox called "inbox":
theLink = self.link( (self, "outbox"), (otherComponent, "inbox") )
Using the handle that we were given, we can destroy that linkage later:
self.unlink(theLinkage = theLink)
Linkages normally go from an outbox to an inbox - after all the whole idea is to get messages that one component sends to its own outbox to arrive at another component's inbox. However you can also create 'passthrough' linkages from an inbox to another inbox; or from an outbox to another outbox.
This is particularly useful if you want to encapsulate a child component - hide it from view so other components only need to be wired up to you.
For example, your component may want any data being sent to one of its inboxes to be forwarded automatically onto an inbox on the child. This is a type '1' passthrough linkage:
thelink = self.link( (self,"inbox"), (myChild,"inbox"), passthrough=1 )
Or if you want anything a child sends to its outbox to be sent out of one of your own outboxes, which is a type '2' passthrough:
thelink = self.link( (myChild,"outbox"), (self,"outbox"), passthrough=2 )
The alternative, of course, is to add extra inboxes and outboxes to communicate with the child, and to write a main() method that simply passes the data on. Passthrough linkages are more efficient and quicker to code!
There is no performance penalty for delivering a message along a such a chain of linkages (eg. outbox ---> outbox ---> inbox ---> inbox ---> inbox). Axon resolves the chain and delivers straight to the final destination inbox!
Components can create and activate other components. They can adopt them as children:
newComponent = FooComponent()
self.addChildren(newComponent)
newComponent.activate()
Making a component your child means that:
Whether another component is your child or not, you can tell if it has terminated yet by calling its _isStopped() method.
For example, a component might want to create a child component, make a linkage to it then wait until that child terminates before cleaning it up. Achieve this by writing code like this in the main() body of the component:
src = RateControlledFileReader("myTextFile",readmode="lines")
dst = ConsoleEchoer()
lnk = self.link( (src,"outbox"), (dst,"inbox") )
self.addChildren(src,dst) # we will be woken if they terminate
src.activate()
dst.activate()
while not dst._isStopped() and not src._isStopped():
self.pause()
yield 1
self.unlink(thelinkage = lnk)
self.removeChild(src)
self.removeChild(dst)
When a component has nothing to do it can pause. This means it will not be executed again until it is woken up. Pausing is a good thing since it relinquishes cpu time for other parts of the system instead of just wasting it.
When would you want to pause? - usually when there is nothing left waiting in any of your inboxes and there is nothing else for your component to do:
class Echoer(Axon.Component.component):
def main(self):
while 1:
if self.dataReady("inbox"):
msg = self.recv("inbox")
print str(msg)
self.send(msg,"outbox")
if not self.anyReady():
self.pause()
yield 1
Calling the pause() method means that at the next yield
statement your component will be put to sleep. It doesn't happen as
soon as you call pause() - only when execution reaches the next yield
.
What will wake up a paused component? - any of the following:
Your component cannot wake itself up - only the actions of other components can cause it to be woken. Why? You try writing some code that stops executing (pauses) yet can issue a method call to ask to be woken! :-)
Components also currently support an 'old' style for writing their main thread of execution.
This way of writing components is likely to be deprecated in the near future. We suggest you write a main() method as a generator instead.
To do old-style; instead of writing a generator you write 3 functions - one for initialisation, main execution and termination. The thread of execution for the component is therefore effectively this:
self.initialiseComponent()
while 1:
result = self.mainBody()
if not result:
break
self.closeDownComponent()
initialiseComponent() is called once when the component first starts to execute.
mainBody() is called every execution cycle whilst it returns a non zero value. If it returns a zero value then the component starts to shut down.
closeDownComponent() is called when the component is about to shut down.
A simple echo component might look like this:
class EchoComponent(Axon.Component.component):
def initialiseComponent(self):
# We can perform pre-loop initialisation here!
# In this case there is nothing really to do
return 1
def mainBody(self):
# We're now in the main loop
if self.dataReady("inbox"):
msg = self.recv("inbox")
print msg
self.send(msg,"outbox")
if not self.anyReady():
self.pause()
# If we wanted to exit the loop, we return a false value
# at the end of this function
# If we want to continue round, we return a true, non-None,
# value. This component is set to run & run...
return 1
def closeDownComponent(self):
# We can't get here since mainLoop above always returns true
# If it returned false however, we would get sent here for
# shutdown code.
# After executing this, the component stops execution
return
Component is a subclass of microprocess (from which it inherits the ability to have a thread of execution). It includes its own postoffice object for creating and destroying linkages. It has a collection of inboxes and outboxes.
Attributes:
Tests passed:
Base class for an Axon component. Subclass to make your own.
A simple example:
class IncrementByN(Axon.Component.component):
Inboxes = { "inbox" : "Send numbers here",
"control" : "NOT USED",
}
Outboxes = { "outbox" : "Incremented numbers come out here",
"signal" : "NOT USED",
}
def __init__(self, N):
super(IncrementByN,self).__init__()
self.n = N
def main(self):
while 1:
while self.dataReady("inbox"):
value = self.recv("inbox")
value = value + self.n
self.send(value,"outbox")
if not self.anyReady():
self.pause()
yield 1
Register component as a child.
This takes a child component, and adds it to the children list of this component. It also registers to be woken up by the child if it terminates.
This has a number of effects internally, and includes registering the component as capable of recieving and sending messages. It doesn't give the child a thread of control however!
You will want to call this function if you create child components of your component.
You want to overide this method locally.
You MUST remember to call the superconstructor for things to work however. The way you do this is: super(YourClass,self).__init__()
Provides a useful string representation of the component. You probably want to override this, and append this description using something like: 'component.__str__(self)'
Overrides original in microprocess class.
Ensures callbacks are deregistered, and all linkages created by this component are destroyed.
For tests and debugging ONLY - delivers message to an inbox.
Register the specified component(s) as children of this component
This component will be woken if any of its children terminate.
Note that any children still need to be activated!
Returns true if any inbox has any data waiting in it.
Used by a component to check an inbox for ready data.
You are unlikely to want to override this method.
Returns list of child components
Stub method. This method is designed to be overridden.
Returns true if data is available in the requested inbox.
Used by a component to check an inbox for ready data.
Call this method to periodically check whether you've been sent any messages to deal with!
You are unlikely to want to override this method.
Stub method. This method is designed to be overridden.
Creates a linkage from one inbox/outbox to another.
-- source - a tuple (component, boxname) of where the link should start from -- sink - a tuple (component, boxname) of where the link should go to
Other optional arguments:
See Axon.Postoffice.postoffice.link() for more information.
Override this method, writing your own main thread of control as a generator. When the component is activated and the scheduler is running, this what gets executed.
Write it as a python generator with regular yield statements returning a non zero value.
If you do not override it, then a default main method exists instead that will:
Stub method. This method is designed to be overridden.
returns the first piece of data in the requested inbox.
Used by a component to recieve a message from the outside world. All comms goes via a named box/input queue
You will want to call this method to actually recieve messages you've been sent. You will want to check for new messages using dataReady first though.
You are unlikely to want to override this method.
Deregister component as a child.
Removes the child component, and deregisters it from notifying us when it terminates. Also removes any linkages this component has made that involve this child.
You probably want to do this when you enter a closedown state of some kind for either your component, or the child component.
appends message to the requested outbox.
Used by a component to send a message to the outside world. All comms goes via a named box/output queue.
You will want to call this method to send messages.
Raises Axon.AxonExceptions.noSpaceInBox if this outbox is linked to a destination inbox that is full.
You are unlikely to want to override this method.
boxname - some boxname, must be an inbox ; size - maximum number of items we're happy with
Destroys all linkages to/from the specified component or destroys the specific linkage specified.
Only destroys linkage(s) that were created by this component itself.
Keyword arguments:
See Axon.Postoffice.postoffice.unlink() for more information.
Got a problem with the documentation? Something unclear that could be clearer? Want to help improve it? Constructive criticism is very welcome - especially if you can suggest a better rewording!
Please leave you feedback here in reply to the documentation thread in the Kamaelia blog.
-- Automatic documentation generator, 09 Dec 2009 at 04:00:25 UTC/GMT