watson-intu / self

Intu is a Cognitive Embodiment Middleware for AI on the edge.
Other
28 stars 27 forks source link

Unable to Post to Blackboard through Python Script #15

Open MaryHewittWork opened 7 years ago

MaryHewittWork commented 7 years ago

While running a self instance, our python script is intended to post text to the Blackboard for conversation to pick up. Unfortunately, this is not occurring, resulting in the following error:

python write_to_blackboard.py --text hello
('hostname: %s, port: %d', 'localhost', 9443)
TopicClient Instantiated!
Topic Client is instantiated!
Connected!
Trying to re instantiated Topic Client for some reason...
Closed down 1006 Going away
Trying to re instantiated Topic Client for some reason...
Exception in thread WebSocketClient:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "build/bdist.linux-x86_64/egg/ws4py/websocket.py", line 527, in run
    self.terminate()
  File "build/bdist.linux-x86_64/egg/ws4py/websocket.py", line 427, in terminate
    self.closed(1006, "Going away")
  File "/home/robonaut/self/clients/self-python-sdk/self/topics/web_socket.py", line 33, in closed
    TopicClient.get_instance().is_connected = False
  File "/home/robonaut/self/clients/self-python-sdk/self/topics/topic_client.py", line 51, in get_instance
    cls.__instance = TopicClient()
TypeError: __init__() takes exactly 4 arguments (1 given)

The full code of that Python script is found below:

#!/usr/bin/env python
import argparse
import sys
import thread
import socket
sys.path.insert(0, "/home/robonaut/self/clients/self-python-sdk/self/")
sys.path.insert(0, "/home/robonaut/self/clients/self-python-sdk/")

from blackboard.blackboard import Blackboard
from blackboard.thing import Thing
from topics.topic_client import TopicClient
#import nasa_common_logging
#from taskforce_plugin_self.tf_topic_client import TaskForceTopicClient
#from taskforce_plugin_self.blackboard_interface import BlackboardInterface

class BlackboardInterface(object):

    def __init__(self, local, text_in, is_say):
        """Constructor

        Sets up logging, gets the IBM self configuration, and sets
        the user text desired to be written to the blackboard.

        Args:
            local: bool, when true, uses a local Intu instance instead
                of the built in assumption of 'captain'
            text: string with text to write to the blackboard
        """
        #self.logger = logging.getLogger(
        #    'taskforce_plugin_self.blackboard_interface')
        #self.selfConfig = SelfUtils.get_self_config()
        self.text = text_in
        self.is_say = is_say
        if local:
            self.hostname = 'localhost'
        else:
            self.hostname = '127.0.0.1'
        self.hostname = '127.0.0.1'
        self.port = 9443

        print('hostname: %s, port: %d',
                         self.hostname, self.port)

    def write(self):
        """Do the actual write to the blackboard

        Creates the Thing object which contains the user text, then
        writes that object to the blackboard and then exits the script.
        """
        thing = Thing()
        thing.set_type('Text')
        textToAdd = {}
        textToAdd['m_Text'] = self.text
        thing.set_body(textToAdd)
        print('[Sending text:] %s', self.text)
        Blackboard.get_instance().add_thing(thing, '{}')
        thread.exit()

    def say(self):
        """Writes a Say object to the blackboard

        Creates a Thing object which contains text to have Intu speak
        out loud.
        """
        thing = Thing()
        thing.set_type('Say')
        textToAdd = {}
        textToAdd['m_Text'] = self.text
        thing.set_body(textToAdd)
        print('[Sending Say:] %s', self.text)
        Blackboard.get_instance().add_thing(thing, '{}')
        thread.exit()

    def on_connected(self):
        """Callback when topic client connects

        Upon successful connection to the intu instance, write the
        actual user requested text (provided in the constructor) to the blackboard
        """
        #self.logger.info('connected to self')
        print('[Sending Say/Text:] Connected to Self')
        if self.is_say:
            self.say()
        else:
            self.write()

    def run(self):
        """Initialize and connect the topic client

        Initializes the topic client, sets the connection callback, then starts it
        """
        #headers = [
        #    ('selfId', ''), ('token', '')]
        #topic = TopicClient.start_instance(
        #    self.hostname, self.port, headers)
        #TopicClient.get_instance().setHeaders('', '')
        #TopicClient.get_instance().set_callback(self.on_connected)
        #topic.start()
        try:
            headers = [('selfId', 'ManualAddtoBlackboard.ForGStT'), ('token', '')]
            topic = TopicClient.start_instance(self.hostname, self.port, headers)
            TopicClient.get_instance().setHeaders("", "")
            TopicClient.get_instance().set_callback(self.on_connected);
            topic.start()
        except KeyboardInterrupt:
            thread.exit()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Blackboard writer')
    parser.add_argument('-l', '--loglevel', type=str, choices=[
                        'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the log level')
    parser.add_argument('--local', action='store_true', default=False,
                        help='Tells the script to use a local Intu instance')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-t', '--text', type=str,
                       help='Text to write to the blackboard')
    group.add_argument('-s', '--say', type=str,
                       help='Text to have the robot say')
    args, unknown_args = parser.parse_known_args(sys.argv[1:])
    #nasa_common_logging.configure_common_logging(level=args.loglevel)

    if args.say:
        bi = BlackboardInterface(args.local, args.say, True)
    else:
        bi = BlackboardInterface(args.local, args.text, False)

    bi.run()
rlyle commented 7 years ago

The fact that I see this twice in your log worries me: Trying to re instantiated Topic Client for some reason...

The TopicClient was intended to be a singleton object originally, but then I think something got changed late in the development of the python SDK and it was no longer a singleton to allow for multiple instances of TopicClient at once. I can't really remember if we reverted that change or not.

rlyle commented 7 years ago

try writing your code without using get_instance() ... since it looks like your getting a TopicClient instance when you call start() already.

takaomoriyama commented 6 years ago

I recreated the issue on my MacBook. I think there are two different issues.

  1. Singleton management of TopicClient is not working properly.
  2. Socket connection seems to be disconnected ("Closed down 1006 Going away" issue)

I found the cause of the first issue. In clients/self-python-sdk/write_to_blackboard.py,

sys.path.insert(0, "<whatever>/self/clients/self-python-sdk/self/")
sys.path.insert(0, "<whatever>/self/clients/self-python-sdk/")
...
from blackboard.blackboard import Blackboard
from blackboard.thing import Thing
from topics.topic_client import TopicClient

this refers to topics.topic_client.TopicClient, and in clients/self-python-sdk/self/topics/web_socket.py,

from topic_client import TopicClient

will refer to self.topics.topic_client.TopicClient which is different from the one above.

The solution is changing the first code snippet to

#sys.path.insert(0, "<whatever>/self/clients/self-python-sdk/self/")
#sys.path.insert(0, "<whatever>/self/clients/self-python-sdk/")
...
from self.blackboard.blackboard import Blackboard
from self.blackboard.thing import Thing
from self.topics.topic_client import TopicClient
takaomoriyama commented 6 years ago

The following version of write_blackboard.py works well on my environment. No change in Intu required. Could you try this ?

#!/usr/bin/env python
import argparse
import sys
import thread
import socket

from self.blackboard.blackboard import Blackboard
from self.blackboard.thing import Thing
from self.topics.topic_client import TopicClient
#import nasa_common_logging
#from taskforce_plugin_self.tf_topic_client import TaskForceTopicClient
#from taskforce_plugin_self.blackboard_interface import BlackboardInterface

class BlackboardInterface(object):

    def __init__(self, local, text_in, is_say):
        """Constructor

        Sets up logging, gets the IBM self configuration, and sets
        the user text desired to be written to the blackboard.

        Args:
            local: bool, when true, uses a local Intu instance instead
                of the built in assumption of 'captain'
            text: string with text to write to the blackboard
        """
        #self.logger = logging.getLogger(
        #    'taskforce_plugin_self.blackboard_interface')
        #self.selfConfig = SelfUtils.get_self_config()
        self.text = text_in
        self.is_say = is_say
        if local:
            self.hostname = 'localhost'
        else:
            self.hostname = '127.0.0.1'
        self.hostname = '127.0.0.1'
        self.port = 9443

        print('hostname: %s, port: %d',
                         self.hostname, self.port)

    def write(self):
        """Do the actual write to the blackboard

        Creates the Thing object which contains the user text, then
        writes that object to the blackboard and then exits the script.
        """
        thing = Thing()
        thing.set_type('Text')
        textToAdd = {}
        textToAdd['m_Text'] = self.text
        thing.set_body(textToAdd)
        print('[Sending text:] %s', self.text)
        Blackboard.get_instance().add_thing(thing, '')
        thread.exit()

    def say(self):
        """Writes a Say object to the blackboard

        Creates a Thing object which contains text to have Intu speak
        out loud.
        """
        thing = Thing()
        thing.set_type('Say')
        textToAdd = {}
        textToAdd['m_Text'] = self.text
        thing.set_body(textToAdd)
        print('[Sending Say:] %s', self.text)
        Blackboard.get_instance().add_thing(thing, '')
        thread.exit()

    def on_connected(self):
        """Callback when topic client connects

        Upon successful connection to the intu instance, write the
        actual user requested text (provided in the constructor) to the blackboard
        """
        #self.logger.info('connected to self')
        print('[Sending Say/Text:] Connected to Self')
        if self.is_say:
            self.say()
        else:
            self.write()

    def run(self):
        """Initialize and connect the topic client

        Initializes the topic client, sets the connection callback, then starts it
        """
        #headers = [
        #    ('selfId', ''), ('token', '')]
        #topic = TopicClient.start_instance(
        #    self.hostname, self.port, headers)
        #TopicClient.get_instance().setHeaders('', '')
        #TopicClient.get_instance().set_callback(self.on_connected)
        #topic.start()
        try:
            headers = [('selfId', 'ManualAddtoBlackboard.ForGStT'), ('token', '')]
            topic = TopicClient.start_instance(self.hostname, self.port, headers)
            TopicClient.get_instance().setHeaders("", "")
            TopicClient.get_instance().set_callback(self.on_connected);
            topic.start()
        except KeyboardInterrupt:
            thread.exit()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Blackboard writer')
    parser.add_argument('-l', '--loglevel', type=str, choices=[
                        'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the log level')
    parser.add_argument('--local', action='store_true', default=False,
                        help='Tells the script to use a local Intu instance')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-t', '--text', type=str,
                       help='Text to write to the blackboard')
    group.add_argument('-s', '--say', type=str,
                       help='Text to have the robot say')
    args, unknown_args = parser.parse_known_args(sys.argv[1:])
    #nasa_common_logging.configure_common_logging(level=args.loglevel)

    if args.say:
        bi = BlackboardInterface(args.local, args.say, True)
    else:
        bi = BlackboardInterface(args.local, args.text, False)

    bi.run()

Place this in the directory /clients/self-python-sdk/, and run as follows:

$ python write_blackboard.py -t "what time is it now"
$ python write_blackboard.py -s "hello there"

If you want see diff, here is it.

$ diff write_to_blackboard.py.orig write_to_blackboard.py
6,7d5
< sys.path.insert(0, "/home/robonaut/self/clients/self-python-sdk/self/")
< sys.path.insert(0, "/home/robonaut/self/clients/self-python-sdk/")
9,11c7,9
< from blackboard.blackboard import Blackboard
< from blackboard.thing import Thing
< from topics.topic_client import TopicClient
---
> from self.blackboard.blackboard import Blackboard
> from self.blackboard.thing import Thing
> from self.topics.topic_client import TopicClient
56c54
<         Blackboard.get_instance().add_thing(thing, '{}')
---
>         Blackboard.get_instance().add_thing(thing, '')
71c69
<         Blackboard.get_instance().add_thing(thing, '{}')
---
>         Blackboard.get_instance().add_thing(thing, '')
ias-robotics commented 6 years ago

@mhew49680 here, on a different account :)

When calling the Python Script as described - no connection (onConnect) is taking place to the Blackboard. I have rebuilt self with and without plugins and all I am getting from the script is:

python write_test_to_blackboard.py -t "1-2-3-4-5-6-7-8-9-10 coffee cup"
('hostname: %s, port: %d', '127.0.0.1', 9443)
TopicClient Instantiated!
Topic Client is Instantiated!

Then it just sits there, as there is no onConnect command taking place to send the information to the Blackboard.

ias-robotics commented 6 years ago

With the exact code takaomoriyama posted we are not getting the text to post to conversation or recognised by intu. In intu we get the following message(s):

[08/08/17 16:44:33.562][STAT][TopicManager] Added connection 1
[08/08/17 16:44:33.562][STAT][TopicManager] Skipping authentication for connection 1
[08/08/17 16:44:33.562][STAT][TopicManager] Connection 1 authenticated, SelfId: ManualAddtoBlackboard.ForGStT
[08/08/17 16:44:33.563][ERRO][Connection] OnReadWS() error: End of file
[08/08/17 16:44:35.493][STAT][SelfInstance] Local config saved to ./config.json.
[08/08/17 16:44:35.493][STAT][DiscoveryAgent] OnDiscovered instance e180caa9-4b52-7f1a-81d9-6984aa9fcd6b, IP: 192.168.100.31, 1 instances

And the output from the command line is as follows:

python write_text_to_blackboard.py -t "fred is here"
('hostname: %s, port: %d', '127.0.0.1', 9443)
TopicClient Instantiated!
Topic Client is instantiated!
Connected!
[Sending Say/Text:] Connected to Self
('[Sending text:] %s', 'fred is here')
Closed down 1006 Going away
ias-robotics commented 6 years ago

Any update on this, we really need it to work and it is holding us up

takaomoriyama commented 6 years ago

Hi, Looking into your log file, your Python SDK is successfully connected to Intu core (written in C++), and Python code is trying to put the "fred is here" to the blackboard in Python SDK. Probably the the problem resides between blackboard on Python side and on Intu core side, or in Intu core itself.

Can you confirm Conversation is working on Intu ?

Let me show you again my execution log on my MacBook. Please try exactly same sequence, and let me know the result and log file.

$ cd workdir
$ git clone --recursive git@github.com:watson-intu/self.git
$ cd self
# Confirm submodules have been properly downloaded
$ git submodule
 e972eceebd53a4b3f0fc148a4297215c83ed5024 clients/self-java-sdk (heads/develop)
 087213b5b67cdf3be27617a88c5e770fa6e24f38 clients/self-javascript-sdk (remotes/origin/RC5)
 a0bb8b753f38e3a47bd0af7e3931e59e8f3fe134 clients/self-python-sdk (heads/develop)
 4cafa61f8804b5c1647993b962a12c2aa8d93d77 clients/self-unity-sdk (heads/develop)
 94f376527082cfd25c225b2f99a5304d6955576f docs (heads/develop)
 5fa402b0280be57de78d1a304d62572a9d809be7 lib/cpp-sdk (0.0.1-542-g5fa402b0)
 c27fcb40f6ddee4695ce0b2a571cb609c3ede533 plugins (remotes/origin/RC5)
$ ./scripts/build_mac.sh
$ cd bin/mac
$ ./run_self.sh
# Go to Intu dashboard and input credentials for Watson STT, TTS, and Conversation. Also update workspace ID for Conversation self_dialog.

Can you please confirm Conversation is working properly before moving into Python SDK as follows ?

I said: What is your name ?
Intu said: My name is Willo, pleased to meet you.

Then on another terminal,

$ cd workdir/self/clients/self-python-sdk
$ cp somewhere/write_test_to_blackboard.py .
$ python write_test_to_blackboard.py -t "1-2-3-4-5-6-7-8-9-10 coffee cup"
(Intu replies as follows)
    hostname: 127.0.0.1, port: 9443
    TopicClient Instantiated!
    Topic Client is instantiated!
    Connected!
    [Sending Say/Text:] Connected to Self
    [Sending text:] 1-2-3-4-5-6-7-8-9-10 coffee cup
    Closed down 1006 Going away
(Intu says as follows) I don't know the answer to 1-2-3-4-5-6-7-8-9-10 coffee cup

Here is Intu log file.

[09/06/17 15:49:31.865][STAT][Conversation] ----- Recognized Text: what's your name  (0.945/0.46065), Language: en-US, TextId: 0x7f9019841800
[09/06/17 15:49:32.572][STAT][Conversation] ---- Intent class QuestionIntent Created, intent: dialog, confidence: 1 / 0.7, text: what's your name, TextId: 0x7f9019841800
[09/06/17 15:49:32.572][STAT][Conversation] Speaking Dialog: My name is [name], pleased to meet you. , TextId: 0x7f9019841800
[09/06/17 15:49:36.126][ERRO][Connection] OnSent() error: Broken pipe
[09/06/17 15:49:36.127][ERRO][TopicManager] Error on connection 5, SelfId: ManualAddtoBlackboard.ForGStT, removing...
[09/06/17 15:49:36.127][STAT][TopicManager] Removing connection 5, SelfId: ManualAddtoBlackboard.ForGStT
[09/06/17 15:49:38.758][STAT][TopicManager] Added connection 6
[09/06/17 15:49:38.758][STAT][TopicManager] Skipping authentication for connection 6
[09/06/17 15:49:38.758][STAT][TopicManager] Connection 6 authenticated, SelfId: ManualAddtoBlackboard.ForGStT
[09/06/17 15:49:38.759][ERRO][Connection] OnReadWS() error: End of file
[09/06/17 15:49:39.607][STAT][Conversation] ---- Intent class QuestionIntent Created, intent: question, confidence: 1 / 0.7, text: 1-2-3-4-5-6-7-8-9-10 coffee cup, TextId: 0x7f90175f4630
[09/06/17 15:49:39.899][ERRO][WebClientT] Failed send, URL: http://cap-sg-prd-1.integration.ibmcloud.com:15517/WatsonApp/deepqa/v1/question/
[09/06/17 15:49:39.899][ERRO][Request] Request failed to connect.
[09/06/17 15:49:39.900][ERRO][QuestionAgent] NULL or improperly formatted JSON returned
[09/06/17 15:49:39.900][STAT][Conversation] Speaking Dialog: I don't know the answer to 1-2-3-4-5-6-7-8-9-10 coffee cup, TextId: 0x7f90175f4630

The following is latest version of my write_test_to_blackboard.py which include some more fixes.

#!/usr/bin/env python
import argparse
import sys
import thread
import socket

from self.blackboard.blackboard import Blackboard
from self.blackboard.thing import Thing
from self.topics.topic_client import TopicClient
#import nasa_common_logging
#from taskforce_plugin_self.tf_topic_client import TaskForceTopicClient
#from taskforce_plugin_self.blackboard_interface import BlackboardInterface

class BlackboardInterface(object):

    def __init__(self, local, text_in, is_say):
        """Constructor

        Sets up logging, gets the IBM self configuration, and sets
        the user text desired to be written to the blackboard.

        Args:
            local: bool, when true, uses a local Intu instance instead
                of the built in assumption of 'captain'
            text: string with text to write to the blackboard
        """
        #self.logger = logging.getLogger(
        #    'taskforce_plugin_self.blackboard_interface')
        #self.selfConfig = SelfUtils.get_self_config()
        self.text = text_in
        self.is_say = is_say
        if local:
            self.hostname = 'localhost'
        else:
            self.hostname = '127.0.0.1'
        self.hostname = '127.0.0.1'
        self.port = 9443

        print('hostname: %s, port: %d' %
                         (self.hostname, self.port))

    def write(self):
        """Do the actual write to the blackboard

        Creates the Thing object which contains the user text, then
        writes that object to the blackboard and then exits the script.
        """
        thing = Thing()
        thing.set_type('Text')
        textToAdd = {}
        textToAdd['m_Text'] = self.text
        thing.set_body(textToAdd)
        print('[Sending text:] %s' % self.text)
        Blackboard.get_instance().add_thing(thing, '')
        thread.exit()

    def say(self):
        """Writes a Say object to the blackboard

        Creates a Thing object which contains text to have Intu speak
        out loud.
        """
        thing = Thing()
        thing.set_type('Say')
        textToAdd = {}
        textToAdd['m_Text'] = self.text
        thing.set_body(textToAdd)
        print('[Sending Say:] %s' % self.text)
        Blackboard.get_instance().add_thing(thing, '')
        thread.exit()

    def on_connected(self):
        """Callback when topic client connects

        Upon successful connection to the intu instance, write the
        actual user requested text (provided in the constructor) to the blackboard
        """
        #self.logger.info('connected to self')
        print('[Sending Say/Text:] Connected to Self')
        if self.is_say:
            self.say()
        else:
            self.write()

    def run(self):
        """Initialize and connect the topic client

        Initializes the topic client, sets the connection callback, then starts it
        """
        #headers = [
        #    ('selfId', ''), ('token', '')]
        #topic = TopicClient.start_instance(
        #    self.hostname, self.port, headers)
        #TopicClient.get_instance().setHeaders('', '')
        #TopicClient.get_instance().set_callback(self.on_connected)
        #topic.start()
        try:
            headers = [('selfId', 'ManualAddtoBlackboard.ForGStT'), ('token', '')]
            topic = TopicClient.start_instance(self.hostname, self.port, headers)
            TopicClient.get_instance().setHeaders("", "")
            TopicClient.get_instance().set_callback(self.on_connected);
            topic.start()
        except KeyboardInterrupt:
            thread.exit()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Blackboard writer')
    parser.add_argument('-l', '--loglevel', type=str, choices=[
                        'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', help='Set the log level')
    parser.add_argument('--local', action='store_true', default=False,
                        help='Tells the script to use a local Intu instance')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-t', '--text', type=str,
                       help='Text to write to the blackboard')
    group.add_argument('-s', '--say', type=str,
                       help='Text to have the robot say')
    args, unknown_args = parser.parse_known_args(sys.argv[1:])
    #nasa_common_logging.configure_common_logging(level=args.loglevel)

    if args.say:
        bi = BlackboardInterface(args.local, args.say, True)
    else:
        bi = BlackboardInterface(args.local, args.text, False)

    bi.run()
ias-robotics commented 6 years ago

Is there any way we can use this blackboard code from a pluggin directory and not have to copy to python-sdk. It makes the solution inappropriate and unscalable. Please can we use this without the sdk

takaomoriyama commented 6 years ago

I hope you have successfully run the code from Python SDK directory. If you want to run it in different directory, for example self/plugins/python, how about setting PYTHONPATH environment variable to point to the Python SDK directory as follows ?

$ mkdir <workdir>/self/plugins/pythontest
$ cd <workdir>/self/plugins/pythontest
$ cp <somewhere>/write_test_to_blackboard.py .
$ export PYTHONPATH=<workdir>/self/clients/self-python-sdk
$ python write_test_to_blackboard.py -t "1-2-3-4-5-6-7-8-9-10 coffee cup"
(Intu replies as follows)
    hostname: 127.0.0.1, port: 9443
    TopicClient Instantiated!
    Topic Client is instantiated!
    Connected!
    [Sending Say/Text:] Connected to Self
    [Sending text:] 1-2-3-4-5-6-7-8-9-10 coffee cup
    Closed down 1006 Going away
(Intu says as follows) I don't know the answer to 1-2-3-4-5-6-7-8-9-10 coffee cup

Please replace \<workdir> and \<somewhere> with your actual directories.

takaomoriyama commented 6 years ago

@wrobotics did you have chance to verify the method above ?