class HelperDict(dict):
    """
    Diese Klasse hält die Configwerte und verhält sich dabei
    weitestgehend wie ein dict.

    Beim versuch auf einen nicht vorhandenen Key zuzugreifen
    wird eine Warning und ein stacktrace ausgegeben, und 0 zurückgeben
    um den Programmablauf möglichst nicht zu stören
    """
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)

        # Wir setzen einen Scope, wird meist ,it set_debug überschrieben
        self.debug = Scope("HelperDict")

    def set_debug(self, debug):
        """
        Setzt den debugscope

        Das ist dafür gedacht damit die Warnings möglichst aus dem scope der
        Config kommen. Wenn das nicht gemacht wird, kommen die ausgaben
        vom Scope "HelperDict"
        """
        self.debug = debug

    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError as e:
            self.debug.error(e, "Config key %s not found." % key)
            return 0
 def __start(self, debugname):
     """
     Interner Starthelfer für den Subprozess
     """
     # Reinitialisieren diverser Problematischer Bereiche
     # da sie sonst im Subprozess broken sind
     reload(bitbots.util)
     reload(bitbots.debug)
     reload(bitbots.debug.debug)
     reload(bitbots.debug)
     self.__debug = Scope("Multiprocessing.%s" %
                          self.__class__.__name__)
     self.__debug("Subprocess gestartet")
     self.debug = Scope(debugname)
     self.__client_con_reader()
     #initialisieren aller felder in __data_client die es noch nicht
     #gibt welche aber provides werden, damit beim senden schon
     #alle namen vorhanden sind, und das nicht immer getesten werden
     #muss
     for name in self.__provides:
         if name not in self.__data_client:
             self.__data_client[name] = None
             self.__client_con.send((name, None))
     while True:
         try:
             self.run()
         except Exception as e:
             if isinstance(e, KeyboardInterrupt):
                 # Keyboard interrupt wollen wir nicht fangen!
                 raise
             try:
                 self.__debug.error(e, "Subprozess has terminated, restart. ")
                 time.sleep(1)
             except Exception as e:
                 print(e)
 def test_create_sub_Scope(self):
     test = Scope('Test')
     test_sub1 = test.sub('sub1')
     self.assertEquals(test_sub1.get_name(), 'Test.sub1')
     test_sub2 = test.sub('SUB2')
     self.assertEquals(test_sub2.get_name(), 'Test.SUB2')
     test_sub3 = test_sub1.sub('bla')
     self.assertEquals(test_sub3.get_name(), 'Test.sub1.bla')
 def test_net_send_string_log(self):
     self.dbg.reset()
     debug = Scope('Test')
     debug.log("Name", "Toller Name mit Umlauten: öä")
     gevent.sleep(1)
     msg = self.dbg.get_debug_data(HOST + "::Test.Name")
     self.assertTrue(msg) # es ist keine nachricht angekommen
     self.assertEquals(msg[0], "string")
     self.assertEquals(msg[1], "Toller Name mit Umlauten: öä")
def yaml_include(loader, node):
    """Include another YAML file."""
    debug = Scope("Util.Config.yaml_loader")
    filename = loader.construct_scalar(node)
    filename = find_resource(filename + '.yaml')
    with open(filename) as include_fp:
        data = yaml.load(include_fp)
    debug.log("Include-Config '%s' geladen" % filename)
    return data
 def test_net_error_prefix(self):
     self.dbg.reset()
     debug = Scope('Test')
     try:
         raise Exception("This is a Test Exception")
     except Exception as e:
         debug.error(e, "Testprefix: ")
     gevent.sleep(1)
     msg = self.dbg.get_debug_data(HOST + "::Test.warning")
     self.assertTrue(msg) # es ist keine nachricht angekommen
     self.assertEquals(msg[0], "warning")
     # wir können hier so nur auf die 2. nachricht zugreifen (momentan traceback)
     self.assertTrue("This is a Test Exception" in msg[1]) # traceback im msg
 def __init__(self):
     self.glog = Scope("bin.Record", console_out=False)  # global logger
     super(DarwinDebugHandler, self).__init__(self)
     self.setLevel(logging.DEBUG)
     self.glog.log("Logging attached to Record-Script")
     if not __debug__:
         self.glog.warning("__debug__ is false, some messages might be optimised into nirvana!")
    def test_net_console_log(self):
        self.dbg.reset()
        debug = Scope('Test')
        debug.log('Testnachricht')
        gevent.sleep(1)
        msg = self.dbg.get_debug_data(HOST + "::Test.log")
        self.assertTrue(msg) # es ist keine nachricht angekommen
        self.assertEquals(msg[0], "log")
        self.assertEquals(msg[1], "Testnachricht")

        debug.log('Dies ist eine längere Nachricht')
        gevent.sleep(1)
        msg = self.dbg.get_debug_data(HOST + "::Test.log")
        self.assertTrue(msg) # es ist keine nachricht angekommen
        self.assertEquals(msg[0], "log")
        self.assertEquals(msg[1], "Dies ist eine längere Nachricht")
 def test_create_Scope(self):
     test = Scope('Test')
     self.assertEquals(test.get_name(), 'Test')
     self.assertTrue(test.get_console_out())
     test2 = Scope('Test', False)
     self.assertEquals(test2.get_name(), 'Test')
     self.assertFalse(test2.get_console_out())
     test3 = Scope('Test.hui')
     self.assertEquals(test3.get_name(), 'Test.hui')
class DarwinDebugHandler(logging.StreamHandler):
    """ Debug-Handler that passes Messages to the darwin debugging
    """
    def __init__(self):
        self.glog = Scope("bin.Record", console_out=False)  # global logger
        super(DarwinDebugHandler, self).__init__(self)
        self.setLevel(logging.DEBUG)
        self.glog.log("Logging attached to Record-Script")
        if not __debug__:
            self.glog.warning("__debug__ is false, some messages might be optimised into nirvana!")

    def write(self, message):
        """ Called by logger
        Implements an function required by Objects attached to a
        :class:`StreamHandler`
        :param message: Incoming Logging Message
        :type message: Should be a String
        :return: Nothing
        """
        self.glog.log(message)

    def flush(self):
        """ Called by logger
        Implements an function required by Objects attached to a
        :class:`StreamHandler`
        (This one actually does nothing, it is only here for the looks)
        :return: Nothing
        """
        pass
 def __init__(self, debug=None):
     if not debug:
         self.debug = Scope("Nice")
     else:
         self.debug = debug.sub("Nice")
     config = get_config()
     if config['nice']:
         try:
             self.nice_ok = True
             self.level = self.get_nice()
             self.__test_nice()
         except:  # behandlung um intern consistent zu bleiben
             self.nice_ok = False
             raise  # fehler weiterwerfen
     else:
         self.debug.log("Nice ist in der Config deaktiviert")
         self.nice_ok = False
    def __init__(self, requires=(), provides=()):
        """
        Initialisiert die internen Datenstrukturen

        :param requires: eine Liste der Datenfelder welche dieses Modul
            benötigt
        :type requires: list of str
        :param provides: eine Liste der Datenfelder welche dieses Modul
            breitstellt
        :type provides: list of str
        """
        self.__data_server = {}
        self.__data_client = {}
        self.__requires = requires
        self.__provides = provides
        self.__con, self.__client_con = multiprocessing.Pipe()
        self.__internal = {}
        self.__started = False
        self.__debug = Scope("Multiprocessing.%s" %
                             self.__class__.__name__)
class AbstractProcessModule(AbstractModule):
    """
    Dieses Modul sorgt dafür das erbende Module in einem eigenen
    Prozess ausgeführt werden. Dies hat einige Vor-, und Nachteile.

    Damit das Modul trotzdem mit den aktuellen Daten aus dem data
    dictonarry versorgt werden kann, wird Interprozesscommunikation
    verwendet. Dabei ist immer daran zu denken das dies durchaus etwas
    Zeit braucht bis die aktuellen Werte da sind. Außerdem sollte man
    nicht zu viel Daten versenden. Das Modul achtet selbstständig darauf
    das zu einem Schlüssel nur Wertänderungen übertragen werden. Damit
    das Modul weiß welche Datenfelder zu übertragen sind müssen diese
    im Konstruktor dieser Klasse mit angegeben werden (beim Super-
    aufruf bei der vererbung).

    .. warning::
        Datenfelder welche nicht in den requires bez. provides stehen
        werden *niemals* übertragen. Beim versuch etwas zu providen was
        nicht vereinbart ist kommt es sogar zu einem Fehler

    .. hint::
        Es sollte vermieden werden die gleichen namen zu requiren und
        providen da durch die relativ langsame übertragung sonst mit
        hoher warschienlichkeit immer der alte und neue Wert abwechselnd
        ankommen, und unendlich lange im kreis gesendet werden

    Die Daten werden wenn das Modul dran ist im :func:`update`
    ausgelesen und dann bei wertänderungen an den Subprosess gesendet.
    An gleicher stelle werden auch die vom Subprozess empfangenen Daten
    in das data dictonary geschrieben.

    .. warning::
        Bei einigen in C geschriebenen Klassen funktioniert das
        Senden nicht immer, diese können nicht als require oder provide
        genutz werden. (Objekte müssen Pickabel sein) Bei Fragen: Nils
        fragen.

    Im Subprocess können die daten mit :func:`get` ausgelesen und mit
    :func:`set` gesetzt werden.

    .. warning::
        Die Funktionen :func:`get` und :func:`set` nur im subprozess
        benutzen (alles was in :func:`run` ist oder  davon aufgeruffen
        wird) sonst kommt es
        zu einer Datenkorruption. Siehe: :func:`set_init`

    Die Methode :func:`run` wird als eingener Prozess gestartet, sollte
    also überschrieben werden wenn man ein Modul von diesem erben lässt.
    """
    def __init__(self, requires=(), provides=()):
        """
        Initialisiert die internen Datenstrukturen

        :param requires: eine Liste der Datenfelder welche dieses Modul
            benötigt
        :type requires: list of str
        :param provides: eine Liste der Datenfelder welche dieses Modul
            breitstellt
        :type provides: list of str
        """
        self.__data_server = {}
        self.__data_client = {}
        self.__requires = requires
        self.__provides = provides
        self.__con, self.__client_con = multiprocessing.Pipe()
        self.__internal = {}
        self.__started = False
        self.__debug = Scope("Multiprocessing.%s" %
                             self.__class__.__name__)

    def run(self):
        """ Dies ist die Methode welche in einem eigenen Process
        ausgeführt wird
        """
        raise NotImplementedError

    def set(self, name, value):
        """
        Setzt einen Wert im data dict

        :param name: Der name des Datenfeldes
        :param value: der zu übertragen value zu dem Namen. Achtung:
            dieser Wert muss mit pickle zu packen sein. (ist fast alles).

        Es wird darauf geachtet nur Wert änderungen zu Senden, um das
        datenvolum gering zu halten

        .. warning::
            Diese Funktion darf *nur* innerhalb des Subprozesses benutzt
            werden, sonst kommt es in der interprozesskommunikation
            zu Problemen (korrupte Daten im schlimmsten fall, mindestens
            aber kommen die werte nicht da an wo man dachte)

            Siehe :func:`set_init`

        .. warning::
            Namen die nicht im provide stehen werfen einen KeyError
        """
        #minimierung des Datenaufkommens, wenn der wert schon so ist,
        #muss er nicht neu gesendet werden
        if self.__data_client[name] != value:
            self.__data_client[name] = value
            self.__client_con.send((name, value))

    def get(self, name, default=None):
        """
        Hohlt einen Wert vom data dict

        :param name: Der name des Datenfeldes
        :param default: Der wert der zurückgegeben wird wenn noch kein
            Wert für den Namen übertragen wurde.
        :return: den Wert der unter name liegt, oder default

        default kommt normalerweise in drei Fällen zum einsatz: Wenn
        der wert für den namen durch die langsame
        Interprozesskommunikation am anfang noch nicht übertragen wurde
        oder wenn der name nicht in den requires war also niemals
        übertragen wird, oder der Wert vom sendenen Modul einfach noch
        nicht gesetzt wurde.

        Default ist standartmäßig None

        .. warning::
            Diese Funktion darf *nur* innerhalb des Subprozesses benutzt
            werden, sonst kommt es in der interprozesskommunikation
            zu Problemen (korrupte Daten im schlimmsten fall, mindestens
            aber kommen die werte nicht da an wo man dachte)

            Siehe :func:`set_init`
        """
        self.__client_con_reader()
        if name in self.__data_client:
            return self.__data_client[name]
        return default

    def set_init(self, name, value, data=None):
        """
        Setzt einen Wert im data dict.

        Diese Methode ist dafür da um Werte vor dem Starten vom
        Subprozess zu setzen. Die Werte stehen damit sofort sowohl im
        globalen data dict als auch im Subprozess (über:func:`get`) zur
        verfügung, es muss nicht die wartezeit der Kommunikation in kauf
        genommen werden.

        .. warning::
            Nur in :func:`__init__` benutzen. Alles andere würde die
            Daten nicht da abliefern wo sie hingehören

        Wenn ein data dict angegeben wir, dann wird der Wert auch sofort in
        das dict geschrieben

        :param name: Name des Keys
        :type name: String
        :param value: Der Wert
        :param data: Das globale Datadictonary (aus start(), optional)
        :type data: dict
        """
        self.__data_server[name] = value
        self.__data_client[name] = value
        if data:
            data[name] = value

    def __start(self, debugname):
        """
        Interner Starthelfer für den Subprozess
        """
        # Reinitialisieren diverser Problematischer Bereiche
        # da sie sonst im Subprozess broken sind
        reload(bitbots.util)
        reload(bitbots.debug)
        reload(bitbots.debug.debug)
        reload(bitbots.debug)
        self.__debug = Scope("Multiprocessing.%s" %
                             self.__class__.__name__)
        self.__debug("Subprocess gestartet")
        self.debug = Scope(debugname)
        self.__client_con_reader()
        #initialisieren aller felder in __data_client die es noch nicht
        #gibt welche aber provides werden, damit beim senden schon
        #alle namen vorhanden sind, und das nicht immer getesten werden
        #muss
        for name in self.__provides:
            if name not in self.__data_client:
                self.__data_client[name] = None
                self.__client_con.send((name, None))
        while True:
            try:
                self.run()
            except Exception as e:
                if isinstance(e, KeyboardInterrupt):
                    # Keyboard interrupt wollen wir nicht fangen!
                    raise
                try:
                    self.__debug.error(e, "Subprozess has terminated, restart. ")
                    time.sleep(1)
                except Exception as e:
                    print(e)
        # TODO: vernünftige fehlerbehandlung!!!



    def __client_con_reader(self):
        """
        Ließt alle daten die in der Clientseitigen Pipe warten
        und pflegt diese in das interne data dict ein, damit sie mit
        :func:`get` geholt werden können. Diese Funktion muss nicht
        explizit aufgeruffen werden, das geht automatisch
        """
        try:
            while self.__client_con.poll():
                data = self.__client_con.recv()
                #print "Client: Recived: ",data
                self.__data_client[data[0]] = data[1]
        except EOFError:
            self.__debug.warning("Clientseitige Pipe ist korrupt")
            raise SystemExit()

    def __server_con_reader(self):
        """
        Ließt alle Daten die in der Serverseitigen Pipe warten
        und pflegt diese in das interne data dict ein, damit sie im
        :func:`update` zurückgeschrieben werden können. Diese Funktion
        muss nicht explizit aufgerufen werden, :func:`update` macht das
        """
        try:
            while self.__con.poll():
                data = self.__con.recv()
                #skyprint "Server: Recived: ",data
                self.__data_server[data[0]] = data[1]
        except EOFError:
            self.__debug.warning("Serverseitige Pipe ist korrupt")
            raise SystemExit()

    def __server_con_sender(self):
        """
        Sendet alle geänderten Daten an den Subprozess
        """
        for key in self.__internal:
            # zur vermeidung unnotig gesendeter daten
            if key not in self.__data_server or \
                    self.__internal[key] != self.__data_server[key]:
                self.__con.send((key, self.__internal[key]))
                self.__data_server[key] = self.__internal[key]

    def update(self, data):
        """
        Update der normalen Updatekette der Modul-Architektur.

        Hier werden die Daten für den Subprozess eingesamelt und das
        versenden gestartet, ebenso werden die empfangenen Daten
        zurück ins data dict geschrieben
        """
        self.__server_con_reader()

        for name in self.__requires:
            if name in data:
                self.__internal[name] = data[name]

        self.__server_con_sender()

        if not self.__started:
            self.__debug("Starte Subprocess")
            process = multiprocessing.Process(target=self.__start,
                name=self.__class__.__name__, args=(self.debug.get_name(),))
            process.deamon = True
            process.start()
            self.__started = True

        for name in self.__provides:
            data[name] = self.__data_server.get(name, None)
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)

        # Wir setzen einen Scope, wird meist ,it set_debug überschrieben
        self.debug = Scope("HelperDict")
 def __init__(self):
     self.debug = Scope('MotorConnectionAnalyser')
     self.cables = {}
     self.load_connection_file()
class MotorConnectionAnalyser:
    """
    """
    def __init__(self):
        self.debug = Scope('MotorConnectionAnalyser')
        self.cables = {}
        self.load_connection_file()

    def load_connection_file(self):
        """ Lädt im idealfall die datei mit den Motorverbindungen
        """
        try:
            path = find_resource('cables.yaml')
            with open(path, 'r') as f:
                self.cables = yaml.load(f)
        except IOError:
            self.debug.warning('Konnte die Resource cables.yaml nicht finden, abbruch!')
        except yaml.YAMLError as e:
            self.debug.warning("Fehler in der cables.yaml: \n %s" % e)

    def find_connection_error(self, motors):
        """Sucht Verbindungsfehler
        anhand einer Liste nicht gefundener Motoren.

        :param motors: Liste nicht gefundener Motoren als int
        :return: Liste mit Motoren die wahrscheinlich Ausgangspunkt einer fehlerhaften verbindung sind
        """
        connected = self.cables.keys()
        connected = [motor for motor in connected if motor not in motors]

        errors = []
        try:
            for motor in motors:
                if self.cables[motor]['to'] in connected:
                    errors.append(motor)
        except KeyError as e:
            self.debug.warning("I don't know this 'motor' %s" % e)
            return 'fail'
        return errors

    def get_error_message(self, motors):
        """ Generiert einen Fehlerbericht
        in natürlicher Sprache als string, anhand einer Liste von nicht gefundenen Motoren
        durch verwendung von :py:func:`find_connection_error`
        """
        if not motors:
            return "I assume the error is in the caller of my motor-connection analysis, there was no faulty motor provided"
        if len(motors) == 1 :
            if motors[0] not in [20, 17, 18, 6, 5]:
                return "Actually, I think this is not a connection error. I am not qualified for that analysis, but I think Motor %s might have the wrong ID"
        errors = self.find_connection_error(motors)
        if errors == 'fail':
            return "Error while analysing the motor-problem, check log-file!"

        l = len(errors)
        if l is 0:
            message = "I did not find any connection-error"
        elif l is 1:
            motor1 = errors[0]
            motor2 = self.cables[motor1]['to']
            #cable_id = self.cables[motor1]['name']

            message = "I assume the error is the cable between Motor %s and %s"
            message = message % (motor1, motor2)
        elif set(errors) == set([1, 2, 7, 8, 19]):
            message = "Sorry dude, it seems my cm-730 board is not working."
        else:
            message = "I assume the following %s connection errors:\n" % len(errors)
            for motor in errors:
                motor2 = self.cables[motor]['to']
                #cable_id = self.cables[motor]['name']
                message += ("Cable between Motor %s and %s . \n" % (motor, motor2))

        return message

    def say_error_message(self, motors):
        """ Liest einen mit :py:func:`get_error_message` generierten String vor
        """
        message = self.get_error_message(motors)
        say(message)
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from bitbots.debug import Scope
from bitbots.util.resource_manager import find_animation
from bitbots.util.config import get_config

import json

import threading

CONFIG = get_config()
debug = Scope("Util.Scope")
try:
    import bitbots.motion.animation as animation
    __anmimation_module = True
except ImportError:
    # für entwickler welche sachen ohne virtualenv (meist windows) testen
    __anmimation_module = False
    debug.warning(
        "Animation Framework NICHT gefunden (bitbots.motion.animation)")
try:
    from bitbots.ipc import STATE_ANIMATION_RUNNING
except ImportError:
    # für entwickler welche sachen ohne virtualenv (meist windows) testen
    STATE_ANIMATION_RUNNING = 1
    debug.warning("IPC Implementation nicht gefunden")


def __play_animation(name, ipc, callback=None):
    """
    Spielt eine Animation ab
class Nice(object):
    """
    Diese Klasse stellt methoden bereit, um die relevanz des aktuellen
    Prozesses zu verändern.

    Wenn das runtersetzen des Nicelevels (hochsetzen der Priorität) nicht
    möglich ist wird aus gründen der sicherheit nichts getan.
    """
    def __init__(self, debug=None):
        if not debug:
            self.debug = Scope("Nice")
        else:
            self.debug = debug.sub("Nice")
        config = get_config()
        if config['nice']:
            try:
                self.nice_ok = True
                self.level = self.get_nice()
                self.__test_nice()
            except:  # behandlung um intern consistent zu bleiben
                self.nice_ok = False
                raise  # fehler weiterwerfen
        else:
            self.debug.log("Nice ist in der Config deaktiviert")
            self.nice_ok = False

    def __test_nice(self):
        """
        Testet ob das runtersetzen des nicelevels möglich ist
        """
        try:
            # wenn wir hier unterbrochen werden passieren dumme dinge
            try:
                self.set_nice(-1, True)
                self.set_nice(1, True)
                self.nice_ok = 1
            except OSError:
                # dann der lange weg
                try:
                    os.popen("sudo -n renice -n %d -p %d 2> /dev/null" %
                        (-1, multiprocessing.current_process().pid))
                except OSError:
                    #irgentwass schiefgegangen, der rest fängt es ab...
                    pass
                time.sleep(1)
                if not (self.get_nice() == -1):
                    # wir dürfen nicht.... :(
                    self.nice_ok = False
                    self.debug.warning(
                        "Nicelevel darf nicht reduziert werden, disable Nice")
                else:
                    self.nice_ok = 2
                    self.set_normal(True)
        except:
            # dann gehts nicht
            # wenn wir hier landen wird es hoffentlich meist ein
            # KeybordInterrupt sein, wenn wir es nicht auf false setzen
            # passieren unter umständen komische dinge
            self.nice_ok = False
            raise  # weitergeben des fehlers...

    def get_active(self):
        """
        :return: Ob das Modul aktiv ist.
        :return type: boolean
        """
        return self.nice_ok

    def get_nice(self):
        """
        :return: Das aktuelle Nicelevel
        :return type: int
        """
        self.level = os.nice(0)
        self.debug.log("niceines", self.level)
        return self.level

    def set_nice(self, change, silence=False):
        """
        Verändert das Nicellevel um change

        :param change: die Änderung des Nicelevels
        :type change: int
        :param silence: Supress debugmessages
        :type silence: boolean
        :return: True wenn erfolgt
        :return type: boolean

        .. hint:: Wenn :func:`get_nice` == False wird ''nichts'' getan
            (außer einer debug warning)

        """
        if self.nice_ok:
            if self.nice_ok == 1:
                self.level = os.nice(change)
            else:
                os.popen("sudo -n renice -n %d -p %d 2> /dev/null" %
                    (self.level + change,
                        multiprocessing.current_process().pid))
                time.sleep(1)
                self.get_nice()
            if not silence:
                self.debug("Set Nicelevel to %d" % self.level)
            return True
        else:
            self.debug.warning("Setzen von Nice nicht möglich")
            return False

    def set_realtime(self):
        """
        Setzt die Priorität auf "Realtime"
        """
        if self.nice_ok:
            return self.set_level(-20)
        else:
            self.debug.warning("Set (Soft-) Realtime Priorität nicht möglich!")
            return False

    def set_normal(self, silence=False):
        """
        Setzt die Prioritöt auf Normal
        """
        return self.set_level(0, silence)

    def set_level(self, level, silence=False):
        """
        Setzt das nice level auf level
        :param level: das Level auf das die Priorität gesetzt werden soll
        :type level: int
        """
        return self.set_nice((self.level - level) * (-1), silence)