Hi,

(This mail might become extremely verbose. Please bear with me)

The speech-dispatcher python API can be accessed at:
http://cvs.freebsoft.org/repository/speechd/src/python/speechd/client.py?view=markup

The client API communicates with the speech server presently in a thread by
polling the socket in the _communication() method. Whenever data is read
from the socket it is added to a buffer self._com_buffer.append((code, msg,
data)) and a semaphore value (self._ssip_reply_semaphore) is incremented. To
read data off this buffer _recv_response(self) is called and this method is
synchronized with the same semaphore self._ssip_reply_semaphore.

When a client instantiates a connection with the speech server a certain
amount of handshaking data is shared between the client API implementation
and speech server.

We are trying to eliminate the need for a thread by monitoring the socket
using gobject.io_add_watch(self._socket, gobject.IO_IN,
self._socket_read_cb). When the gtk mainloop is running and an input
activity is observed on the socket self._socket_read_cb will be called.
self._socket_read_cb in essence replicates _communication()

When the handshaking is taking place the gtk mainloop has not yet started
and hence we cannot rely on the event generated by gobject to read data off
the socket. And if this handshaking does not take place the API
implementation blocks the entire process thus leading to a deadlock. Now I
figured that we can allow the polling thread to exist until the handshaking
takes place and then shift to the gobject method of monitoring the socket. I
achieve this by defining a variable self._first_run. When the handshaking
completes I make the polling thread exit (by calling close_thread(self) from
the main control thread) . In this time frame I expect that the gtk mainloop
will start itself (I am quite sure that it does start actually).

Now the main problem:
the gobject method does not seem to run the callback to read data from the
socket when data is written to the socket. (I tried to test the socket
monitoring method in a separate script and it worked fine then). If the API
implementation is not able to read data from the buffer it blocks or
deadlocks...

I am unable to determine the exact mistake that I am making. Is there
something that I am missing in the way the API is implemented or is it a
problem with the way i am using gobject.io_add_watch?


Any clues/hints ?

Thanks,
Hemant
--- client.py	2008-06-13 16:13:33.000000000 +0530
+++ client_gobject.py	2008-06-18 22:31:12.000000000 +0530
@@ -111,7 +111,7 @@
                           705: CallbackType.RESUME,
                           }
     
-    def __init__(self, host, port):
+    def __init__(self, host, port, use_gobject=True):
         """Init connection: open the socket to server,
         initialize buffers, launch a communication handling
         thread."""
@@ -121,10 +121,48 @@
         self._com_buffer = []
         self._callback = None
         self._ssip_reply_semaphore = threading.Semaphore(0)
-        self._communication_thread = \
+        self._first_run = True
+        
+
+        if use_gobject:
+            self._communication_thread = None
+            import gobject
+            gobject.io_add_watch(self._socket, gobject.IO_IN, self._socket_read_cb)
+            self._communication_thread = \
+                threading.Thread(target=self._communication, kwargs={},
+                                 name="SSIP client communication thread")
+            self._communication_thread.start()
+                       
+        else:
+            self._communication_thread = \
                 threading.Thread(target=self._communication, kwargs={},
                                  name="SSIP client communication thread")
-        self._communication_thread.start()
+            self._communication_thread.start()
+        
+    def _socket_read_cb(self, source, condition):
+        
+        print "Event detected on the socket!"
+        try:
+            code, msg, data = self._recv_message()
+        except IOError:
+            # If the socket has been closed, stop listening
+            return False
+        if code/100 != 7:
+            # This is not an index mark nor an event
+            self._com_buffer.append((code, msg, data))
+            self._ssip_reply_semaphore.release()
+            # Ignore the event if no callback function has been registered.
+        if self._callback is not None:
+            type = self._CALLBACK_TYPE_MAP[code]
            if type == CallbackType.INDEX_MARK:
+                kwargs = {'index_mark': data[2]}
+            else:
+                kwargs = {}
+                # Get message and client ID of the event
+                msg_id, client_id = map(int, data[:2])
+                self._callback(msg_id, client_id, type, **kwargs)
+
+        return True
+ 
     
     def close(self):
         """Close the server connection, destroy the communication thread."""
@@ -137,6 +175,15 @@
         self._socket.close()
         # Wait for the other thread to terminate
         self._communication_thread.join()
+    
+    def close_thread(self):
+        self._first_run = False
+        print "close_thread:: FIRST RUN IS ::"
+        print self._first_run
+        print "close_thread::first run now complete"
+        self._communication_thread.join()
+        print "close_thread::thread closed"
+        self._communication_thread = None
         
     def _communication(self):
         """Handle incomming socket communication.
@@ -151,7 +198,10 @@
         interrupted by closing the socket on which it is listening for
         reading."""
 
-        while True:
+        while self.first_run:
+            print "_communicate::INSIDE THE LOOP"
+            print self._first_run
+            
             try:
                 code, msg, data = self._recv_message()
             except IOError:
@@ -173,6 +223,9 @@
                 msg_id, client_id = map(int, data[:2])
                 self._callback(msg_id, client_id, type, **kwargs)
                 
+        print "_communicate::_first_run has now been set to False"
+        sys.exit()
+                
                 
     def _readline(self):
         """Read one whole line from the socket.
@@ -215,8 +268,11 @@
         and return the triplet (code, msg, data)."""
         # TODO: This check is dumb but seems to work.  The main thread
         # hangs without it, when the Speech Dispatcher connection is lost.
-        if not self._communication_thread.isAlive():
-            raise SSIPCommunicationError
+
+        if not self._communication_thread is None:        
+            if not self._communication_thread.isAlive():
+                raise SSIPCommunicationError
+        
         print "_recv_response::made it till here"
         self._ssip_reply_semaphore.acquire()
         print "_recv_response::do i smell some data? yes I do"
@@ -253,6 +309,7 @@
             self._socket.send(cmd + self._NEWLINE)
         except socket.error:
             raise SSIPCommunicationError("Speech Dispatcher connection lost.")
+                  
         code, msg, data = self._recv_response()
         print "send_command:: read some data from the socket"
         print "CODE :"
@@ -288,6 +345,7 @@
         except socket.error:
             raise SSIPCommunicationError("Speech Dispatcher connection lost.")
         code, msg, response_data = self._recv_response()
+        print "send_data::made it till here"
         if code/100 != 2:
             raise SSIPDataError(code, msg, data)
         return code, msg, response_data
@@ -418,7 +476,10 @@
                       CallbackType.PAUSE,
                       CallbackType.RESUME):
             conn.send_command('SET', 'self', 'NOTIFICATION', event, 'on')
-
+         
+         #once the initial communication is done, shift to communicating with
+         #gobject   
+        self._conn.close_thread()
     
     def __del__(self):
         """Close the connection"""
#!/usr/bin/env python

# example helloworld2.py

import pygtk
pygtk.require('2.0')
import gtk
import socket
import gobject
from client_gobject import *

class HelloWorld2:

    # Our new improved callback.  The data passed to this method
    # is printed to stdout.
    def callback(self, widget, data):
        print "Hello again - %s was pressed" % data
        
        self.speechd_client.set_priority(Priority.MESSAGE)
        self.speechd_client.set_language('en')
        self.speechd_client.set_rate(-10)
        #self.speechd_client.set_output_module('espeak')
        #self.speechd_client.set_pitch(-20)
        #self.speechd_client.set_volume(0)
        #self.speechd_client.set_cap_let_recogn("icon")
        #for i in range(0,5,1):
        #    self.speechd_client.speak("Give me the callback", self.sugar_callback, [CallbackType.BEGIN, CallbackType.END] )
					

    def sugar_callback(self,arguments):
		print "I came to the callback this is good!"
    
    
    # another callback
    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.speechd_client = SSIPClient('sugar','main','olpc')       
        # Create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

        # This is a new call, which just sets the title of our
        # new window to "Hello Buttons!"
        self.window.set_title("Hello Buttons!")

        # Here we just set a handler for delete_event that immediately
        # exits GTK.
        self.window.connect("delete_event", self.delete_event)

        # Sets the border width of the window.
        self.window.set_border_width(10)

        # We create a box to pack widgets into.  This is described in detail
        # in the "packing" section. The box is not really visible, it
        # is just used as a tool to arrange widgets.
        self.box1 = gtk.HBox(False, 0)

        # Put the box into the main window.
        self.window.add(self.box1)

        # Creates a new button with the label "Button 1".
        self.button1 = gtk.Button("Button 1")

        # Now when the button is clicked, we call the "callback" method
        # with a pointer to "button 1" as its argument
        self.button1.connect("clicked", self.callback, "button 1")

        # Instead of add(), we pack this button into the invisible
        # box, which has been packed into the window.
        self.box1.pack_start(self.button1, True, True, 0)

        # Always remember this step, this tells GTK that our preparation for
        # this button is complete, and it can now be displayed.
        self.button1.show()

        # Do these same steps again to create a second button
        self.button2 = gtk.Button("Button 2")

        # Call the same callback method with a different argument,
        # passing a pointer to "button 2" instead.
        self.button2.connect("clicked", self.callback, "button 2")

        self.box1.pack_start(self.button2, True, True, 0)

        # The order in which we show the buttons is not really important, but I
        # recommend showing the window last, so it all pops up at once.
        self.button2.show()
        self.box1.show()
        self.window.show()

def main():
    gtk.main()

if __name__ == "__main__":
    hello = HelloWorld2()
    main()
_______________________________________________
Devel mailing list
Devel@lists.laptop.org
http://lists.laptop.org/listinfo/devel

Reply via email to