This page offers an overview about what a service could be (or not) and the (detailed) steps of how to integrate it into the SIC Framework. The code for all the existing services can be found at https://bitbucket.org/socialroboticshub/processing/src/master/.
What Is (Not) a Service?
Do’s
A service is required to:
use input from a device and/or another service
do more than 1 line of code in processing
give output that is useful for another service and/or an application
Additionally, it should be reusable/generic (i.e. more than 1 specific application can make use of the service).
Do’nts
A service should only relate to the software aspect of the SIC framework. An example of what would NOT count as a service is integrating both a third-party device (e.g.: smartwatch) and the data collected from it into an application. In this case, the device should be added separately as one, and the data collection and processing should be turned into a service itself.
Summary How to Add a Service to the SIC Framework
create a new folder in https://bitbucket.org/socialroboticshub/processing/src/master/ with the name of the service
copy the certificate file https://bitbucket.org/socialroboticshub/docker/src/master/cbsr/beamforming/cert.pem from any of the other services' folders into the service’s folder
copy any additional files that the services may need into the service’s folder
create a factory file inheriting from the
CBSRFactory
in the service’s folder, and override the superclass’s methodscreate a service file inheriting from
CBSRService
in the service’s folder, and override the superclass’s methodsupdate the https://bitbucket.org/socialroboticshub/processing/src/master/deploy_to_docker.sh file in the root folder with the new service files
deploy the new service to the https://bitbucket.org/socialroboticshub/docker/src/master/ folder by running the
deploy_to_docker.sh
fileupdate the https://bitbucket.org/socialroboticshub/docker/src/master/docker-compose.yml file in the
docker
folder with the new serviceupdate the https://bitbucket.org/socialroboticshub/docker/src/master/Dockerfile.python3 file in the
docker
folder with the new service’s dependenciesupdate the topics in the constructor of the Abstract Connector from the https://bitbucket.org/socialroboticshub/connectors/src/master/python/social_interaction_cloud/ folder with the name of the new service
update the device listeners in
enable_service
in the Abstract Connector with the serviceupdate the listened to channels in
__listen
in the Abstract Connector with the servicecreate the corresponding event handler method for the service in the Abstract Connector
create the corresponding event handler method for the service in the Basic Connector
use the service in a new file
The detailed explanation of these steps with a sentiment analysis example can be found below:
Detailed How to Add a Service to the SIC Framework
create a new folder in https://bitbucket.org/socialroboticshub/processing/src/master/ with the name of the service.
sentiment_analysis
(https://bitbucket.org/socialroboticshub/docker/src/master/cbsr/sentiment/ ) will be used as the example folder and service in this casecopy the certificate file https://bitbucket.org/socialroboticshub/docker/src/master/cbsr/beamforming/cert.pem from any of the other services' folders into the
sentiment
foldercopy the https://bitbucket.org/socialroboticshub/docker/src/master/cbsr/sentiment/classifier.pickle into the
sentiment
foldercreate a https://bitbucket.org/socialroboticshub/docker/src/master/cbsr/sentiment/sentiment_factory.py file in the
sentiment
folder
from os import getcwd from cbsr.factory import CBSRfactory from nltk import download from nltk.data import path from sentiment_service import SentimentAnalysisService class SentimentAnalysisFactory(CBSRfactory): def __init__(self): super(SentimentAnalysisFactory, self).__init__() def get_connection_channel(self): return 'sentiment_analysis' def create_service(self, connect, identifier, disconnect): return SentimentAnalysisService(connect, identifier, disconnect) if __name__ == '__main__': cwd = getcwd() download('punkt', download_dir=cwd) download('omw-1.4', download_dir=cwd) download('averaged_perceptron_tagger', download_dir=cwd) download('wordnet', download_dir=cwd) path.append(cwd) sentiment_analysis_factory = SentimentAnalysisFactory() sentiment_analysis_factory.run()
5. create a https://bitbucket.org/socialroboticshub/docker/src/master/cbsr/sentiment/sentiment_service.py file in the sentiment
folder
""" This file shows an example of a sentiment service that uses the text_transcript channel result from the Dialogflow speech-to-text to get the the type of sentiment """ from pickle import load from re import sub from string import punctuation from cbsr.service import CBSRservice from nltk.stem.wordnet import WordNetLemmatizer from nltk.tag import pos_tag from nltk.tokenize import word_tokenize class SentimentAnalysisService(CBSRservice): def __init__(self, connect, identifier, disconnect): super(SentimentAnalysisService, self).__init__(connect, identifier, disconnect) with open('classifier.pickle', 'rb') as pickle: self.classifier = load(pickle) self.lemmatizer = WordNetLemmatizer() def get_device_types(self): return ['mic'] def get_channel_action_mapping(self): return {self.get_full_channel('text_transcript'): self.execute} def execute(self, message): sentence = message['data'].decode() tokens = self.remove_noise(word_tokenize(sentence)) sentiment = self.classifier.classify(dict([token, True] for token in tokens)) print(sentiment) self.publish('text_sentiment', sentiment) ...
6. update the https://bitbucket.org/socialroboticshub/processing/src/master/deploy_to_docker.sh file in the root folder with the new service files
... echo Deploying my_service... cd ../my_service cp -f {cert.pem, *.py, classifier.pickle} ../../docker/cbsr/my_service # extra files can be copied if used ...
7. deploy the new service to the https://bitbucket.org/socialroboticshub/docker/src/master/ folder by running the deploy_to_docker.sh
file
8. update the https://bitbucket.org/socialroboticshub/docker/src/master/docker-compose.yml file in the docker
folder with the new service
... # ------------------------------------------------------------ # Sentiment Analysis # ------------------------------------------------------------ sentiment_analysis: image: python3 build: context: . dockerfile: Dockerfile.python3 hostname: sentiment_analysis user: "${NEW_UID}:${NEW_GID}" env_file: - ./.env working_dir: /my_service command: python3 sentiment_factory.py volumes: - ./cbsr/mock:/sentiment:rw${MOUNT_OPTIONS} tty: true stdin_open: false networks: app_net: ipv4_address: 172.16.238.x # address has to differ from those of the already existing services' depends_on: - redis - dialogflow # - any other services the service depends on ...
9. update the https://bitbucket.org/socialroboticshub/docker/src/master/Dockerfile.python3 file in the docker
folder with the new service’s dependencies
RUN pip3 install --no-cache-dir --upgrade --prefer-binary \ redis~=4.1 \ hiredis~=2.0 \ simplejson~=3.17 \ Pillow~=9.0 \ numpy~=1.22 \ imutils~=0.5 \ [any other dependendencies] \ ...
10. update the topics
in the constructor of the Abstract Connector abstract_connector.py
from the https://bitbucket.org/socialroboticshub/connectors/src/master/python/social_interaction_cloud/ folder with the name of the new service
topics = [..., 'text_sentiment']
11. update the corresponding list of devices listeners in enable_service
in abstract_connector.py
with my_service
... ########################### # Management # ########################### def enable_service(self, name: str) -> None: ... elif ... and name == 'sentiment_analysis': for mic in self.devices[self.device_types['mic']]: pipe.publish(name, mic) ...
12. update the channels in __listen
in abstract_connector.py
with my_service
... elif channel = 'text_sentiment': self.on_text_sentiment(message=data.decode('utf-8')) ...
13. create the corresponding event handler method on_my_service
in abstract_connector.py
... ########################### # Event handlers # ########################### ... def on_text_sentiment(self, message: str) -> None: pass ...
14. create the corresponding inherited event handler method on_my_service
in basic_connector.py
... ########################### # Event handlers # ########################### ... def on_text_sentiment(self, message: str) -> None: """ :param message: the message published on the text_sentiment channel This method notifies the listeners that a new message has been posted on the text_sentiment channel; This method can be further inherited and overridden """ self.__notify_listeners('onTextSentiment', message) ...
15. use the new service by creating a new file sentiment_example.py
, overriding the on_text_sentiment
method and running it
from enum import Enum from functools import partial from social_interaction_cloud.action import ActionRunner from social_interaction_cloud.basic_connector import BasicSICConnector class SentimentConnector(BasicSICConnector): def __init__(self, server_ip: str, dialogflow_key_file: str, dialogflow_agent_id: str): super(SentimentConnector, self).__init__(server_ip, 'en-US', dialogflow_key_file, dialogflow_agent_id) self.enable_service('sentiment_analysis') self.sentiment = None def on_text_sentiment(self, sentiment: str) -> None: print(sentiment) self.sentiment = sentiment class Example: def __init__(self, server_ip: str, dialogflow_key_file: str, dialogflow_agent_id: str): self.sic = SentimentConnector(server_ip, dialogflow_key_file, dialogflow_agent_id) self.action_runner = ActionRunner(self.sic) self.recognition_manager = RecognitionManager(2) self.user_model = {} ...