123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- #!/usr/bin/env python
- # coding: utf8
- import argparse
- import logging
- import time
- from kalliope.core import ShellGui
- from kalliope.core import Utils
- from kalliope.core.ConfigurationManager import SettingLoader
- from kalliope.core.ConfigurationManager.BrainLoader import BrainLoader
- from kalliope.core.SignalLauncher import SignalLauncher
- from kalliope.core.Utils.RpiUtils import RpiUtils
- from flask import Flask
- from kalliope.core.RestAPI.FlaskAPI import FlaskAPI
- from ._version import version_str
- import signal
- import sys
- from kalliope.core.ResourcesManager import ResourcesManager
- from kalliope.core.SynapseLauncher import SynapseLauncher
- from kalliope.core.OrderAnalyser import OrderAnalyser
- logging.basicConfig()
- logger = logging.getLogger("kalliope")
- def signal_handler(signal, frame):
- """
- Used to catch a keyboard signal like Ctrl+C in order to kill the kalliope program.
- :param signal: signal handler
- :param frame: execution frame
- """
- print("\n")
- Utils.print_info("Ctrl+C pressed. Killing Kalliope")
- sys.exit(0)
- # actions available
- ACTION_LIST = ["start", "gui", "install", "uninstall"]
- def parse_args(args):
- """
- Parsing function
- :param args: arguments passed from the command line
- :return: return parser
- """
- # create arguments
- parser = argparse.ArgumentParser(description='Kalliope')
- parser.add_argument("action", help="[start|gui|install|uninstall]")
- parser.add_argument("--run-synapse",
- help="Name of a synapse to load surrounded by quote")
- parser.add_argument("--run-order", help="order surrounded by a quote")
- parser.add_argument("--brain-file", help="Full path of a brain file")
- parser.add_argument("--debug", action='store_true',
- help="Show debug output")
- parser.add_argument("--git-url", help="Git URL of the neuron to install")
- parser.add_argument("--neuron-name", help="Neuron name to uninstall")
- parser.add_argument("--stt-name", help="STT name to uninstall")
- parser.add_argument("--tts-name", help="TTS name to uninstall")
- parser.add_argument("--trigger-name", help="Trigger name to uninstall")
- parser.add_argument("--signal-name", help="Signal name to uninstall")
- parser.add_argument("--muted", action='store_true', help="Starts Kalliope muted")
- parser.add_argument('-v', '--version', action='version',
- version='Kalliope ' + version_str)
- # parse arguments from script parameters
- return parser.parse_args(args)
- def main():
- """Entry point of Kalliope program."""
- # parse argument. the script name is removed
- try:
- parser = parse_args(sys.argv[1:])
- except SystemExit:
- sys.exit(1)
- # check if we want debug
- configure_logging(debug=parser.debug)
- logger.debug("kalliope args: %s" % parser)
- # by default, no brain file is set.
- # Use the default one: brain.yml in the root path
- brain_file = None
- # check if user set a brain.yml file
- if parser.brain_file:
- brain_file = parser.brain_file
- # check the user provide a valid action
- if parser.action not in ACTION_LIST:
- Utils.print_warning("%s is not a recognised action\n" % parser.action)
- sys.exit(1)
- # install modules
- if parser.action == "install":
- if not parser.git_url:
- Utils.print_danger("You must specify the git url")
- sys.exit(1)
- else:
- parameters = {
- "git_url": parser.git_url
- }
- res_manager = ResourcesManager(**parameters)
- res_manager.install()
- return
- # uninstall modules
- if parser.action == "uninstall":
- if not parser.neuron_name \
- and not parser.stt_name \
- and not parser.tts_name \
- and not parser.trigger_name\
- and not parser.signal_name:
- Utils.print_danger("You must specify a module name with "
- "--neuron-name "
- "or --stt-name "
- "or --tts-name "
- "or --trigger-name "
- "or --signal-name")
- sys.exit(1)
- else:
- res_manager = ResourcesManager()
- res_manager.uninstall(neuron_name=parser.neuron_name,
- stt_name=parser.stt_name,
- tts_name=parser.tts_name,
- trigger_name=parser.trigger_name,
- signal_name=parser.signal_name)
- return
- # load the brain once
- brain_loader = BrainLoader(file_path=brain_file)
- brain = brain_loader.brain
- # load settings
- # get global configuration once
- settings_loader = SettingLoader()
- settings = settings_loader.settings
- if parser.action == "start":
- # user set a synapse to start
- if parser.run_synapse is not None:
- SynapseLauncher.start_synapse_by_name(parser.run_synapse,
- brain=brain)
- if parser.run_order is not None:
- SynapseLauncher.run_matching_synapse_from_order(parser.run_order,
- brain=brain,
- settings=settings,
- is_api_call=False)
- if (parser.run_synapse is None) and (parser.run_order is None):
- # if --muted
- if parser.muted:
- settings.start_options['muted'] = True
- # start rest api
- start_rest_api(settings, brain)
- start_kalliope(settings, brain)
- if parser.action == "gui":
- try:
- ShellGui(brain=brain)
- except (KeyboardInterrupt, SystemExit):
- Utils.print_info("Ctrl+C pressed. Killing Kalliope")
- sys.exit(0)
- class AppFilter(logging.Filter):
- """
- Class used to add a custom entry into the logger
- """
- def filter(self, record):
- record.app_version = "kalliope-%s" % version_str
- return True
- def configure_logging(debug=None):
- """
- Prepare log folder in current home directory.
- :param debug: If true, set the lof level to debug
- """
- logger = logging.getLogger("kalliope")
- logger.addFilter(AppFilter())
- logger.propagate = False
- syslog = logging.StreamHandler()
- syslog .setLevel(logging.DEBUG)
- formatter = logging.Formatter('%(asctime)s :: %(app_version)s :: %(message)s', "%Y-%m-%d %H:%M:%S")
- syslog .setFormatter(formatter)
- if debug:
- logger.setLevel(logging.DEBUG)
- else:
- logger.setLevel(logging.INFO)
- # add the handlers to logger
- logger.addHandler(syslog)
- logger.debug("Logger ready")
- def get_list_signal_class_to_load(brain):
- """
- Return a list of signal class name
- For all synapse, each signal type is added to a list only if the signal is not yet present in the list
- :param brain: Brain object
- :type brain: Brain
- :return: set of signal class
- """
- list_signal_class_name = set()
- for synapse in brain.synapses:
- for signal_object in synapse.signals:
- list_signal_class_name.add(signal_object.name)
- logger.debug("[Kalliope entrypoint] List of signal class to load: %s" % list_signal_class_name)
- return list_signal_class_name
- def start_rest_api(settings, brain):
- """
- Start the Rest API if asked in the user settings
- """
- # run the api if the user want it
- if settings.rest_api.active:
- Utils.print_info("Starting REST API Listening port: %s" % settings.rest_api.port)
- app = Flask(__name__)
- flask_api = FlaskAPI(app=app,
- port=settings.rest_api.port,
- brain=brain,
- allowed_cors_origin=settings.rest_api.allowed_cors_origin)
- flask_api.daemon = True
- flask_api.start()
- def start_kalliope(settings, brain):
- """
- Start all signals declared in the brain
- """
- # start kalliope
- Utils.print_success("Starting Kalliope")
- Utils.print_info("Press Ctrl+C for stopping")
- # catch signal for killing on Ctrl+C pressed
- signal.signal(signal.SIGINT, signal_handler)
- # get a list of signal class to load from declared synapse in the brain
- # this list will contain string of signal class type.
- # For example, if the brain contains multiple time the signal type "order", the list will be ["order"]
- # If the brain contains some synapse with "order" and "event", the list will be ["order", "event"]
- list_signals_class_to_load = get_list_signal_class_to_load(brain)
- # start each class name
- try:
- for signal_class_name in list_signals_class_to_load:
- signal_instance = SignalLauncher.launch_signal_class_by_name(signal_name=signal_class_name,
- settings=settings)
- if signal_instance is not None:
- signal_instance.daemon = True
- signal_instance.start()
- while True: # keep main thread alive
- time.sleep(0.1)
- except (KeyboardInterrupt, SystemExit):
- # we need to switch GPIO pin to default status if we are using a Rpi
- if settings.rpi_settings:
- Utils.print_info("GPIO cleaned")
- logger.debug("Clean GPIO")
- import RPi.GPIO as GPIO
- GPIO.cleanup()
|