> This is true, thanks. Now let's say I wanted to write to the client's
> console before reading anything from their console.
>
> With no other changes, this Pipeline setting doesn't work:
> Pipeline(
>                  MyComponent(),
>                  ConsoleReader(),
> ).run()
>
> but I am expecting it to work. What am I missing?

"Pipeline" isn't about saying what order to run things in - its more like  
expressing how to plug things together: The declaration above is saying:  
""" Make two components - an instance of MyComponent and an instance of  
ConsoleReader; now plug them together so that the "outbox" of  
"MyComponent" is joined to the "inbox" of "ConsoleReader" """

You could think of this as being like wiring your video recorder  
(MyComponent) up to your television set (ConsoleReader). Both are turned  
on and run at the same time. It just so happens that one sends stuff to  
the other.

In the Kamaelia system, the MyComponent places data into its "outbox"  
outbox. Because it is in this pipeline, that data is automatically put  
straight into the "inbox" inbox of the ConsoleReader, ready to be  
collected.

This is all about the flow of data. The order in which things happen is  
defined by the behaviour of each component.

So going back to your original aim:

> This is true, thanks. Now let's say I wanted to write to the client's
> console before reading anything from their console.

So you want a program where the flow of data is as follows:

    input from console ---> MyComponent ----> output to console

I'm guessing there are two possibilities for what you're after achieving.  
One is easier than the other :-)

In the simplest case, you want a pipeline like this:

     Pipeline(
         ConsoleReader(),
         MyComponent(),
         ConsoleEchoer()
     ).run()

What is actually being wired up here? These two diagrams from  
http://www.kamaelia.org/Cookbook/Pipelines may help. The first shows the  
'intention' you express when you write a Pipeline. The second shows the  
detail of what inboxes and outboxes actually get wired up.

http://www.kamaelia.org/images/pipeline1_intention.gif
http://www.kamaelia.org/images/pipeline1_inside.gif


Now the main() generator in MyComponent is where we can control the actual  
behaviour, so we'll do something like this:

     def main(self):
         self.send("Initial message for the console", "outbox")
         while not self.doShutdown():
             if self.dataReady("inbox"):
             data = self.recv("inbox")
             # let's echo what we received...
             self.send(data, 'outbox')

             if not self.anyReady():
                 self.pause()

             yield 1


Thats the simple version. Now a little more complexity! ...

The above code won't guarantee is whether the "initial message for the  
console" is displayed before the prompt that is displayed by the  
ConsoleReader. Why is this? Because the Pipeline declaration effectively  
says "make all these and start them all at teh same time".

If you want explicit behaviour where a component isn't created or  
activated, or wired up until a later point in the execution (ie. you want  
to control the order things happen in) then you need to express it  
explicitly.

Here's *a* way to do it:

We can use the Carousel component do delay the creation and wiring up of  
the ConsoleReader component until MyComponent sends a message telling it  
to do so. There's some explanation of Carousel here:  
http://www.kamaelia.org/Cookbook/Carousels if it helps.

In short, Carousel is designed to contain another component. When it  
receives a message, it calls a helper function (that you supply) to create  
a component. It then takes that component and wires it up so that its  
"outbox" and "inbox" are mapped to the "outbox" and "inbox" of the  
Carousel component itself. We'll declare this helper globally for now:

     def makeConsoleReader(message):         # we ignore the parameter
         return ConsoleReader()


So that MyComponent can send a message to the Carousel, we'll need to add  
an extra outbox to MyComponent, and get it to send that signal:

     Class MyComponent(component):
         Outboxes = {"outbox"      : "some data out",
                     "signal"      : "Shutdown signal",
                     "ready"       : "Sends a message when initialisation  
is complete",
                    }

         ...

         def main(self):
         self.send("Initial message for the console", "outbox")

         yield 1

         self.send("OK", "ready")

         while not self.doShutdown():
             if self.dataReady("inbox"):
             data = self.recv("inbox")
             # let's echo what we received...
             self.send(data, 'outbox')

             if not self.anyReady():
                 self.pause()

             yield 1

Note the 'yield' statements - which give other components a chance to run  
- giving the ConsoleEchoer a chance to display that initial message.  
Remember that we don't control the precise order in which components  
execute: if we sent the initial message and the ready message without a  
yield in between, we might be unlucky, and the Carousel might get executed  
first!

We cannot use a simple pipeline now, since we need to also wire up the  
"ready" outbox of MyComponent to the Carousel's "next" inbox. A Pipeline  
only wires up the standard "inbox" and "control" inboxes and "outbox" and  
"signal" outboxes. So we'll use a Graphline instead, where we specify each  
linkage explicitly:

     from Kamaelia.Chassis.Carousel import Carousel
     from Kamaelia.Chassis.Graphline import Graphline

     ....

     Graphline(
         READER = Carousel(makeConsoleReader),
         MYCOMP = MyComponent(),
         OUTPUT = ConsoleEchoer(),
         linkages = {
             # main flow of data:
             ("READER", "outbox") : ("MYCOMP", "inbox"),
             ("MYCOMP", "outbox") : ("OUTPUT", "inbox"),

             # the 'ready' message:
             ("MYCOMP", "ready")  : ("READER", "next"),

             # the 'signal' and 'control' boxes used for shutdown:
             ("READER", "signal") : ("MYCOMP", "control"),
             ("MYCOMP", "signal") : ("OUTPUT", "control"),

         }
     ).run()

So what should happen when this runs?

1) The components are created and wired up as shown above.

2) They all start executing.

2a) The Carousel and ConsoleEchoer only do stuff in response to messages  
they receive, so they sit there waiting. Only MyComponent really starts  
doing anything significant

3) MyComponent sends out its "initial message" out of its "outbox". The  
ConsoleEchoer receives this and displays the message on the console

4) MyComponent sends a message out of its "ready" outbox, and it is  
received at the "next" inbox of the Carousel.
4a) The Carousel uses the makeConsoleReader() function we wrote to create  
a ConsoleReader component. It gets wired up so that it uses the outboxes  
and inboxes of the Carousel.
4b) The ConsoleReader component also gets activated, and so its main()  
method starts running. It therefore displays its prompt for user input

5) When the user enters input, the ConsoleReader sends out of its "outbox"  
to the "inbox" of MyComponent.



Hmm, more long winded than I originally hoped this would be. Hope it helps  
though.


Matt
-- 
| Matt Hammond
| Research Engineer, FM&T, BBC, Kingswood Warren, Tadworth, Surrey, UK
| http://www.bbc.co.uk/rd/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"kamaelia" group.
To post to this group, send email to kamaelia@googlegroups.com
To unsubscribe from this group, send email to 
kamaelia+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/kamaelia?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to