예제 #1
0
 def __init__(self, parentEnvironment):
     self._parentEnvironment = parentEnvironment
     self._persistenceLayer = parentEnvironment._persistenceLayer
     self._tasks = {}
     self._signal_channel = "task_manager"
     self._signal_dispatcher = SignalDispatcher(self._signal_channel)
     self.signal_channel_prefix = "environment_"+str(self._parentEnvironment.cid)
예제 #2
0
 def test_with_basic_message(self):
     iSignalDispatcher = SignalDispatcher("test_channel")
     def signalHandler(signal=None,sender=None,realsender=None,data=None,time=None,*args,**kwargs):
         self.assertEquals(signal,"just a test message")
         self.assertEquals(sender,"test_channel")
     iSignalDispatcher.add_handler(channel="test_channel",handler=signalHandler)
     iSignalDispatcher.send_message("just a test message")
예제 #3
0
    def __init__(self,
                 pathManager=None,
                 packageCheckEnabled=False,
                 packageCheckFrequency=240):
        self._path_manager = pathManager
        self._signal_channel = "package_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "package_manager"

        pollapli_package = Package(name="Pollapli",
                                   description="core package",
                                   version="0.5.0",
                                   installed=True)
        pollapli_package.cid = uuid.UUID(
            "23b0d813-a6b2-461a-88ad-a7020ae67742")
        self._installed_packages = {pollapli_package.cid: pollapli_package}
        self._available_packages = {}

        self.package_check_frequency = packageCheckFrequency
        self.package_check_enabled = packageCheckEnabled
        self.package_list_url = "http://kaosat.net/pollapli/pollapli_packages.json"
        self._package_check = task.LoopingCall(self.refresh_packageList)

        self._addon_path = "."
        self.updates_path = "."
        self._max_download_attempts = 5
        self._downloader = DownloaderWithProgress()
예제 #4
0
    def __init__(self,
                 deviceType="",
                 connectionType="",
                 hardware_interface_klass=None,
                 logicHandlerKlass=None,
                 protocol=None,
                 options={},
                 *args,
                 **kwargs):
        self.driverType = self.__class__.__name__.lower()
        self.deviceType = deviceType
        self.connectionType = connectionType
        self.extra_params = options
        self.protocol = protocol
        self.hardware_interface_klass = hardware_interface_klass
        self.logicHandlerKlass = logicHandlerKlass

        self.deviceId = None
        """will be needed to identify a specific device, as the system does not work base on ports"""

        self._signal_dispatcher = None
        self.signal_channel_prefix = ""
        self._signal_channel = ""

        self.isConfigured = False  #when the port association has not been set
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.isConnected = False
        self.isPluggedIn = False
        self.autoConnect = False  #if autoconnect is set to true, the device will be connected as soon as a it is plugged in and detected

        self.connectionErrors = 0
        self.maxConnectionErrors = 2
        self.connectionTimeout = 4

        self.connectionMode = 1
        self.deferred = defer.Deferred()
        """just for future reference : this is not implemented but would be a declarative way to 
        define the different "configuration steps" of this driver"
        *basically a dictonary with keys beeing the connection modes, and values a list of strings
        representing the methods to call
        *would require a "validator"  of sorts (certain elements need to be mandatory : such as the
        validation/setting of device ids
        """
        configSteps = {}
        configSteps[0] = ["_handle_deviceHandshake", "_handle_deviceIdInit"]
        configSteps[1] = [
            "_handle_deviceHandshake", "_handle_deviceIdInit",
            "some_other_method"
        ]

        #just a test
        self._signal_dispatcher = SignalDispatcher("driver_manager")
        """for exposing capabilites"""
        self.endpoints = []
예제 #5
0
    def __init__(self,
                 hardware_id=None,
                 auto_connect=False,
                 max_connection_errors=2,
                 connection_timeout=4,
                 do_hanshake=False,
                 do_authentification=False):
        """
        autoconnect:if autoconnect is True,device will be connected as soon as
        it is plugged in and detected
        max_connection_errors: the number of connection errors above which
        the driver gets disconnected
        connection_timeout: the number of seconds after which the driver
        gets disconnected (only in the initial , configuration phases by
        default)
        """
        BaseComponent.__init__(self, parent=None)
        self.auto_connect = auto_connect
        self.max_connection_errors = max_connection_errors
        self.connection_timeout = connection_timeout
        self.do_authentification = do_authentification
        self.do_handshake = do_hanshake
        self._hardware_interface = None
        self.hardware_id = hardware_id
        self.is_configured = False
        self.is_bound = False  # when port association has not been set
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.is_connected = False
        self.is_bound = False
        self.is_busy = False

        self.errors = []
        self.connection_mode = 1
        self._connection_errors = 0
        self._connection_timeout = None
        self.deferred = defer.Deferred()

        self._signal_channel_prefix = ""
        self._signal_dispatcher = SignalDispatcher("driver_manager")
예제 #6
0
    def __init__(self,deviceType="",connectionType="",hardware_interface_klass=None,logicHandlerKlass=None,protocol=None,options={},*args,**kwargs):    
        self.driverType=self.__class__.__name__.lower()
        self.deviceType=deviceType
        self.connectionType=connectionType
        self.extra_params=options
        self.protocol=protocol
        self.hardware_interface_klass=hardware_interface_klass
        self.logicHandlerKlass=logicHandlerKlass

        self.deviceId=None
        """will be needed to identify a specific device, as the system does not work base on ports"""
     
        self._signal_dispatcher=None 
        self.signal_channel_prefix=""
        self._signal_channel=""
        
        self.isConfigured=False#when the port association has not been set
        self.is_handshake_ok=False
        self.is_authentification_ok=False
        self.isConnected=False
        self.isPluggedIn=False
        self.autoConnect=False#if autoconnect is set to true, the device will be connected as soon as a it is plugged in and detected
        
       
        self.connectionErrors=0
        self.maxConnectionErrors=2
        self.connectionTimeout=4
         
        self.connectionMode=1
        self.deferred=defer.Deferred()
        
        """just for future reference : this is not implemented but would be a declarative way to 
        define the different "configuration steps" of this driver"
        *basically a dictonary with keys beeing the connection modes, and values a list of strings
        representing the methods to call
        *would require a "validator"  of sorts (certain elements need to be mandatory : such as the
        validation/setting of device ids
        """
        configSteps={}
        configSteps[0]=["_handle_deviceHandshake","_handle_deviceIdInit"]
        configSteps[1]=["_handle_deviceHandshake","_handle_deviceIdInit","some_other_method"]
        
        #just a test
        self._signal_dispatcher=SignalDispatcher("driver_manager")
        
        """for exposing capabilites"""
        self.endpoints=[]
예제 #7
0
 def __init__(self,
              name="base_device",
              description="base device",
              environment="Home"):
     """
     :param environment : just a tag to identify the environment this device
     belongs to
     """
     BaseDeviceComponent.__init__(self,
                                  None,
                                  name="base_device",
                                  description="base device")
     self.name = name
     self.description = description
     self.status = "inactive"
     self.environment = environment
     self.driver = None
     self._signal_channel = "device %s" % self.cid
     self._signal_dispatcher = SignalDispatcher(self._signal_channel)
예제 #8
0
    def __init__(self, pathManager=None, packageCheckEnabled=False, packageCheckFrequency = 240):
        self._path_manager = pathManager
        self._signal_channel = "package_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "package_manager"

        pollapli_package = Package(name="Pollapli", description="core package", version="0.5.0", installed=True)
        pollapli_package.cid = uuid.UUID("23b0d813-a6b2-461a-88ad-a7020ae67742")
        self._installed_packages = {pollapli_package.cid: pollapli_package}
        self._available_packages = {}

        self.package_check_frequency = packageCheckFrequency
        self.package_check_enabled = packageCheckEnabled
        self.package_list_url = "http://kaosat.net/pollapli/pollapli_packages.json"
        self._package_check = task.LoopingCall(self.refresh_packageList)

        self._addon_path = "."
        self.updates_path = "."
        self._max_download_attempts = 5
        self._downloader = DownloaderWithProgress()
예제 #9
0
    def test_with_basic_message(self):
        iSignalDispatcher = SignalDispatcher("test_channel")

        def signalHandler(signal=None,
                          sender=None,
                          realsender=None,
                          data=None,
                          time=None,
                          *args,
                          **kwargs):
            self.assertEquals(signal, "just a test message")
            self.assertEquals(sender, "test_channel")

        iSignalDispatcher.add_handler(channel="test_channel",
                                      handler=signalHandler)
        iSignalDispatcher.send_message("just a test message")
예제 #10
0
    def __init__(self, hardware_id=None, auto_connect=False,
        max_connection_errors=2, connection_timeout=4, do_hanshake=False,
        do_authentification=False):
        """
        autoconnect:if autoconnect is True,device will be connected as soon as
        it is plugged in and detected
        max_connection_errors: the number of connection errors above which
        the driver gets disconnected
        connection_timeout: the number of seconds after which the driver
        gets disconnected (only in the initial , configuration phases by
        default)
        """
        BaseComponent.__init__(self, parent=None)
        self.auto_connect = auto_connect
        self.max_connection_errors = max_connection_errors
        self.connection_timeout = connection_timeout
        self.do_authentification = do_authentification
        self.do_handshake = do_hanshake
        self._hardware_interface = None
        self.hardware_id = hardware_id
        self.is_configured = False
        self.is_bound = False  # when port association has not been set
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.is_connected = False
        self.is_bound = False
        self.is_busy = False

        self.errors = []
        self.connection_mode = 1
        self._connection_errors = 0
        self._connection_timeout = None
        self.deferred = defer.Deferred()

        self._signal_channel_prefix = ""
        self._signal_dispatcher = SignalDispatcher("driver_manager")
예제 #11
0
class DeviceManager(object):
    """
    Class for managing devices: works as a container, a handler
    and a central management point for the list of available devices
     the signal signature for device manager events is as follows:
     -for example environment_id.device_created : this is for coherence,
     signal names use underscores, while hierarchy is represented by dots)
    """

    def __init__(self, parentEnvironment):
        self._parentEnvironment = parentEnvironment
        self._persistenceLayer = parentEnvironment._persistenceLayer
        self._devices = {}
        self._signal_channel = "device_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "environment_" + str(self._parentEnvironment.cid)

    @defer.inlineCallbacks
    def setup(self):
        if self._persistenceLayer is None:
            self._persistenceLayer = self._parentEnvironment._persistenceLayer
        devices = yield self._persistenceLayer.load_devices(environmentId=self._parentEnvironment.cid)
        for device in devices:
            device._parent = self._parentEnvironment
            device._persistenceLayer = self._persistenceLayer
            self._devices[device.cid] = device
            #yield device.setup()

    def _send_signal(self, signal="", data=None):
        prefix = self.signal_channel_prefix + "."
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    """
    ###########################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the
    general handling of devices
    """

    @defer.inlineCallbacks
    def add_device(self, name="Default device", description="", device_type=None, *args, **kwargs):
        """
        Add a new device to the list of devices of the current environment
        Params:
        name: the name of the device
        Desciption: short description of device
        device_type: the device_type of the device : very important , as it
        will be used to instanciate the correct class instance
        Connector:the connector to use for this device
        Driver: the driver to use for this device's connector
        """

        device = Device(parent=self._parentEnvironment, name=name, description=description, device_type=device_type)
        yield self._persistenceLayer.save_device(device)
        self._devices[device.cid] = device
        log.msg("Added  device ", name, logLevel=logging.CRITICAL)
        self._send_signal("device_created", device)
        defer.returnValue(device)

    def get_device(self, device_id):
        if not device_id in self._devices.keys():
            raise DeviceNotFound()
        return self._devices[device_id]

    def get_devices(self, filters=None, *args, **kwargs):
        """
        Returns the list of devices, filtered by  the filter param
        the filter is a dictionary of list, with each key beeing an attribute
        to check, and the values in the list , values of that param to check
        against
        """
        deferred = defer.Deferred()

        def filter_check(device, filters):
            for key in filters.keys():
                if not getattr(device, key) in filters[key]:
                    return False
            return True

        def get(filters, device_list):
            if filter:
                return [device for device in device_list if filter_check(device, filters)]
            else:
                return device_list
        deferred.addCallback(get, self._devices.values())
        reactor.callLater(0.5, deferred.callback, filters)
        return deferred

    @defer.inlineCallbacks
    def update_device(self, device_id, name=None, description=None, *args, **kwargs):
        """Method for device update"""
        device = self._devices[device_id]
        device.name = name
        device.description = description

        yield self._persistenceLayer.save_device(device)
        self._send_signal("device_updated", device)
        log.msg("updated device :new name", name, "new descrption", description, logLevel=logging.CRITICAL)
        defer.succeed(device)

    @defer.inlineCallbacks
    def delete_device(self, device_id):
        """
        Remove a device : this needs a whole set of checks,
        as it would delete a device completely
        Params:
        device_id: the device_id of the device
        """

        device = self._devices.get(device_id, None)
        if device is None:
            raise DeviceNotFound()
        yield self._persistenceLayer.delete_device(device)
        del self._devices[device_id]
        self._send_signal("device_deleted", device)
        log.msg("Removed device ", device.name, logLevel=logging.CRITICAL)
        defer.succeed(True)

    @defer.inlineCallbacks
    def clear_devices(self):
        """
        Removes & deletes ALL the devices, should be used with care
        """
        for device in self._devices.values():
            yield self.delete_device(device.cid)
        self._send_signal("devices_cleared", self._devices)

    """
예제 #12
0
class TaskManager(object):    
    def __init__(self, parentEnvironment):
        self._parentEnvironment = parentEnvironment
        self._persistenceLayer = parentEnvironment._persistenceLayer
        self._tasks = {}
        self._signal_channel = "task_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "environment_"+str(self._parentEnvironment.cid)
    
    @defer.inlineCallbacks
    def setup(self):            
        if self._persistenceLayer is None:
            self._persistenceLayer = self._parentEnvironment._persistenceLayer        
        tasks = yield self._persistenceLayer.load_tasks(environmentId = self._parentEnvironment.cid)
        for task in tasks:
            task._parent = self._parentEnvironment
            task._persistenceLayer = self._persistenceLayer
            self._tasks[task.cid] = task
            #yield task.setup()   
        
    def _send_signal(self,signal="",data=None):
        prefix=self.signal_channel_prefix+"."
        self._signal_dispatcher.send_message(prefix+signal,self,data)
    
    """
    ####################################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the general handling of tasks
    """
 
    @defer.inlineCallbacks
    def add_task(self,name="Default Task",description="Default task description",status = "inactive",params={},*args,**kwargs):
        """
        Add a new task to the list of task of the current environment
        Params:
        name: the name of the task
        Desciption: short description of task
        """
        task = Task(parent= self._parentEnvironment, name = name, description = description, status = status)
        #yield task.setup()
        self._tasks[task.cid] = task
        yield self._persistenceLayer.save_task(task)
        log.msg("Added task named:",name ," description:",description,"with id",task.cid, system="task manager", logLevel=logging.CRITICAL)         
        self._signal_dispatcher.send_message("task.created",self,task)
        defer.returnValue(task)
    
    def get_task(self,id):
        """
        returns the task with given id
        """
        if not id in self._tasks.keys():
            raise TaskNotFound() 
        else: 
            defer.succeed(self._tasks[id])
    
    def get_tasks(self,filter=None):
        """
        Returns the list of tasks, filtered by  the filter param
        the filter is a dictionary of list, with each key beeing an attribute
        to check, and the values in the list , values of that param to check against
        """
        d=defer.Deferred()
        def filter_check(task,filter):
            for key in filter.keys():
                if not getattr(task, key) in filter[key]:
                    return False
            return True
      
        def get(filter,tasksList):
            if filter:
                return [task for task in tasksList if filter_check(task,filter)]
            else:               
                return tasksList
            
        d.addCallback(get,self._tasks.values())
        reactor.callLater(0.5,d.callback,filter)
        return d
          
    @defer.inlineCallbacks  
    def remove_task(self,id,forceStop=False):
        """Removes the task with id==id 
        Shuts down the task before removing it 
        If forcestop is true, shutdown the task even if it is running
        """
        task = self._tasks[id]
        if forceStop:
            if task.isRunning:
                    task.shutdown()
        yield self._persistenceLayer.delete_task(task) 
        del self._tasks[id]
        self._send_signal("task_deleted", task)
        log.msg("Removed task ",task.name,logLevel=logging.CRITICAL)      
       
    @defer.inlineCallbacks
    def clear_tasks(self,forceStop=False):
        """Clears the task list completely 
        Params: forceStop: if set to true, the current running task gets stopped and removed aswell
        """
        for device in self._tasks.values():
                yield self.remove_task(device.id)  
        self._send_signal("devices_cleared", self._tasks)   
       
    """
    ####################################################################################
    The following are the methods for the more detailed manipulation of tasks
    """ 
    def add_conditionToTask(self, id, condition):
        pass
    
    def add_actionToTask(self, id, action):
        pass
예제 #13
0
class Driver(object):
    """
    Driver class: higher level handler of device connection that formats outgoing and incoming commands
     according to a spec before they get sent to the lower level connector.
     It actually mimics the way system device drivers work in a way.
     You can think of the events beeing sent out by the driver (dataRecieved etc) as interupts of sorts
     
      ConnectionModes :
        0:setup
        1:normal
        2:setId
        3:forced: to forcefully connect devices which have no deviceId stored 
     
    Thoughts for future evolution
    each driver will have a series of endpoints or slots/hooks, which represent the actual subdevices it handles
    for example for reprap type devices, there is a "position" endpoint (abstract), 3 endpoints for the 
    cartesian bot motors , at least an endpoint for head temperature , one for the heater etc
    or this could be in a hiearchy , reflecting the one off the nodes:
    variable endpoint : position, and sub ones for motors
    """
    def __init__(self,
                 deviceType="",
                 connectionType="",
                 hardware_interface_klass=None,
                 logicHandlerKlass=None,
                 protocol=None,
                 options={},
                 *args,
                 **kwargs):
        self.driverType = self.__class__.__name__.lower()
        self.deviceType = deviceType
        self.connectionType = connectionType
        self.extra_params = options
        self.protocol = protocol
        self.hardware_interface_klass = hardware_interface_klass
        self.logicHandlerKlass = logicHandlerKlass

        self.deviceId = None
        """will be needed to identify a specific device, as the system does not work base on ports"""

        self._signal_dispatcher = None
        self.signal_channel_prefix = ""
        self._signal_channel = ""

        self.isConfigured = False  #when the port association has not been set
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.isConnected = False
        self.isPluggedIn = False
        self.autoConnect = False  #if autoconnect is set to true, the device will be connected as soon as a it is plugged in and detected

        self.connectionErrors = 0
        self.maxConnectionErrors = 2
        self.connectionTimeout = 4

        self.connectionMode = 1
        self.deferred = defer.Deferred()
        """just for future reference : this is not implemented but would be a declarative way to 
        define the different "configuration steps" of this driver"
        *basically a dictonary with keys beeing the connection modes, and values a list of strings
        representing the methods to call
        *would require a "validator"  of sorts (certain elements need to be mandatory : such as the
        validation/setting of device ids
        """
        configSteps = {}
        configSteps[0] = ["_handle_deviceHandshake", "_handle_deviceIdInit"]
        configSteps[1] = [
            "_handle_deviceHandshake", "_handle_deviceIdInit",
            "some_other_method"
        ]

        #just a test
        self._signal_dispatcher = SignalDispatcher("driver_manager")
        """for exposing capabilites"""
        self.endpoints = []

    def afterInit(self):
        """this is a workaround needed when loading a driver from db"""
        try:
            if not isinstance(self.extra_params, dict):
                self.extra_params = ast.literal_eval(self.extra_params)
        except Exception as inst:
            log.msg("Failed to load driver extra_params from db:",
                    inst,
                    system="Driver",
                    logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def setup(self, *args, **kwargs):
        self.hardwareHandler = self.hardware_interface_klass(
            self, self.protocol, **self.extra_params)
        self.logicHandler = self.logicHandlerKlass(self, **self.extra_params)

        node = (yield self.node.get())
        env = (yield node.environment.get())
        self.signal_channel_prefix = "environment_" + str(
            env.id) + ".node_" + str(node.id)

        self._signal_dispatcher.add_handler(handler=self.send_command,
                                            signal="addCommand")
        log.msg("Driver of type",
                self.driverType,
                "setup sucessfully",
                system="Driver",
                logLevel=logging.INFO)

    def bind(self, port, setId=True):
        self.deferred = defer.Deferred()
        log.msg("Attemtping to bind driver",
                self,
                "with deviceId:",
                self.deviceId,
                "to port",
                port,
                system="Driver",
                logLevel=logging.DEBUG)
        self.hardwareHandler.connect(setIdMode=setId, port=port)
        return self.deferred

    def connect(self, mode=None, *args, **kwargs):
        if not self.isConnected:
            if mode is not None:
                self.connectionMode = mode
                log.msg("Connecting in mode:",
                        self.connectionMode,
                        system="Driver",
                        logLevel=logging.CRITICAL)
                if mode == 3:
                    """special case for forced connection"""
                    unboundPorts = DriverManager.bindings.get_unbound_ports()
                    if len(unboundPorts) > 0:
                        port = unboundPorts[0]
                        log.msg("Connecting in mode:",
                                self.connectionMode,
                                "to port",
                                port,
                                system="Driver",
                                logLevel=logging.CRITICAL)
                        DriverManager.bindings.bind(self, port)
                        self.pluggedIn(port)
                        self.hardwareHandler.connect(port=port)

                else:
                    self.hardwareHandler.connect()
            else:
                self.hardwareHandler.connect()

    def reconnect(self, *args, **kwargs):
        self.hardwareHandler.reconnect(*args, **kwargs)

    def disconnect(self, *args, **kwargs):
        self.hardwareHandler.disconnect(*args, **kwargs)

    def pluggedIn(self, port):
        self._send_signal("plugged_In", port)
        self.isPluggedIn = True
        if self.autoConnect:
            #slight delay, to prevent certain problems when trying to send data to the device too fast
            reactor.callLater(1, self.connect, 1)

    def pluggedOut(self, port):
        self.isConfigured = False
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.isConnected = False
        self.isPluggedIn = False
        self._send_signal("plugged_Out", port)
        #self._signal_dispatcher.send_message("pluggedOut",{"data":port})

    def _send_signal(self, signal="", data=None):
        prefix = self.signal_channel_prefix + ".driver."
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    def send_command(self, data, sender=None, callback=None, *args, **kwargs):
        # print("going to send command",data,"from",sender)
        if not self.isConnected:
            raise DeviceNotConnected()
        if self.logicHandler:
            self.logicHandler._handle_request(data=data,
                                              sender=sender,
                                              callback=callback)

    def _send_data(self, data, *arrgs, **kwargs):
        self.hardwareHandler.send_data(data)

    def _handle_response(self, data):
        if self.logicHandler:
            self.logicHandler._handle_response(data)

    """higher level methods"""

    def startup(self):
        pass

    def shutdown(self):
        pass

    def init(self):
        pass

    def get_firmware_version(self):
        pass

    def set_debug_level(self, level):
        pass

    def teststuff(self, params, *args, **kwargs):
        pass

    def variable_set(self, variable, params, sender=None, *args, **kwargs):
        pass

    def variable_get(self, variable, params, sender=None, *args, **kwargs):
        pass

    """
    ####################################################################################
                                Experimental
    """

    def start_command(self):
        pass

    def close_command(self):
        pass

    def get_endpoint(self, filter=None):
        """return a list of endpoints, filtered by parameters"""
        d = defer.Deferred()

        def filter_check(endpoint, filter):
            for key in filter.keys():
                if not getattr(endpoint, key) in filter[key]:
                    return False
            return True

        def get(filter):
            if filter:
                return [
                    endpoint for endpoint in self.endpoints
                    if filter_check(endpoint, filter)
                ]
            else:
                pass

        d.addCallback(get)
        reactor.callLater(0.5, d.callback, filter)
        return d
예제 #14
0
class DeviceManager(object):
    """
    Class for managing devices: works as a container, a handler
    and a central management point for the list of available devices
     the signal signature for device manager events is as follows:
     -for example environment_id.device_created : this is for coherence,
     signal names use underscores, while hierarchy is represented by dots)
    """
    def __init__(self, parentEnvironment):
        self._parentEnvironment = parentEnvironment
        self._persistenceLayer = parentEnvironment._persistenceLayer
        self._devices = {}
        self._signal_channel = "device_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "environment_" + str(
            self._parentEnvironment.cid)

    @defer.inlineCallbacks
    def setup(self):
        if self._persistenceLayer is None:
            self._persistenceLayer = self._parentEnvironment._persistenceLayer
        devices = yield self._persistenceLayer.load_devices(
            environmentId=self._parentEnvironment.cid)
        for device in devices:
            device._parent = self._parentEnvironment
            device._persistenceLayer = self._persistenceLayer
            self._devices[device.cid] = device
            #yield device.setup()

    def _send_signal(self, signal="", data=None):
        prefix = self.signal_channel_prefix + "."
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    """
    ###########################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the
    general handling of devices
    """

    @defer.inlineCallbacks
    def add_device(self,
                   name="Default device",
                   description="",
                   device_type=None,
                   *args,
                   **kwargs):
        """
        Add a new device to the list of devices of the current environment
        Params:
        name: the name of the device
        Desciption: short description of device
        device_type: the device_type of the device : very important , as it
        will be used to instanciate the correct class instance
        Connector:the connector to use for this device
        Driver: the driver to use for this device's connector
        """

        device = Device(parent=self._parentEnvironment,
                        name=name,
                        description=description,
                        device_type=device_type)
        yield self._persistenceLayer.save_device(device)
        self._devices[device.cid] = device
        log.msg("Added  device ", name, logLevel=logging.CRITICAL)
        self._send_signal("device_created", device)
        defer.returnValue(device)

    def get_device(self, device_id):
        if not device_id in self._devices.keys():
            raise DeviceNotFound()
        return self._devices[device_id]

    def get_devices(self, filters=None, *args, **kwargs):
        """
        Returns the list of devices, filtered by  the filter param
        the filter is a dictionary of list, with each key beeing an attribute
        to check, and the values in the list , values of that param to check
        against
        """
        deferred = defer.Deferred()

        def filter_check(device, filters):
            for key in filters.keys():
                if not getattr(device, key) in filters[key]:
                    return False
            return True

        def get(filters, device_list):
            if filter:
                return [
                    device for device in device_list
                    if filter_check(device, filters)
                ]
            else:
                return device_list

        deferred.addCallback(get, self._devices.values())
        reactor.callLater(0.5, deferred.callback, filters)
        return deferred

    @defer.inlineCallbacks
    def update_device(self,
                      device_id,
                      name=None,
                      description=None,
                      *args,
                      **kwargs):
        """Method for device update"""
        device = self._devices[device_id]
        device.name = name
        device.description = description

        yield self._persistenceLayer.save_device(device)
        self._send_signal("device_updated", device)
        log.msg("updated device :new name",
                name,
                "new descrption",
                description,
                logLevel=logging.CRITICAL)
        defer.succeed(device)

    @defer.inlineCallbacks
    def delete_device(self, device_id):
        """
        Remove a device : this needs a whole set of checks,
        as it would delete a device completely
        Params:
        device_id: the device_id of the device
        """

        device = self._devices.get(device_id, None)
        if device is None:
            raise DeviceNotFound()
        yield self._persistenceLayer.delete_device(device)
        del self._devices[device_id]
        self._send_signal("device_deleted", device)
        log.msg("Removed device ", device.name, logLevel=logging.CRITICAL)
        defer.succeed(True)

    @defer.inlineCallbacks
    def clear_devices(self):
        """
        Removes & deletes ALL the devices, should be used with care
        """
        for device in self._devices.values():
            yield self.delete_device(device.cid)
        self._send_signal("devices_cleared", self._devices)

    """
예제 #15
0
class PackageManager(object):
    """
    Class for managing updates/addons: works as a container, a handler
    and a central management point for the list of available and installed addons/updates
    """
    def __init__(self, pathManager=None, packageCheckEnabled=False, packageCheckFrequency = 240):
        self._path_manager = pathManager
        self._signal_channel = "package_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "package_manager"

        pollapli_package = Package(name="Pollapli", description="core package", version="0.5.0", installed=True)
        pollapli_package.cid = uuid.UUID("23b0d813-a6b2-461a-88ad-a7020ae67742")
        self._installed_packages = {pollapli_package.cid: pollapli_package}
        self._available_packages = {}

        self.package_check_frequency = packageCheckFrequency
        self.package_check_enabled = packageCheckEnabled
        self.package_list_url = "http://kaosat.net/pollapli/pollapli_packages.json"
        self._package_check = task.LoopingCall(self.refresh_packageList)

        self._addon_path = "."
        self.updates_path = "."
        self._max_download_attempts = 5
        self._downloader = DownloaderWithProgress()

    @defer.inlineCallbacks
    def setup(self):
        """initial configuration method :
        * first it checks for locally copied addons and extracts/installs them
        * then tries to fetch the list of available packages from the update
        server
        * if any new (newer version number or never seen before) update is
        available, it adds it to the dictionary
        of available updates, but it does NOT download or install those updates
        """
        if self.package_check_enabled:
            self._package_check.start(interval=self.package_check_frequency, now=False)
        yield self._list_localPackages()
        #        updates = yield self._persistenceLayer.load_updates()
#        for update in updates:
#            self._updates[update.name]=update
        log.msg("Package Manager setup succesfully ", system="Package Manager", logLevel=logging.INFO)

    def tearDown(self):
        pass

    def _send_signal(self, signal="", data=None):
        prefix = "%s." % self.signal_channel_prefix
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    def enable_package_checking(self):
        if not self.package_check_enabled:
            self._package_check.start(interval=self.package_check_frequency, now=False)
            self.package_check_enabled = True

    def disable_package_checking(self):
        if self.package_check_enabled:
            self._package_check.stop()
            self.package_check_enabled = False

    """
    ####################################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the general handling of updates/addons
    """

    def get_package(self, id=None):
        package = self._installed_packages.get(id)
        if package is None:
            raise UnknownPackage()
        return package

    def get_packages(self, filters=None):
        """
        Returns the list of packages, filtered by  the filter param
        the filter is a dictionary of list, with each key beeing an attribute
        to check, and the values in the list , values of that param to check against
        """
        deferred = defer.Deferred()

        def filter_check(package, filters):
            for key in filters.keys():
                if not getattr(package, key) in filters[key]:
                    return False
            return True

        @defer.inlineCallbacks
        def _get_packages(filters, package_list):
            yield self.refresh_packageList()
            if filter:
                defer.returnValue([package for package in package_list if filter_check(package, filters)])
            else:
                defer.returnValue(package_list)

        deferred.addCallback(_get_packages, self._installed_packages.values())
        reactor.callLater(0.5, deferred.callback, filters)
        return deferred

    def delete_package(self, package_id):
        """
        Remove an package : this needs a whole set of checks,
        as it would delete an package completely
        Params:
        id: the id of the package
        """
        deferred = defer.Deferred()

        def remove(package_id, packages):
            package = packages[id]
            if package.type == "package":
                raise Exception("only addons can be removed")
            packages[id].delete()
            del packages[id]
            log.msg("Removed addOn ", package.name, "with id ", package_id, logLevel=logging.CRITICAL)
        deferred.addCallback(remove, self._installed_packages)
        reactor.callLater(0, deferred.callback, id)
        return deferred

    @defer.inlineCallbacks
    def clear_packages(self):
        """
        Removes & deletes ALL the packages that are addons "simple" packages cannot be deleted
        This should be used with care,as well as checks on client side
        """
        for package_id in self._installed_packages.keys():
            yield self.delete_package(package_id=package_id)

    @defer.inlineCallbacks
    def get_plugins(self, interface=None, addOnName=None):
        """
        find a specific plugin in the list of available addOns, by interface and/or addOn
        """
        plugins = []

        @defer.inlineCallbacks
        def scan(path):
            plugins = []
            try:
                addonpackages = pkgutil.walk_packages(path=[path], prefix='')
                for loader, name,isPkg in addonpackages:
                    mod = pkgutil.get_loader(name).load_module(name)
                    try:
                        plugins.extend((yield getPlugins(interface, mod)))
                    except Exception as inst:
                        log.msg("error in fetching plugin: ", str(inst), system="Package Manager", logLevel=logging.CRITICAL)
            except Exception as inst:
                log.msg("error %s in listing packages in path : %s " % (str(inst), path), system="Package Manager", logLevel=logging.CRITICAL)
            defer.returnValue(plugins)

        for addOn in self._installed_packages.itervalues():
            if addOn.type == "addon":
                if addOnName:
                    if addOn.name == addOnName and addOn.enabled:
                        plugins.extend((yield scan(addOn.installPath)))
                else:
                    if  addOn.enabled:
                        plugins.extend((yield scan(addOn.installPath)))
        log.msg("Got plugins: ", str(plugins), system="Package Manager", logLevel=logging.DEBUG)
        defer.returnValue(plugins)

    def enable_addon(self, id=None):
        package = self._installed_packages.get(id)
        if package is None:
            raise UnknownPackage()
        package.enabled = True

    def disable_addon(self, id=None):
        package = self._installed_packages.get(id)
        if package is None:
            raise UnknownPackage() 
        package.enabled = False

    """
    ####################################################################################
    Package download and installation methods
    """

    @defer.inlineCallbacks
    def refresh_packageList(self):
        """Fetches the remote package list, downloads it, and if there were any changes, packages
        the in memory package list accordingly
        """
        log.msg("checking for new packages : time", time.time(), logLevel=logging.CRITICAL)
        packageInfoPath = os.path.join(self._path_manager.tmpPath, "packages.txt") 
        try:
            yield DownloaderWithProgress.download(url=self.package_list_url, destination=packageInfoPath)
            self._parse_packageListFile()
        except Exception as inst:
            log.msg("Failed to download package master list: error:", inst, system="Package Manager", logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def setup_package(self,id):
        try:
            yield self._downloadPackage(id)
            yield self.installPackage(id)
        except Exception as inst:
            log.msg("Failed to setup package ", id ,"because of error", inst, system="Package Manager", logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def _download_package(self, id):
        """downloads the specified package, if the download was successfull, it checks the package's md5 checksum
        versus the one stored in the package info for integretiy, and if succefull, dispatches a success message
        """
        package = self._available_packages.get(id)
        if package is None:
            raise PackageNotFound()
        try:
            downloadPath = os.path.join(self._path_manager.tmpPath,package.file)  
            yield DownloaderWithProgress.download(url = package.downloadUrl, destination= downloadPath, object=package, refChecksum=package.fileHash)
            package.downloaded = True
            #update.installPath=self._addon_path
            self._send_signal("package_download_succeeded", package)
            log.msg("Successfully downloaded package ",package.name,system="Package Manager",logLevel=logging.DEBUG)
        except Exception as inst:
            self._send_signal("package_download_failed", package)
            log.msg("Failed to download package",package.name," error:",inst,system="Package Manager",logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def install_package(self,id):
        """installs the specified package"""
        package = self._available_packages.get(id)
        if package is None:
            raise PackageNotFound()
        if not package.downloaded:
            raise Exception("Cannot install package that was not downloaded first")
        """Steps: 
            extract package in tmp folder
            create list of files it is going to replace
            backup those files to a "rollback" directory
            copy the new files to the correct place
            restart the whole system
            check if all starts well
            if not, delete new files, copy back rollback files, and restart again
            if yes all done
        """
        baseName = os.path.splitext(package.file)[0]
        baseName = baseName.replace('-','_').replace('.','_')
        tmpPath = os.path.join(self._path_manager.tmpPath,baseName)
        sourcePath = os.path.join(self._path_manager.tmpPath,package.file)
        
        if package.type == "addon":
            try:
                destinationPath = os.path.join(self._path_manager._addon_path,baseName)
                
                extractedPackageDir = yield self._extract_package(sourcePath,tmpPath) 
                extractedPackageDir = os.path.join(tmpPath,extractedPackageDir)
                shutil.copytree(extractedPackageDir, destinationPath)
                shutil.rmtree(tmpPath)
                os.remove(sourcePath)
                
                self._installed_packages[package.cid] = package
                package.installPath = os.path.join(self._path_manager._addon_path,os.path.basename(extractedPackageDir))
                package.enabled = True
                self.add_package_toPythonPath(package.installPath)
                self._send_signal("addon_install_succeeded", package)
            except Exception as inst:
                raise Exception("Failed to install addOn: error %s" %(str(inst)))
            
        elif package.type == "update":
            #TODO: add restart of system 
            try:
                packageToUpdate = self._installed_packages.get(package.targetId)
                if packageToUpdate is None:
                    raise Exception("Attempting to update not installed package, aborting")
                destinationPath = packageToUpdate.installPath
                
                extractedPackagePath = yield self._extract_package(sourcePath,tmpPath)
                extractedPackagePath = os.path.join(tmpPath,extractedPackagePath)
#                yield self._backup_files(".")
                dirCompare = filecmp.dircmp(destinationPath,extractedPackagePath,hide = [])
                filesToBackup = dirCompare.common
                 
                backupFolderName = os.path.splitext(packageToUpdate.file)[0]
                backupFolderName = backupFolderName.replace('-','_').replace('.','_').replace(' ','')+"_back"
                backupFolderName = os.path.join(self._path_manager.tmpPath,backupFolderName)
                #TODO: add id at end ?
                os.makedirs(backupFolderName)
                
                for fileDir in filesToBackup:
                    fileDir = os.path.join(destinationPath,fileDir)
                    if os.path.isdir(fileDir):
                        dir_util.copy_tree(fileDir, backupFolderName)
                    else:
                        file_util.copy_file(fileDir, backupFolderName)
                        
                dir_util.copy_tree(extractedPackagePath, destinationPath) 
                shutil.rmtree(backupFolderName)  
                packageToUpdate.version = package.toVersion
                self._send_signal("update_install_succeeded", package)
            except Exception as inst:
                raise Exception("Failed to install update: error %s" %(str(inst)))
        else:
                raise Exception("Unknown package type")  
    """
    ####################################################################################
    Helper Methods    
    """
    def _list_localPackages(self):
        """
        Scans the packages path for addons etc , and adds them to the list of currently available packages
        """
        #TODO : also parse info for main (pollapli) package
        packageDirs = os.listdir(self._path_manager._addon_path)
        for fileDir in packageDirs : 
            if zipfile.is_zipfile(fileDir):
                sourcePath = fileDir
                baseName = os.path.splitext(sourcePath)[0]
                baseName = baseName.replace('-','_').replace('.','_')
                tmpPath = os.path.join(self._path_manager.tmpPath,baseName)
                
                destinationPath = os.path.join(self._path_manager._addon_path,baseName)
                extractedPackageDir = yield self._extract_package(sourcePath,tmpPath) 
                extractedPackageDir = os.path.join(sourcePath,extractedPackageDir)
                shutil.copytree(extractedPackageDir, destinationPath)
                shutil.rmtree(tmpPath)
                os.remove(sourcePath)
                fileDir = destinationPath
            self.add_package_toPythonPath(fileDir)
    
    def _parse_packageListFile(self):
        packagesFileName = os.path.join(self._path_manager.tmpPath,"pollapli_packages.json")
        packageFile = file(packagesFileName,"r")
        packageInfos = json.loads(packageFile.read(),"iso-8859-1")    
        packageFile.close()

        packageList = [Package.from_dict(package) for package in packageInfos["packages"]]   
        newPackageCount = 0
        
        for package in packageList:
            if package.type == "update":
                if package.targetId in self._installed_packages.keys():
                    if package.fromVersion == self._installed_packages[package.targetId].version:
                        if self._available_packages.get(package.cid,None) is None:
                            self._available_packages[package.cid] = package
                            newPackageCount +=1
            elif package.type == "addon":
                if package.cid not in self._installed_packages.keys():
                    if self._available_packages.get(package.cid,None) is None:
                            self._available_packages[package.cid] = package
                            newPackageCount +=1
            
        if newPackageCount>0:
            self._send_signal("new_packages_available", newPackageCount)     
    
    def _backup_packageFiles(self,rootPath):
        d = defer.Deferred()
        return d
    
    def _extract_package(self,sourcePath,destinationPath):
        d = defer.Deferred()      
        def extract(sourcePath,destinationPath):
            if not zipfile.is_zipfile(sourcePath):
                raise Exception("invalid package extension")
            packageFile = ZipFile(sourcePath, 'r')
            packageFile.extractall(path = destinationPath)
            packageFile.close()
            return os.listdir(destinationPath)[0]
            
        d.addCallback(extract,destinationPath=destinationPath)
        reactor.callLater(0,d.callback,sourcePath)
        return d
    
    @defer.inlineCallbacks
    def _extract_allPackages(self):
        """
        helper "hack" function to extract egg/zip files into their adapted directories
        """
        #TODO: fix this
        packageDirs = os.listdir(self._path_manager._addon_path)
        for fileDir in packageDirs : 
            if zipfile.is_zipfile(fileDir):
                yield self._extract_package(fileDir)
            if os.path.isdir(fileDir):
                self.add_package_toPythonPath(fileDir)
        
        #should perhaps be like this:
        #if it is a zip file, use the install_package method
        #if it is a directory, check if is in the path, and if not , add it to the path
    
    def add_package_toPythonPath(self,path):
        if os.path.isdir(path):
            if not path in sys.path:
                sys.path.insert(0, path) 

    @defer.inlineCallbacks
    def update_addOns(self):
        """
        wrapper method, for extraction+ list package in case of newly installed addons
        """
        yield self.extract_addons()
        yield self.list_addons()

    def save_package_list(self):
        for package in self._installed_packages:
            import json
            json.dumps(package)
예제 #16
0
 def __init__(self,persistenceLayer = None):
     self._persistenceLayer = persistenceLayer
     self._logger=log.PythonLoggingObserver("dobozweb.core.components.environments.environmentManager")
     self._environments = {}
     self._signal_channel="environment_manager"
     self._signal_dispatcher = SignalDispatcher(self._signal_channel)
예제 #17
0
class EnvironmentManager(object):
    """
    Class acting as a central access point for all the functionality of environments
    """
    def __init__(self,persistenceLayer = None):
        self._persistenceLayer = persistenceLayer
        self._logger=log.PythonLoggingObserver("dobozweb.core.components.environments.environmentManager")
        self._environments = {}
        self._signal_channel="environment_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        
    def __getattr__(self, attr_name):
        for env in self._environments.values():
            if hasattr(env, attr_name):
                return getattr(env, attr_name)
        raise AttributeError(attr_name)  
      
    @defer.inlineCallbacks
    def setup(self,*args,**kwargs):
        """Retrieve all existing environments from disk"""
        environments = yield self._persistenceLayer.load_environments()
        for environment in environments:
            self._environments[environment.cid] = environment
            environment._persistenceLayer = self._persistenceLayer
            yield environment.setup()
        log.msg("Environment manager setup correctly", system="environement manager", logLevel=logging.INFO)
    
    def teardown(self):
        """
        Shuts down the environment manager and everything associated with it : ie EVERYTHING !!
        Should not be called in most cases
        """
        pass
        
    def _send_signal(self, signal="", data=None):
        prefix=self._signal_channel+"."
        self._signal_dispatcher.send_message(prefix+signal,self,data)    
    """
    ####################################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the general handling of environements
    """
    @defer.inlineCallbacks
    def add_environment(self,name="Default Environment",description="Add Description here",status="inactive"):
        """
        Add an environment to the list of managed environements : 
        Automatically creates a new folder and launches the new environement auto creation
        Params:
        name: the name of the environment
        description:a short description of the environment
        status: either frozen or live : whether the environment is active or not
        """
        for environment in self._environments.values():
            if environment.name == name:
                raise EnvironmentAlreadyExists()
        environment = Environment(persistenceLayer=self._persistenceLayer, name=name,description=description,status=status)
        yield self._persistenceLayer.save_environment(environment)
        self._environments[environment.cid] = environment
        
        self._send_signal("environment.created",environment)
        log.msg("Added environment named:",name ," description:",description,"with id",environment.cid, system="environment manager", logLevel=logging.CRITICAL)         
        defer.returnValue(environment)

    def get_environments(self,filter=None):
        """
        Returns the list of environments, filtered by  the filter param
        the filter is a dictionary of list, with each key beeing an attribute
        to check, and the values in the list , values of that param to check against
        """
        d=defer.Deferred()
        
        def filter_check(env,filter):
            for key in filter.keys():
                if not getattr(env, key) in filter[key]:
                    return False
            return True
      
        def _get_envs(filter,envsList):
            if filter:
                return [env for env in envsList if filter_check(env,filter)]
            else:
                return envsList
            
        d.addCallback(_get_envs,self._environments.values())
        reactor.callLater(0,d.callback,filter)
        return d
    
    
    def get_environment(self,id, *args, **kwargs):   
        if not id in self._environments.keys():
            raise EnvironmentNotFound() 
        else:
            #raise EnvironmentNotFound() 
            #defer.succeed(self._environments[id])
            return self._environments[id]
    
    def update_environment(self,id,name,description,status):
        environment = self._environments[id]
        environment.update(name,description,status)
        self._send_signal("environment_updated", environment)
        #return self.environments[id].update(name,description,status)
    
    @defer.inlineCallbacks
    def remove_environment(self,id=None,name=None):
        """
        Remove an environment : this needs a whole set of checks, 
        as it would delete an environment completely (very dangerous)
        Params:
        name: the name of the environment
        """
        try:
            environment = self._environments[id]
            yield self._persistenceLayer.delete_environment(environment)
            #self.environments[envName].teardown()
            del self._environments[id]
            self._send_signal("environment_deleted", environment)
            log.msg("Removed environment ",environment.name, system="environment manager",logLevel=logging.CRITICAL)
        except Exception as inst:
            raise Exception("Failed to delete environment because of error %s" %str(inst))
        
#        d = defer.Deferred()
#        def remove(id,envs):
#            try:
#                environment = envs[id]
#                yield self._persistenceLayer.delete_environment(environment)
#                envPath=os.path.join(FileManager.dataPath,environment._name)
#                #self.environments[envName].teardown()
#                del envs[id]
#                if os.path.isdir(envPath): 
#                    shutil.rmtree(envPath)
#                    log.msg("Removed environment ",environment._name, system="environment manager",logLevel=logging.CRITICAL)
#            except:
#                raise Exception("Failed to delete environment")
#                #should raise  specific exception
#                
#        d.addCallback(remove,self._environments)
#        reactor.callLater(0,d.callback,id)
#        return d

        
    @defer.inlineCallbacks
    def clear_environments(self):
        """
        Removes & deletes ALL the environments, should be used with care
        """
        for envId in self._environments.keys():
            yield self.remove_environment(id = envId)    
        self._send_signal("environments_cleared", self._environments)       
    """
예제 #18
0
class Driver(object):
    """
    Driver class: higher level handler of device connection that formats outgoing and incoming commands
     according to a spec before they get sent to the lower level connector.
     It actually mimics the way system device drivers work in a way.
     You can think of the events beeing sent out by the driver (dataRecieved etc) as interupts of sorts
     
      ConnectionModes :
        0:setup
        1:normal
        2:setId
        3:forced: to forcefully connect devices which have no deviceId stored 
     
    Thoughts for future evolution
    each driver will have a series of endpoints or slots/hooks, which represent the actual subdevices it handles
    for example for reprap type devices, there is a "position" endpoint (abstract), 3 endpoints for the 
    cartesian bot motors , at least an endpoint for head temperature , one for the heater etc
    or this could be in a hiearchy , reflecting the one off the nodes:
    variable endpoint : position, and sub ones for motors
    """
    
    def __init__(self,deviceType="",connectionType="",hardware_interface_klass=None,logicHandlerKlass=None,protocol=None,options={},*args,**kwargs):    
        self.driverType=self.__class__.__name__.lower()
        self.deviceType=deviceType
        self.connectionType=connectionType
        self.extra_params=options
        self.protocol=protocol
        self.hardware_interface_klass=hardware_interface_klass
        self.logicHandlerKlass=logicHandlerKlass

        self.deviceId=None
        """will be needed to identify a specific device, as the system does not work base on ports"""
     
        self._signal_dispatcher=None 
        self.signal_channel_prefix=""
        self._signal_channel=""
        
        self.isConfigured=False#when the port association has not been set
        self.is_handshake_ok=False
        self.is_authentification_ok=False
        self.isConnected=False
        self.isPluggedIn=False
        self.autoConnect=False#if autoconnect is set to true, the device will be connected as soon as a it is plugged in and detected
        
       
        self.connectionErrors=0
        self.maxConnectionErrors=2
        self.connectionTimeout=4
         
        self.connectionMode=1
        self.deferred=defer.Deferred()
        
        """just for future reference : this is not implemented but would be a declarative way to 
        define the different "configuration steps" of this driver"
        *basically a dictonary with keys beeing the connection modes, and values a list of strings
        representing the methods to call
        *would require a "validator"  of sorts (certain elements need to be mandatory : such as the
        validation/setting of device ids
        """
        configSteps={}
        configSteps[0]=["_handle_deviceHandshake","_handle_deviceIdInit"]
        configSteps[1]=["_handle_deviceHandshake","_handle_deviceIdInit","some_other_method"]
        
        #just a test
        self._signal_dispatcher=SignalDispatcher("driver_manager")
        
        """for exposing capabilites"""
        self.endpoints=[]
        
        
    def afterInit(self):
       
        """this is a workaround needed when loading a driver from db"""
        try:
            if not isinstance(self.extra_params,dict):
                self.extra_params=ast.literal_eval(self.extra_params)
        except Exception as inst:
            log.msg("Failed to load driver extra_params from db:",inst,system="Driver",logLevel=logging.CRITICAL)

    
    @defer.inlineCallbacks    
    def setup(self,*args,**kwargs):  
        self.hardwareHandler=self.hardware_interface_klass(self,self.protocol,**self.extra_params)
        self.logicHandler=self.logicHandlerKlass(self,**self.extra_params)  
        
        
        node= (yield self.node.get())
        env= (yield node.environment.get())
        self.signal_channel_prefix="environment_"+str(env.id)+".node_"+str(node.id)

        self._signal_dispatcher.add_handler(handler=self.send_command,signal="addCommand")
        log.msg("Driver of type",self.driverType ,"setup sucessfully",system="Driver",logLevel=logging.INFO)
        
    def bind(self,port,setId=True):
        self.deferred=defer.Deferred()
        log.msg("Attemtping to bind driver",self ,"with deviceId:",self.deviceId,"to port",port,system="Driver",logLevel=logging.DEBUG) 
        self.hardwareHandler.connect(setIdMode=setId,port=port)     
        return self.deferred
    
    def connect(self,mode=None,*args,**kwargs):
        if not self.isConnected:
            if mode is not None:
                self.connectionMode=mode
                log.msg("Connecting in mode:",self.connectionMode,system="Driver",logLevel=logging.CRITICAL) 
                if mode==3:
                    """special case for forced connection"""
                    unboundPorts=DriverManager.bindings.get_unbound_ports()
                    if len(unboundPorts)>0:
                        port=unboundPorts[0]
                        log.msg("Connecting in mode:",self.connectionMode,"to port",port,system="Driver",logLevel=logging.CRITICAL)
                        DriverManager.bindings.bind(self,port)
                        self.pluggedIn(port)
                        self.hardwareHandler.connect(port=port)
                        
                else:
                    self.hardwareHandler.connect()
            else:
                self.hardwareHandler.connect()
                
    def reconnect(self,*args,**kwargs):
        self.hardwareHandler.reconnect(*args,**kwargs)
    def disconnect(self,*args,**kwargs):
        self.hardwareHandler.disconnect(*args,**kwargs)
    
    def pluggedIn(self,port):    
        self._send_signal("plugged_In",port)
        self.isPluggedIn=True
        if self.autoConnect:
            #slight delay, to prevent certain problems when trying to send data to the device too fast
            reactor.callLater(1,self.connect,1)
           
    def pluggedOut(self,port):
        self.isConfigured=False  
        self.is_handshake_ok=False
        self.is_authentification_ok=False
        self.isConnected=False
        self.isPluggedIn=False
        self._send_signal("plugged_Out",port)
        #self._signal_dispatcher.send_message("pluggedOut",{"data":port})
    
    def _send_signal(self,signal="",data=None):
        prefix=self.signal_channel_prefix+".driver."
        self._signal_dispatcher.send_message(prefix+signal,self,data)
    
    def send_command(self,data,sender=None,callback=None,*args,**kwargs):
       # print("going to send command",data,"from",sender)
        if not self.isConnected:
            raise DeviceNotConnected()
        if self.logicHandler:
            self.logicHandler._handle_request(data=data,sender=sender,callback=callback)
    
    def _send_data(self,data,*arrgs,**kwargs):
        self.hardwareHandler.send_data(data)
         
    def _handle_response(self,data):
        if self.logicHandler:
            self.logicHandler._handle_response(data)
    
    """higher level methods""" 
    def startup(self):
        pass
    def shutdown(self):
        pass
    def init(self):
        pass
    def get_firmware_version(self):
        pass
    def set_debug_level(self,level):
        pass
    
    def teststuff(self,params,*args,**kwargs):
        pass
    
    def variable_set(self,variable,params,sender=None,*args,**kwargs):
        pass
    def variable_get(self,variable,params,sender=None,*args,**kwargs):
        pass
    
    """
    ####################################################################################
                                Experimental
    """ 
    def start_command(self):
        pass
    def close_command(self):
        pass
    
    def get_endpoint(self,filter=None):
        """return a list of endpoints, filtered by parameters"""
        d=defer.Deferred()
        
        def filter_check(endpoint,filter):
            for key in filter.keys():
                if not getattr(endpoint, key) in filter[key]:
                    return False
            return True
      
        def get(filter):
            if filter:
                return [endpoint for endpoint in self.endpoints if filter_check(endpoint,filter)]
            else:               
                pass
            
        d.addCallback(get)
        reactor.callLater(0.5,d.callback,filter)
        return d
예제 #19
0
class Driver(BaseComponent):
    """
    Driver class: higher level handler of device connection that formats
    outgoing and incoming commands according to a spec before they get
    sent to the lower level connector.
     It actually mimics the way system device drivers work in a way.
     You can think of the events beeing sent out by the driver(dataRecieved...)
     as interupts of sorts
    """
    def __init__(self,
                 hardware_id=None,
                 auto_connect=False,
                 max_connection_errors=2,
                 connection_timeout=4,
                 do_hanshake=False,
                 do_authentification=False):
        """
        autoconnect:if autoconnect is True,device will be connected as soon as
        it is plugged in and detected
        max_connection_errors: the number of connection errors above which
        the driver gets disconnected
        connection_timeout: the number of seconds after which the driver
        gets disconnected (only in the initial , configuration phases by
        default)
        """
        BaseComponent.__init__(self, parent=None)
        self.auto_connect = auto_connect
        self.max_connection_errors = max_connection_errors
        self.connection_timeout = connection_timeout
        self.do_authentification = do_authentification
        self.do_handshake = do_hanshake
        self._hardware_interface = None
        self.hardware_id = hardware_id
        self.is_configured = False
        self.is_bound = False  # when port association has not been set
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.is_connected = False
        self.is_bound = False
        self.is_busy = False

        self.errors = []
        self.connection_mode = 1
        self._connection_errors = 0
        self._connection_timeout = None
        self.deferred = defer.Deferred()

        self._signal_channel_prefix = ""
        self._signal_dispatcher = SignalDispatcher("driver_manager")

    def __eq__(self, other):
        return self.__class__ == other.__class__

    def __ne__(self, other):
        return not self.__eq__(other)

    def setup(self, *args, **kwargs):
        """do the driver setup phase"""
        self._signal_dispatcher.add_handler(handler=self.send_command,
                                            signal="addCommand")
        class_name = self.__class__.__name__.lower()
        log.msg("Driver of type",
                class_name,
                "setup sucessfully",
                system="Driver",
                logLevel=logging.INFO)

    def _send_signal(self, signal="", data=None):
        prefix = self._signal_channel_prefix + ".driver."
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    @property
    def hardware_interface_class(self):
        """Get the current voltage."""
        return self._hardware_interface.__class__

    @property
    def connection_errors(self):
        """Get the current voltage."""
        return self._connection_errors

    @connection_errors.setter
    def connection_errors(self, value):
        self._connection_errors = value
        if self._connection_errors >= self.max_connection_errors:
            self.disconnect(clear_port=True)
            log.msg(
                "cricital error while (re-)starting serial connection : please check your driver settings and device id, as well as cables,  and make sure no other process is using the port ",
                system="Driver",
                logLevel=logging.CRITICAL)
            self.deferred.errback(self.errors[-1])
            #self._hardware_interface._connect()  #weird way of handling disconnect

    """
    ###########################################################################
    The following are the timeout related methods
    """

    def set_connection_timeout(self):
        """sets internal timeout"""
        if self.connection_timeout > 0:
            log.msg("Setting timeout at ",
                    time.time(),
                    system="Driver",
                    logLevel=logging.DEBUG)
            self._connection_timeout = reactor.callLater(
                self.connection_timeout, self._connection_timeout_check)

    def cancel_connection_timeout(self):
        """cancels internal timeout"""
        if self._connection_timeout is not None:
            try:
                self._connection_timeout.cancel()
                log.msg("Canceling timeout at ",
                        time.time(),
                        system="Driver",
                        logLevel=logging.DEBUG)
            except:
                pass

    def _connection_timeout_check(self):
        """checks the timeout"""
        log.msg("Timeout check at ", time.time(), logLevel=logging.DEBUG)
        self.cancel_connection_timeout()
        self.errors.append(TimeoutError())
        self.connection_errors += 1
        if self.connection_errors < self.max_connection_errors:
            self.reconnect()

    """
    ###########################################################################
    The following are the connection related methods
    """

    def connect(self, port=None, connection_mode=None):
        """
        connection_mode :
        0:setup
        1:normal
        2:forced: to forcefully connect devices which have no deviceId stored
        """
        if self.is_connected:
            raise Exception("Driver already connected")
        if connection_mode is None:
            raise Exception("Invalid connection mode")

        self.deferred = defer.Deferred()
        self.connection_mode = connection_mode
        self.errors = []
        self._connection_errors = 0
        self.is_busy = True

        mode_str = "Normal"
        if self.connection_mode == 1:
            mode_str = "Setup"
        log.msg("Connecting driver in %s mode:" % mode_str,
                system="Driver",
                logLevel=logging.CRITICAL)
        reactor.callLater(0.1, self._hardware_interface.connect, port)
        return self.deferred

    def reconnect(self, *args, **kwargs):
        """Reconnect driver"""
        self._hardware_interface.reconnect(*args, **kwargs)

    def disconnect(self, *args, **kwargs):
        """Disconnect driver"""
        log.msg("Disconnecting driver",
                system="Driver",
                logLevel=logging.CRITICAL)
        self.is_connected = False
        self.is_busy = False
        self.cancel_connection_timeout()
        self._hardware_interface.disconnect(*args, **kwargs)

    """
    ###########################################################################
    The following are the methods dealing with communication with the hardware
    """

    def send_command(self, command):
        """send a command to the physical device"""
        if not self.is_connected:
            raise DeviceNotConnected()
        self.command_deferred = defer.Deferred()
        reactor.callLater(0.01, self._hardware_interface.send_data, command)
        return self.command_deferred

    def _handle_response(self, data):
        """handle hardware response"""
        self.command_deferred.callback(data)

    """
    ###########################################################################
    The following are the higher level methods
    """

    def startup(self):
        """send startup command to hardware"""
        raise NotImplementedError()

    def shutdown(self):
        """send shutdown command to hardware"""
        raise NotImplementedError()

    def get_firmware_info(self):
        """retrieve firmware version from hardware"""
        raise NotImplementedError()

    def set_debug_level(self, level):
        """set hardware debug level, if any"""
        raise NotImplementedError()
예제 #20
0
class Driver(BaseComponent):
    """
    Driver class: higher level handler of device connection that formats
    outgoing and incoming commands according to a spec before they get
    sent to the lower level connector.
     It actually mimics the way system device drivers work in a way.
     You can think of the events beeing sent out by the driver(dataRecieved...)
     as interupts of sorts
    """
    def __init__(self, hardware_id=None, auto_connect=False,
        max_connection_errors=2, connection_timeout=4, do_hanshake=False,
        do_authentification=False):
        """
        autoconnect:if autoconnect is True,device will be connected as soon as
        it is plugged in and detected
        max_connection_errors: the number of connection errors above which
        the driver gets disconnected
        connection_timeout: the number of seconds after which the driver
        gets disconnected (only in the initial , configuration phases by
        default)
        """
        BaseComponent.__init__(self, parent=None)
        self.auto_connect = auto_connect
        self.max_connection_errors = max_connection_errors
        self.connection_timeout = connection_timeout
        self.do_authentification = do_authentification
        self.do_handshake = do_hanshake
        self._hardware_interface = None
        self.hardware_id = hardware_id
        self.is_configured = False
        self.is_bound = False  # when port association has not been set
        self.is_handshake_ok = False
        self.is_authentification_ok = False
        self.is_connected = False
        self.is_bound = False
        self.is_busy = False

        self.errors = []
        self.connection_mode = 1
        self._connection_errors = 0
        self._connection_timeout = None
        self.deferred = defer.Deferred()

        self._signal_channel_prefix = ""
        self._signal_dispatcher = SignalDispatcher("driver_manager")

    def __eq__(self, other):
        return self.__class__ == other.__class__

    def __ne__(self, other):
        return not self.__eq__(other)

    def setup(self, *args, **kwargs):
        """do the driver setup phase"""
        self._signal_dispatcher.add_handler(handler=self.send_command, signal="addCommand")
        class_name = self.__class__.__name__.lower()
        log.msg("Driver of type", class_name, "setup sucessfully", system="Driver", logLevel=logging.INFO)

    def _send_signal(self, signal="", data=None):
        prefix = self._signal_channel_prefix + ".driver."
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    @property
    def hardware_interface_class(self):
        """Get the current voltage."""
        return self._hardware_interface.__class__

    @property
    def connection_errors(self):
        """Get the current voltage."""
        return self._connection_errors

    @connection_errors.setter
    def connection_errors(self, value):
        self._connection_errors = value
        if self._connection_errors >= self.max_connection_errors:
            self.disconnect(clear_port=True)
            log.msg("cricital error while (re-)starting serial connection : please check your driver settings and device id, as well as cables,  and make sure no other process is using the port ", system="Driver", logLevel=logging.CRITICAL)
            self.deferred.errback(self.errors[-1])
            #self._hardware_interface._connect()  #weird way of handling disconnect
    """
    ###########################################################################
    The following are the timeout related methods
    """
    def set_connection_timeout(self):
        """sets internal timeout"""
        if self.connection_timeout > 0:
            log.msg("Setting timeout at ", time.time(), system="Driver", logLevel=logging.DEBUG)
            self._connection_timeout = reactor.callLater(self.connection_timeout, self._connection_timeout_check)

    def cancel_connection_timeout(self):
        """cancels internal timeout"""
        if self._connection_timeout is not None:
            try:
                self._connection_timeout.cancel()
                log.msg("Canceling timeout at ", time.time(), system="Driver", logLevel=logging.DEBUG)
            except:
                pass

    def _connection_timeout_check(self):
        """checks the timeout"""
        log.msg("Timeout check at ", time.time(), logLevel=logging.DEBUG)
        self.cancel_connection_timeout()
        self.errors.append(TimeoutError())
        self.connection_errors += 1
        if self.connection_errors < self.max_connection_errors:
            self.reconnect()

    """
    ###########################################################################
    The following are the connection related methods
    """
    def connect(self, port=None, connection_mode=None):
        """
        connection_mode :
        0:setup
        1:normal
        2:forced: to forcefully connect devices which have no deviceId stored
        """
        if self.is_connected:
            raise Exception("Driver already connected")
        if connection_mode is None:
            raise Exception("Invalid connection mode")

        self.deferred = defer.Deferred()
        self.connection_mode = connection_mode
        self.errors = []
        self._connection_errors = 0
        self.is_busy = True

        mode_str = "Normal"
        if self.connection_mode == 1:
            mode_str = "Setup"
        log.msg("Connecting driver in %s mode:" % mode_str, system="Driver", logLevel=logging.CRITICAL)
        reactor.callLater(0.1, self._hardware_interface.connect, port)
        return self.deferred

    def reconnect(self, *args, **kwargs):
        """Reconnect driver"""
        self._hardware_interface.reconnect(*args, **kwargs)

    def disconnect(self, *args, **kwargs):
        """Disconnect driver"""
        log.msg("Disconnecting driver", system="Driver", logLevel=logging.CRITICAL)
        self.is_connected = False
        self.is_busy = False
        self.cancel_connection_timeout()
        self._hardware_interface.disconnect(*args, **kwargs)

    """
    ###########################################################################
    The following are the methods dealing with communication with the hardware
    """

    def send_command(self, command):
        """send a command to the physical device"""
        if not self.is_connected:
            raise DeviceNotConnected()
        self.command_deferred = defer.Deferred()
        reactor.callLater(0.01, self._hardware_interface.send_data, command)
        return self.command_deferred

    def _handle_response(self, data):
        """handle hardware response"""
        self.command_deferred.callback(data)

    """
    ###########################################################################
    The following are the higher level methods
    """
    def startup(self):
        """send startup command to hardware"""
        raise NotImplementedError()

    def shutdown(self):
        """send shutdown command to hardware"""
        raise NotImplementedError()

    def get_firmware_info(self):
        """retrieve firmware version from hardware"""
        raise NotImplementedError()

    def set_debug_level(self, level):
        """set hardware debug level, if any"""
        raise NotImplementedError()
예제 #21
0
class PackageManager(object):
    """
    Class for managing updates/addons: works as a container, a handler
    and a central management point for the list of available and installed addons/updates
    """
    def __init__(self,
                 pathManager=None,
                 packageCheckEnabled=False,
                 packageCheckFrequency=240):
        self._path_manager = pathManager
        self._signal_channel = "package_manager"
        self._signal_dispatcher = SignalDispatcher(self._signal_channel)
        self.signal_channel_prefix = "package_manager"

        pollapli_package = Package(name="Pollapli",
                                   description="core package",
                                   version="0.5.0",
                                   installed=True)
        pollapli_package.cid = uuid.UUID(
            "23b0d813-a6b2-461a-88ad-a7020ae67742")
        self._installed_packages = {pollapli_package.cid: pollapli_package}
        self._available_packages = {}

        self.package_check_frequency = packageCheckFrequency
        self.package_check_enabled = packageCheckEnabled
        self.package_list_url = "http://kaosat.net/pollapli/pollapli_packages.json"
        self._package_check = task.LoopingCall(self.refresh_packageList)

        self._addon_path = "."
        self.updates_path = "."
        self._max_download_attempts = 5
        self._downloader = DownloaderWithProgress()

    @defer.inlineCallbacks
    def setup(self):
        """initial configuration method :
        * first it checks for locally copied addons and extracts/installs them
        * then tries to fetch the list of available packages from the update
        server
        * if any new (newer version number or never seen before) update is
        available, it adds it to the dictionary
        of available updates, but it does NOT download or install those updates
        """
        if self.package_check_enabled:
            self._package_check.start(interval=self.package_check_frequency,
                                      now=False)
        yield self._list_localPackages()
        #        updates = yield self._persistenceLayer.load_updates()
        #        for update in updates:
        #            self._updates[update.name]=update
        log.msg("Package Manager setup succesfully ",
                system="Package Manager",
                logLevel=logging.INFO)

    def tearDown(self):
        pass

    def _send_signal(self, signal="", data=None):
        prefix = "%s." % self.signal_channel_prefix
        self._signal_dispatcher.send_message(prefix + signal, self, data)

    def enable_package_checking(self):
        if not self.package_check_enabled:
            self._package_check.start(interval=self.package_check_frequency,
                                      now=False)
            self.package_check_enabled = True

    def disable_package_checking(self):
        if self.package_check_enabled:
            self._package_check.stop()
            self.package_check_enabled = False

    """
    ####################################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the general handling of updates/addons
    """

    def get_package(self, id=None):
        package = self._installed_packages.get(id)
        if package is None:
            raise UnknownPackage()
        return package

    def get_packages(self, filters=None):
        """
        Returns the list of packages, filtered by  the filter param
        the filter is a dictionary of list, with each key beeing an attribute
        to check, and the values in the list , values of that param to check against
        """
        deferred = defer.Deferred()

        def filter_check(package, filters):
            for key in filters.keys():
                if not getattr(package, key) in filters[key]:
                    return False
            return True

        @defer.inlineCallbacks
        def _get_packages(filters, package_list):
            yield self.refresh_packageList()
            if filter:
                defer.returnValue([
                    package for package in package_list
                    if filter_check(package, filters)
                ])
            else:
                defer.returnValue(package_list)

        deferred.addCallback(_get_packages, self._installed_packages.values())
        reactor.callLater(0.5, deferred.callback, filters)
        return deferred

    def delete_package(self, package_id):
        """
        Remove an package : this needs a whole set of checks,
        as it would delete an package completely
        Params:
        id: the id of the package
        """
        deferred = defer.Deferred()

        def remove(package_id, packages):
            package = packages[id]
            if package.type == "package":
                raise Exception("only addons can be removed")
            packages[id].delete()
            del packages[id]
            log.msg("Removed addOn ",
                    package.name,
                    "with id ",
                    package_id,
                    logLevel=logging.CRITICAL)

        deferred.addCallback(remove, self._installed_packages)
        reactor.callLater(0, deferred.callback, id)
        return deferred

    @defer.inlineCallbacks
    def clear_packages(self):
        """
        Removes & deletes ALL the packages that are addons "simple" packages cannot be deleted
        This should be used with care,as well as checks on client side
        """
        for package_id in self._installed_packages.keys():
            yield self.delete_package(package_id=package_id)

    @defer.inlineCallbacks
    def get_plugins(self, interface=None, addOnName=None):
        """
        find a specific plugin in the list of available addOns, by interface and/or addOn
        """
        plugins = []

        @defer.inlineCallbacks
        def scan(path):
            plugins = []
            try:
                addonpackages = pkgutil.walk_packages(path=[path], prefix='')
                for loader, name, isPkg in addonpackages:
                    mod = pkgutil.get_loader(name).load_module(name)
                    try:
                        plugins.extend((yield getPlugins(interface, mod)))
                    except Exception as inst:
                        log.msg("error in fetching plugin: ",
                                str(inst),
                                system="Package Manager",
                                logLevel=logging.CRITICAL)
            except Exception as inst:
                log.msg("error %s in listing packages in path : %s " %
                        (str(inst), path),
                        system="Package Manager",
                        logLevel=logging.CRITICAL)
            defer.returnValue(plugins)

        for addOn in self._installed_packages.itervalues():
            if addOn.type == "addon":
                if addOnName:
                    if addOn.name == addOnName and addOn.enabled:
                        plugins.extend((yield scan(addOn.installPath)))
                else:
                    if addOn.enabled:
                        plugins.extend((yield scan(addOn.installPath)))
        log.msg("Got plugins: ",
                str(plugins),
                system="Package Manager",
                logLevel=logging.DEBUG)
        defer.returnValue(plugins)

    def enable_addon(self, id=None):
        package = self._installed_packages.get(id)
        if package is None:
            raise UnknownPackage()
        package.enabled = True

    def disable_addon(self, id=None):
        package = self._installed_packages.get(id)
        if package is None:
            raise UnknownPackage()
        package.enabled = False

    """
    ####################################################################################
    Package download and installation methods
    """

    @defer.inlineCallbacks
    def refresh_packageList(self):
        """Fetches the remote package list, downloads it, and if there were any changes, packages
        the in memory package list accordingly
        """
        log.msg("checking for new packages : time",
                time.time(),
                logLevel=logging.CRITICAL)
        packageInfoPath = os.path.join(self._path_manager.tmpPath,
                                       "packages.txt")
        try:
            yield DownloaderWithProgress.download(url=self.package_list_url,
                                                  destination=packageInfoPath)
            self._parse_packageListFile()
        except Exception as inst:
            log.msg("Failed to download package master list: error:",
                    inst,
                    system="Package Manager",
                    logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def setup_package(self, id):
        try:
            yield self._downloadPackage(id)
            yield self.installPackage(id)
        except Exception as inst:
            log.msg("Failed to setup package ",
                    id,
                    "because of error",
                    inst,
                    system="Package Manager",
                    logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def _download_package(self, id):
        """downloads the specified package, if the download was successfull, it checks the package's md5 checksum
        versus the one stored in the package info for integretiy, and if succefull, dispatches a success message
        """
        package = self._available_packages.get(id)
        if package is None:
            raise PackageNotFound()
        try:
            downloadPath = os.path.join(self._path_manager.tmpPath,
                                        package.file)
            yield DownloaderWithProgress.download(url=package.downloadUrl,
                                                  destination=downloadPath,
                                                  object=package,
                                                  refChecksum=package.fileHash)
            package.downloaded = True
            #update.installPath=self._addon_path
            self._send_signal("package_download_succeeded", package)
            log.msg("Successfully downloaded package ",
                    package.name,
                    system="Package Manager",
                    logLevel=logging.DEBUG)
        except Exception as inst:
            self._send_signal("package_download_failed", package)
            log.msg("Failed to download package",
                    package.name,
                    " error:",
                    inst,
                    system="Package Manager",
                    logLevel=logging.CRITICAL)

    @defer.inlineCallbacks
    def install_package(self, id):
        """installs the specified package"""
        package = self._available_packages.get(id)
        if package is None:
            raise PackageNotFound()
        if not package.downloaded:
            raise Exception(
                "Cannot install package that was not downloaded first")
        """Steps: 
            extract package in tmp folder
            create list of files it is going to replace
            backup those files to a "rollback" directory
            copy the new files to the correct place
            restart the whole system
            check if all starts well
            if not, delete new files, copy back rollback files, and restart again
            if yes all done
        """
        baseName = os.path.splitext(package.file)[0]
        baseName = baseName.replace('-', '_').replace('.', '_')
        tmpPath = os.path.join(self._path_manager.tmpPath, baseName)
        sourcePath = os.path.join(self._path_manager.tmpPath, package.file)

        if package.type == "addon":
            try:
                destinationPath = os.path.join(self._path_manager._addon_path,
                                               baseName)

                extractedPackageDir = yield self._extract_package(
                    sourcePath, tmpPath)
                extractedPackageDir = os.path.join(tmpPath,
                                                   extractedPackageDir)
                shutil.copytree(extractedPackageDir, destinationPath)
                shutil.rmtree(tmpPath)
                os.remove(sourcePath)

                self._installed_packages[package.cid] = package
                package.installPath = os.path.join(
                    self._path_manager._addon_path,
                    os.path.basename(extractedPackageDir))
                package.enabled = True
                self.add_package_toPythonPath(package.installPath)
                self._send_signal("addon_install_succeeded", package)
            except Exception as inst:
                raise Exception("Failed to install addOn: error %s" %
                                (str(inst)))

        elif package.type == "update":
            #TODO: add restart of system
            try:
                packageToUpdate = self._installed_packages.get(
                    package.targetId)
                if packageToUpdate is None:
                    raise Exception(
                        "Attempting to update not installed package, aborting")
                destinationPath = packageToUpdate.installPath

                extractedPackagePath = yield self._extract_package(
                    sourcePath, tmpPath)
                extractedPackagePath = os.path.join(tmpPath,
                                                    extractedPackagePath)
                #                yield self._backup_files(".")
                dirCompare = filecmp.dircmp(destinationPath,
                                            extractedPackagePath,
                                            hide=[])
                filesToBackup = dirCompare.common

                backupFolderName = os.path.splitext(packageToUpdate.file)[0]
                backupFolderName = backupFolderName.replace('-', '_').replace(
                    '.', '_').replace(' ', '') + "_back"
                backupFolderName = os.path.join(self._path_manager.tmpPath,
                                                backupFolderName)
                #TODO: add id at end ?
                os.makedirs(backupFolderName)

                for fileDir in filesToBackup:
                    fileDir = os.path.join(destinationPath, fileDir)
                    if os.path.isdir(fileDir):
                        dir_util.copy_tree(fileDir, backupFolderName)
                    else:
                        file_util.copy_file(fileDir, backupFolderName)

                dir_util.copy_tree(extractedPackagePath, destinationPath)
                shutil.rmtree(backupFolderName)
                packageToUpdate.version = package.toVersion
                self._send_signal("update_install_succeeded", package)
            except Exception as inst:
                raise Exception("Failed to install update: error %s" %
                                (str(inst)))
        else:
            raise Exception("Unknown package type")

    """
    ####################################################################################
    Helper Methods    
    """

    def _list_localPackages(self):
        """
        Scans the packages path for addons etc , and adds them to the list of currently available packages
        """
        #TODO : also parse info for main (pollapli) package
        packageDirs = os.listdir(self._path_manager._addon_path)
        for fileDir in packageDirs:
            if zipfile.is_zipfile(fileDir):
                sourcePath = fileDir
                baseName = os.path.splitext(sourcePath)[0]
                baseName = baseName.replace('-', '_').replace('.', '_')
                tmpPath = os.path.join(self._path_manager.tmpPath, baseName)

                destinationPath = os.path.join(self._path_manager._addon_path,
                                               baseName)
                extractedPackageDir = yield self._extract_package(
                    sourcePath, tmpPath)
                extractedPackageDir = os.path.join(sourcePath,
                                                   extractedPackageDir)
                shutil.copytree(extractedPackageDir, destinationPath)
                shutil.rmtree(tmpPath)
                os.remove(sourcePath)
                fileDir = destinationPath
            self.add_package_toPythonPath(fileDir)

    def _parse_packageListFile(self):
        packagesFileName = os.path.join(self._path_manager.tmpPath,
                                        "pollapli_packages.json")
        packageFile = file(packagesFileName, "r")
        packageInfos = json.loads(packageFile.read(), "iso-8859-1")
        packageFile.close()

        packageList = [
            Package.from_dict(package) for package in packageInfos["packages"]
        ]
        newPackageCount = 0

        for package in packageList:
            if package.type == "update":
                if package.targetId in self._installed_packages.keys():
                    if package.fromVersion == self._installed_packages[
                            package.targetId].version:
                        if self._available_packages.get(package.cid,
                                                        None) is None:
                            self._available_packages[package.cid] = package
                            newPackageCount += 1
            elif package.type == "addon":
                if package.cid not in self._installed_packages.keys():
                    if self._available_packages.get(package.cid, None) is None:
                        self._available_packages[package.cid] = package
                        newPackageCount += 1

        if newPackageCount > 0:
            self._send_signal("new_packages_available", newPackageCount)

    def _backup_packageFiles(self, rootPath):
        d = defer.Deferred()
        return d

    def _extract_package(self, sourcePath, destinationPath):
        d = defer.Deferred()

        def extract(sourcePath, destinationPath):
            if not zipfile.is_zipfile(sourcePath):
                raise Exception("invalid package extension")
            packageFile = ZipFile(sourcePath, 'r')
            packageFile.extractall(path=destinationPath)
            packageFile.close()
            return os.listdir(destinationPath)[0]

        d.addCallback(extract, destinationPath=destinationPath)
        reactor.callLater(0, d.callback, sourcePath)
        return d

    @defer.inlineCallbacks
    def _extract_allPackages(self):
        """
        helper "hack" function to extract egg/zip files into their adapted directories
        """
        #TODO: fix this
        packageDirs = os.listdir(self._path_manager._addon_path)
        for fileDir in packageDirs:
            if zipfile.is_zipfile(fileDir):
                yield self._extract_package(fileDir)
            if os.path.isdir(fileDir):
                self.add_package_toPythonPath(fileDir)

        #should perhaps be like this:
        #if it is a zip file, use the install_package method
        #if it is a directory, check if is in the path, and if not , add it to the path

    def add_package_toPythonPath(self, path):
        if os.path.isdir(path):
            if not path in sys.path:
                sys.path.insert(0, path)

    @defer.inlineCallbacks
    def update_addOns(self):
        """
        wrapper method, for extraction+ list package in case of newly installed addons
        """
        yield self.extract_addons()
        yield self.list_addons()

    def save_package_list(self):
        for package in self._installed_packages:
            import json
            json.dumps(package)