Exemple #1
0
class NodeManager(object):
    """
    Class for managing nodes: works as a container, a handler
    and a central management point for the list of available nodes
    (for future plugin based system)
    
    
     the signal signature for node manager events is as follows: 
     -for example environment_id.node_created : this is for coherence, signal names use underscores, while hierarchy is represented by dots)
    
    """
    nodeTypes = {}

    #nodeTypes["reprap"]=ReprapCapability

    def __init__(self, parentEnvironment):
        self.logger = log.PythonLoggingObserver(
            "dobozweb.core.components.nodes.nodeManager")
        self.parentEnvironment = parentEnvironment
        self.nodes = {}
        self.lastNodeId = 0
        self._signal_channel = "node_manager"
        self.signalHandler = SignalHander(self._signal_channel)
        self.signal_channel_prefix = "environment_" + str(
            self.parentEnvironment.id)

    @defer.inlineCallbacks
    def setup(self):
        @defer.inlineCallbacks
        def addNode(nodes):
            for node in nodes:
                node.environment.set(self.parentEnvironment)
                self.nodes[node.id] = node
                yield node.setup()

        yield Node.all().addCallback(addNode)
        defer.returnValue(None)

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

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

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

        node = yield Node(name=name, description=description, type=type).save()
        node.environment.set(self.parentEnvironment)
        self.nodes[node.id] = node
        log.msg("Added  node ",
                name,
                " with id set to ",
                str(node.id),
                logLevel=logging.CRITICAL)
        self._send_signal("node_created", node)

        defer.returnValue(node)

    def get_nodes(self, filter=None):
        """
        Returns the list of nodes, 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(node, filter):
            for key in filter.keys():
                if not getattr(node, key) in filter[key]:
                    return False
            return True

        def get(filter, nodesList):
            if filter:
                return WrapperList(data=[
                    node for node in nodesList if filter_check(node, filter)
                ],
                                   rootType="nodes")
            else:
                return WrapperList(data=nodesList, rootType="nodes")

        d.addCallback(get, self.nodes.values())
        reactor.callLater(0.5, d.callback, filter)
        return d

    def get_node(self, id):
        if not id in self.nodes.keys():
            raise NodeNotFound()
        return self.nodes[id]

    def update_node(self, id, name=None, description=None, *args, **kwargs):
        """Method for node update"""
        node = self.nodes[id]

        @defer.inlineCallbacks
        def _update():
            node.name = name
            node.description = description
            yield node.save()
            self._send_signal("node_updated", node)
            log.msg("updating node ",
                    id,
                    "newname",
                    name,
                    "newdescrption",
                    description,
                    logLevel=logging.CRITICAL)

        _update()

        defer.succeed(node)

        #self.nodes[id].update()

    def delete_node(self, id):
        """
        Remove a node : this needs a whole set of checks, 
        as it would delete an node completely 
        Params:
        id: the id of the node
        """
        @defer.inlineCallbacks
        def _remove(id):
            """
             little "trick" for node deletion : to keep the objects id alive (as it gets removed normally before the event 
             has a chance to be dispatched), the id + data of a node to be deleted gets saved, then the event is dispatched using
              this "fake node"'s data 
            """

            nodeName = self.nodes[id].name
            tmpNode = self.nodes[id]
            tmpId = tmpNode.id
            yield self.nodes[id].delete()
            del self.nodes[id]
            tmpNode.id = tmpId
            self._send_signal("node_deleted", tmpNode)
            log.msg("Removed node ",
                    nodeName,
                    "with id ",
                    id,
                    logLevel=logging.CRITICAL)

        if not id in self.nodes.keys():
            raise NodeNotFound()
        _remove(id)
        defer.succeed(True)

    def clear_nodes(self):
        """
        Removes & deletes ALL the nodes, should be used with care
        """
        @defer.inlineCallbacks
        def _clear():
            for node in self.nodes.values():
                yield self.delete_node(node.id)
            self._send_signal("nodes_cleared", self.nodes)

        _clear()

        defer.succeed(True)

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

    def set_driver(self, nodeId, **kwargs):
        """Method to set a nodes connector's driver 
        Params:
        nodeId: the id of the node
        WARNING: cheap hack for now, always defaults to serial
        connector
        """
        #real clumsy, will be switched with better dynamic driver instanciation
        driver = None
        if kwargs["type"] == "teacup":
            del kwargs["type"]
            driver = TeacupDriver(**kwargs)
        elif kwargs["type"] == "fived":
            del kwargs["type"]
            driver = FiveDDriver(**kwargs)
        if driver:
            self.nodes[nodeId].connector.set_driver(driver)
            self.logger.critical(
                "Set driver for connector of node %d with params %s", nodeId,
                str(kwargs))
        else:
            raise Exception("unknown driver ")

    def start_node(self, nodeId, **kwargs):
        self.nodes[nodeId].start(**kwargs)

    def stop_node(self, nodeId):
        self.nodes[nodeId].stop()
class EnvironmentManager(object):
    """
    Class acting as a central access point for all the functionality of environments
    """
    envPath=None
    environments={}
    idCounter=1
    def __init__(self,envPath):
        self.logger=log.PythonLoggingObserver("dobozweb.core.components.environments.environmentManager")
        self.path=EnvironmentManager.envPath
        self.idCounter=EnvironmentManager.idCounter
        
        self._signal_channel="environment_manager"
        self.signalHandler=SignalHander(self._signal_channel)
    
    @classmethod
    @defer.inlineCallbacks
    def setup(cls,*args,**kwargs):
        """Retrieve all existing environments from disk"""
        maxFoundId=1
        for fileDir in os.listdir(EnvironmentManager.envPath): 
            if os.path.isdir(os.path.join(EnvironmentManager.envPath,fileDir)):        
                envName= fileDir
                envPath=os.path.join(EnvironmentManager.envPath,envName)
                dbPath=os.path.join(envPath,envName)+".db"
                if os.path.exists(dbPath):
                    Registry.DBPOOL = adbapi.ConnectionPool("sqlite3",database=dbPath,check_same_thread=False)
                    @defer.inlineCallbacks        
                    def addEnv(env,maxFoundId):
                        EnvironmentManager.environments[env[0].id]=env[0]
                        yield env[0].setup()
                        if env[0].id>maxFoundId:
                            maxFoundId=env[0].id
                        
                        defer.returnValue(maxFoundId)
                    
                    maxFoundId=yield Environment.find().addCallback(addEnv,maxFoundId)
                    EnvironmentManager.idCounter=maxFoundId+1
                    #temporary: this should be recalled from db from within the environments ?
        
        log.msg("Environment manager setup correctly", system="environement manager", logLevel=logging.CRITICAL)
        
    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)
        
        
    def stop(self):
        """
        Shuts down the environment manager and everything associated with it : ie EVERYTHING !!
        Should not be called in most cases
        """
        pass

    """
    ####################################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the general handling of environements
    """
    @defer.inlineCallbacks
    def add_environment(self,name="home_test",description="Add Description here",status="frozen"):
        """
        Add an environment to the list of managed environements : 
        Automatically creates a new folder and launches the new environement auto creation
        Params:
        EnvName: the name of the environment
        description:a short description of the environment
        status: either frozen or live : whether the environment is active or not
        """
        envPath=os.path.join(self.path,name)  
        dbpath=os.path.join(envPath,name)+".db"
        
        #if such an environment does not exist, add it
        doCreate=True
        if name in os.listdir(self.path):
            if os.path.exists(os.path.join(self.path,name,dbpath)):
                doCreate=False
        else:
            os.mkdir(envPath)
        
        if doCreate:
            Registry.DBPOOL = adbapi.ConnectionPool("sqlite3",database=dbpath,check_same_thread=False)
            
            env=Environment(path=envPath,name=name,description=description,status=status)
            yield self._generateDatabase()
            yield env.save()  
            
            """rather horrid hack of sorts, required to have different, sequential id in the different dbs"""
            self.force_id(self.idCounter)  
            Registry.DBPOOL.close()
            Registry.DBPOOL = adbapi.ConnectionPool("sqlite3",database=dbpath,check_same_thread=False)
            
            def addEnv(env):
                self.environments[env[0].id]=env[0]
                self.idCounter+=1
            yield Environment.find().addCallback(addEnv)
            
            env=self.environments[self.idCounter-1]
            self.signalHandler.send_message("environment.created",self,env)
            log.msg("Adding environment named:",name ," description:",description,"with id",env.id, system="environment manager", logLevel=logging.CRITICAL)         
            defer.returnValue(env)
        else:
            raise EnvironmentAlreadyExists()

        defer.returnValue(None) 
    

    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(filter,envsList):
            if filter:
                #return [env for env in self.environments if getattr(env, "id") in filter["id"]]
                #return [env for env in self.environments if [True for key in filter.keys() if getattr(env, key)in filter[key]]]
              
                return WrapperList(data=[env for env in envsList if filter_check(env,filter)],rootType="environments")
            else:
                return WrapperList(data=envsList,rootType="environments")
            
        d.addCallback(get,self.environments.values())
        reactor.callLater(0.5,d.callback,filter)
        return d
    
    def get_environment(self,envId):      
        env=self.environments.get(envId)
        if env is None:
            raise EnvironmentNotFound()
        return env
    
    def update_environment(self,id,name,description,status):
        #print("updating env",id,name,description,status)
        return self.environments[id].update(name,description,status)
    
    def remove_environment(self,id):
        """
        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
        """
        d=defer.Deferred()
        def remove(id,envs,path):
            try:
                Registry.DBPOOL.close()
                envName=envs[id].name
                envPath=os.path.join(path,envName)    
                #self.environments[envName].shutdown()
                del envs[id]
                if os.path.isdir(envPath): 
                    shutil.rmtree(envPath)
                            #self.logger.critical("Removed and deleted envrionment: '%s' at : '%s'",envName,envPath) 
                    log.msg("Removed environment ",envName,"with id ",id, system="environment manager",logLevel=logging.CRITICAL)
            except:
                pass
                #should raise  specific exception
                #raise Exception("failed to delete env")
        d.addCallback(remove,self.environments,self.path)
        reactor.callLater(0,d.callback,id)
        return d

        
    @defer.inlineCallbacks
    def clear_environments(self):
        """
        Removes & deletes ALL the environments, should be used with care
        """
        print(self.environments)
        for env in self.environments.values():
            yield self.remove_environment(env.id)        
        defer.returnValue(None)
    """
    ####################################################################################
    Helper Methods    
    """
    @defer.inlineCallbacks
    def force_id(self,id):     
        query=   '''UPDATE environments SET id ='''+str(id)+''' where id=1'''
        yield Registry.DBPOOL.runQuery(query)
        defer.returnValue(None)
    
    @defer.inlineCallbacks
    def _generateMasterDatabase(self):
        yield Registry.DBPOOL.runQuery('''CREATE TABLE environments(
             id INTEGER PRIMARY KEY ,
             name TEXT,
             status TEXT NOT NULL DEFAULT "Live",
             description TEXT
             )''')
    @defer.inlineCallbacks
    def _generateDatabase(self):
        yield Registry.DBPOOL.runQuery('''CREATE TABLE addons(
             id INTEGER PRIMARY KEY,
             name TEXT,
             description TEXT,
             active boolean NOT NULL default true           
             )''')
        yield Registry.DBPOOL.runQuery('''CREATE TABLE environments(
             id INTEGER PRIMARY KEY,
             name TEXT,
             description TEXT,
             status TEXT NOT NULL DEFAULT "Live"
             )''')
        
        yield Registry.DBPOOL.runQuery('''CREATE TABLE nodes(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             environment_id INTEGER NOT NULL,
             type TEXT NOT NULL ,
             name TEXT,          
             description TEXT,
             recipe TEXT,
            FOREIGN KEY(environment_id) REFERENCES environments(id) 
             )''')
        
      #FOREIGN KEY(node_id) REFERENCES nodes(id)  
        yield Registry.DBPOOL.runQuery('''CREATE TABLE drivers(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             node_id INTEGER NOT NULL,
             driverType TEXT NOT NULL,
             deviceType TEXT NOT NULL ,
             deviceId TEXT,
             options BLOB  ,
             FOREIGN KEY(node_id) REFERENCES nodes(id)      
             )''')
        
        yield Registry.DBPOOL.runQuery('''CREATE TABLE tasks(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             environment_id INTEGER NOT NULL,
             name TEXT,          
             description TEXT,
             type TEXT,
             params TEXT,
             FOREIGN KEY(environment_id) REFERENCES environments(id)  
             )''')
        yield Registry.DBPOOL.runQuery('''CREATE TABLE actions(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             task_id INTEGER NOT NULL,
             actionType TEXT,          
             params TEXT,
             FOREIGN KEY(task_id) REFERENCES tasks(id)
             )''')
        yield Registry.DBPOOL.runQuery('''CREATE TABLE conditions(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             task_id INTEGER NOT NULL,
             name TEXT,          
             description TEXT,
             FOREIGN KEY(task_id) REFERENCES tasks(id)
             )''')
        
       
             
        yield Registry.DBPOOL.runQuery('''CREATE TABLE sensors(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             node_id INTEGER NOT NULL,
             captureType TEXT NOT NULL,
             captureMode TEXT NOT NULL DEFAULT "Analog",
             type TEXT NOT NULL ,
             realName TEXT NOT NULL,
             name TEXT,
             description TEXT,
             FOREIGN KEY(node_id) REFERENCES nodes(id)
             )''')
        
        yield Registry.DBPOOL.runQuery('''CREATE TABLE readings(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             node_id INTEGER NOT NULL,
             sensor_id INTEGER NOT NULL,
             data INTEGER NOT NULL,
             dateTime TEXT NOT NULL,
             FOREIGN KEY(node_id) REFERENCES nodes(id),
             FOREIGN KEY(sensor_id) REFERENCES sensors(id)
             )''')
        """this should be in the master db, but will have to do for now (only support for one environment,
        because of the limitations of twistar: one db )"""
        
        yield Registry.DBPOOL.runQuery('''CREATE TABLE updates(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             type TEXT NOT NULL,
             name TEXT NOT NULL,
             description TEXT,
             version TEXT NOT NULL,
             downloadUrl TEXT NOT NULL,
             tags TEXT NOT NULL,
             img TEXT NOT NULL,
             file TEXT NOT NULL,
             fileHash TEXT NOT NULL,
             downloaded TEXT NOT NULL,
             installed TEXT NOT NULL,
             enabled TEXT NOT NULL
             
             )''')
        
        defer.returnValue(None)
Exemple #3
0
class TaskManager(object):
    
    def __init__(self,parentEnvironment):
        self.tasks={}
        self.parentEnvironment=parentEnvironment
        self._signal_channel="task_manager"
        self.signalHandler=SignalHander(self._signal_channel)
    
    @defer.inlineCallbacks
    def setup(self):         
        yield self.load(self.parentEnvironment)
        self.signal_channel_prefix="environment_"+str(self.parentEnvironment.id)    
        defer.returnValue(self)
    
    def _send_signal(self,signal="",data=None):
        prefix=self.signal_channel_prefix+"."
        self.signalHandler.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 load(self,parentEnvironment=None,taskId=None,taskType=None,*args,**kwargs):
        if taskId is not None:
            """For retrieval of single task"""
            task=yield Task.find(where=['environment_id = ? and task_id=?', parentEnvironment.id,taskId])
            defer.returnValue(task)
        else:
            tasks=yield Task.find(where=['environment_id = ?', parentEnvironment.id])
#            for task in tasks:
#                task.environment.set(parentEnvironment) 
#                yield task.setup()
#                #temp hack           
#                actions=yield PrintAction.find(where=['task_id = ?', task.id])              
#                action=actions[0]
#                
#                actionParams=ast.literal_eval(action.params)
#                action.setup(actionParams)
#                
#                action.task.set(task)
#                action.parentTask=task
#                task.set_action(action)
#                #print("action info ptask",action.parentTask,"filename",action.printFileName)
#                self.tasks[task.id]=task
                
            
                
            defer.returnValue(tasks)
    
    @defer.inlineCallbacks
    def add_task(self,nodeId=None,nodeElement=None,name="task",description="",taskType=None,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
        type: the type of the task : very important , as it will be used to instanciate the correct class
        instance
        Connector:the connector to use for this taks
       
        """
        
        """JUST FOR TESTING !!!!
        The finding of nodes and target variables should be done using the normal element.get_subelement(params) API
        """    
        
        targetNode= yield self.parentEnvironment.get_node(1)
        targetVariable=targetNode.testElement
        print("zerzer",targetVariable)
        
        
        task=yield Task(taskType=taskType,extra_params=None).save()
        log.msg("Added  task ",name, logLevel=logging.CRITICAL)
        
        task.add_action(UpdateAndGetVariable_action(targetNode,targetVariable))
        print("Added test action")
        
        task.start()
                
#        task= yield Task(name,description,type,params).save()
#        task.environment.set(self.parentEnvironment)  
#        yield task.setup()
#        
#        action=yield PrintAction(parentTask=task,**params).save()
#        action.task.set(task)
#        task.set_action(action)
#        self.tasks[task.id]=task
#        task.start()
#            
#        print("tasks",self.tasks)
##            capability= yield NodeManager.environmentTypes[type](name,description).save()
##            capability.environment.set(self.parentEnv)
##            capability.environment.set(environment)
##            environment.capability=capability
#            #task.events.OnExited+=self._on_task_exited
#        log.msg("Added  task ",name," of type ",type," with id set to ",str(task.id), logLevel=logging.CRITICAL)
#        defer.returnValue(task)

        
    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):
            print("taskList",tasksList)
            if filter:
                
                return WrapperList(data=[task for task in environmentsList if filter_check(task,filter)],rootType="tasks")
            else:               
                return WrapperList(data=tasksList,rootType="tasks")
            
        d.addCallback(get,self.tasks.values())
        reactor.callLater(0.5,d.callback,filter)
        return d
            
    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
        """
        if forceStop:
            if self.tasks[id].isRunning:
                    self.tasks[id].shutdown()
        del self.tasks[id]
        self.logger.critical ("Task %s Removed ",id)         
       
    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
        """
        [self.remove_task(task.id, forceStop) for task in self.tasks]
    
    def get_task(self,id):
        """
        returns the task with given id
        """
        return self.tasks[id]
    
    def start_task(self,id):
        """Starts the task in line"""
        try:
            self.logger.critical ("Starting task with id %d",id)
            self.tasks[id].connect(self.connector)
            self.tasks[id].start()
        except Exception as inst:
            self.logger.critical ("Error while starting task %s",str(inst))
   
    def stop_task(self,id):
        """Forcefully Stops the task with the given id """
        if self.tasks[id].isRunning:
            self.tasks[id].shutdown()
                
    def _on_task_exited(self,args,kargs):
        """Handles the event when the task exited (whether force stopped or because it was finished """
        self._task_shutdown()
        self.logger.critical ("Task Finished ,%g remaining tasks",len(self.tasks))
        
    def _task_shutdown(self):
        """helper function for cleaning up after a task ended """
        self.currentTask.disconnect()
        self.currentTask.events.OnExited-=self._on_task_exited
        self.currentTask=None 
        del self.tasks[0]   
Exemple #4
0
class NodeManager(object):
    """
    Class for managing nodes: works as a container, a handler
    and a central management point for the list of available nodes
    (for future plugin based system)
    
    
     the signal signature for node manager events is as follows: 
     -for example environment_id.node_created : this is for coherence, signal names use underscores, while hierarchy is represented by dots)
    
    """
    nodeTypes={}
    #nodeTypes["reprap"]=ReprapCapability
    
    def __init__(self,parentEnvironment):
        self.logger=log.PythonLoggingObserver("dobozweb.core.components.nodes.nodeManager")
        self.parentEnvironment=parentEnvironment
        self.nodes={}
        self.lastNodeId=0
        self._signal_channel="node_manager"
        self.signalHandler=SignalHander(self._signal_channel)
        self.signal_channel_prefix="environment_"+str(self.parentEnvironment.id)
     
    @defer.inlineCallbacks    
    def setup(self):
       
        @defer.inlineCallbacks
        def addNode(nodes):
            for node in nodes:
                node.environment.set(self.parentEnvironment)
                self.nodes[node.id]=node
                yield node.setup()
               
        yield Node.all().addCallback(addNode)
        defer.returnValue(None)
    
    
    def _send_signal(self,signal="",data=None):
        prefix=self.signal_channel_prefix+"."
        self.signalHandler.send_message(prefix+signal,self,data)    
    """
    ####################################################################################
    The following are the "CRUD" (Create, read, update,delete) methods for the general handling of nodes
    """
    @defer.inlineCallbacks
    def add_node(self,name="node",description="",type=None,*args,**kwargs):
        """
        Add a new node to the list of nodes of the current environment
        Params:
        name: the name of the node
        Desciption: short description of node
        type: the type of the node : very important , as it will be used to instanciate the correct class
        instance
        Connector:the connector to use for this node
        Driver: the driver to use for this node's connector
        """
            
        
        node= yield Node(name=name,description=description,type=type).save()
        node.environment.set(self.parentEnvironment)
        self.nodes[node.id]=node
        log.msg("Added  node ",name," with id set to ",str(node.id), logLevel=logging.CRITICAL)
        self._send_signal("node_created", node)
       
        defer.returnValue(node)
        
    
    def get_nodes(self,filter=None):
        """
        Returns the list of nodes, 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(node,filter):
            for key in filter.keys():
                if not getattr(node, key) in filter[key]:
                    return False
            return True
      
        def get(filter,nodesList):
            if filter:
                return WrapperList(data=[node for node in nodesList if filter_check(node,filter)],rootType="nodes")
            else:               
                return WrapperList(data=nodesList,rootType="nodes")
            
        d.addCallback(get,self.nodes.values())
        reactor.callLater(0.5,d.callback,filter)
        return d
    
    def get_node(self,id):
        if not id in self.nodes.keys():
            raise NodeNotFound()
        return self.nodes[id]
    
    
    def update_node(self,id,name=None,description=None,*args,**kwargs):
        """Method for node update"""   
        node=self.nodes[id]
        
        @defer.inlineCallbacks
        def _update():
            node.name=name
            node.description=description
            yield node.save()
            self._send_signal("node_updated", node)
            log.msg("updating node ",id,"newname",name,"newdescrption",description,logLevel=logging.CRITICAL)
        _update()
        
        
        defer.succeed(node)

        #self.nodes[id].update()
    
    
    def delete_node(self,id):
        """
        Remove a node : this needs a whole set of checks, 
        as it would delete an node completely 
        Params:
        id: the id of the node
        """
        @defer.inlineCallbacks
        def _remove(id):
            """
             little "trick" for node deletion : to keep the objects id alive (as it gets removed normally before the event 
             has a chance to be dispatched), the id + data of a node to be deleted gets saved, then the event is dispatched using
              this "fake node"'s data 
            """
           
            nodeName=self.nodes[id].name    
            tmpNode=self.nodes[id]
            tmpId=tmpNode.id
            yield self.nodes[id].delete() 
            del self.nodes[id]
            tmpNode.id=tmpId
            self._send_signal("node_deleted", tmpNode)
            log.msg("Removed node ",nodeName,"with id ",id,logLevel=logging.CRITICAL)
        if not id in self.nodes.keys():
            raise NodeNotFound()
        _remove(id)
        defer.succeed(True)
 
            
    
    def clear_nodes(self):
        """
        Removes & deletes ALL the nodes, should be used with care
        """
        @defer.inlineCallbacks
        def _clear():
            for node in self.nodes.values():
                yield self.delete_node(node.id)  
            self._send_signal("nodes_cleared", self.nodes)   
        _clear();  
             
        defer.succeed(True)     

   
    """
    ####################################################################################
    Helper Methods    
    """
        
    def set_driver(self,nodeId,**kwargs):
        """Method to set a nodes connector's driver 
        Params:
        nodeId: the id of the node
        WARNING: cheap hack for now, always defaults to serial
        connector
        """
        #real clumsy, will be switched with better dynamic driver instanciation
        driver=None
        if kwargs["type"]== "teacup":
            del kwargs["type"]
            driver=TeacupDriver(**kwargs)
        elif kwargs["type"]=="fived":
            del kwargs["type"]
            driver=FiveDDriver(**kwargs)
        if driver:
            self.nodes[nodeId].connector.set_driver(driver)  
            self.logger.critical("Set driver for connector of node %d with params %s",nodeId,str(kwargs))
        else:
            raise Exception("unknown driver ")
        
    def start_node(self,nodeId,**kwargs):
        self.nodes[nodeId].start(**kwargs)
        
    def stop_node(self,nodeId):
        self.nodes[nodeId].stop()
Exemple #5
0
class TaskManager(object):
    def __init__(self, parentEnvironment):
        self.tasks = {}
        self.parentEnvironment = parentEnvironment
        self._signal_channel = "task_manager"
        self.signalHandler = SignalHander(self._signal_channel)

    @defer.inlineCallbacks
    def setup(self):
        yield self.load(self.parentEnvironment)
        self.signal_channel_prefix = "environment_" + str(
            self.parentEnvironment.id)
        defer.returnValue(self)

    def _send_signal(self, signal="", data=None):
        prefix = self.signal_channel_prefix + "."
        self.signalHandler.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 load(self,
             parentEnvironment=None,
             taskId=None,
             taskType=None,
             *args,
             **kwargs):
        if taskId is not None:
            """For retrieval of single task"""
            task = yield Task.find(where=[
                'environment_id = ? and task_id=?', parentEnvironment.id,
                taskId
            ])
            defer.returnValue(task)
        else:
            tasks = yield Task.find(
                where=['environment_id = ?', parentEnvironment.id])
            #            for task in tasks:
            #                task.environment.set(parentEnvironment)
            #                yield task.setup()
            #                #temp hack
            #                actions=yield PrintAction.find(where=['task_id = ?', task.id])
            #                action=actions[0]
            #
            #                actionParams=ast.literal_eval(action.params)
            #                action.setup(actionParams)
            #
            #                action.task.set(task)
            #                action.parentTask=task
            #                task.set_action(action)
            #                #print("action info ptask",action.parentTask,"filename",action.printFileName)
            #                self.tasks[task.id]=task

            defer.returnValue(tasks)

    @defer.inlineCallbacks
    def add_task(self,
                 nodeId=None,
                 nodeElement=None,
                 name="task",
                 description="",
                 taskType=None,
                 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
        type: the type of the task : very important , as it will be used to instanciate the correct class
        instance
        Connector:the connector to use for this taks
       
        """
        """JUST FOR TESTING !!!!
        The finding of nodes and target variables should be done using the normal element.get_subelement(params) API
        """

        targetNode = yield self.parentEnvironment.get_node(1)
        targetVariable = targetNode.testElement
        print("zerzer", targetVariable)

        task = yield Task(taskType=taskType, extra_params=None).save()
        log.msg("Added  task ", name, logLevel=logging.CRITICAL)

        task.add_action(UpdateAndGetVariable_action(targetNode,
                                                    targetVariable))
        print("Added test action")

        task.start()

#        task= yield Task(name,description,type,params).save()
#        task.environment.set(self.parentEnvironment)
#        yield task.setup()
#
#        action=yield PrintAction(parentTask=task,**params).save()
#        action.task.set(task)
#        task.set_action(action)
#        self.tasks[task.id]=task
#        task.start()
#
#        print("tasks",self.tasks)
##            capability= yield NodeManager.environmentTypes[type](name,description).save()
##            capability.environment.set(self.parentEnv)
##            capability.environment.set(environment)
##            environment.capability=capability
#            #task.events.OnExited+=self._on_task_exited
#        log.msg("Added  task ",name," of type ",type," with id set to ",str(task.id), logLevel=logging.CRITICAL)
#        defer.returnValue(task)

    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):
            print("taskList", tasksList)
            if filter:

                return WrapperList(data=[
                    task for task in environmentsList
                    if filter_check(task, filter)
                ],
                                   rootType="tasks")
            else:
                return WrapperList(data=tasksList, rootType="tasks")

        d.addCallback(get, self.tasks.values())
        reactor.callLater(0.5, d.callback, filter)
        return d

    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
        """
        if forceStop:
            if self.tasks[id].isRunning:
                self.tasks[id].shutdown()
        del self.tasks[id]
        self.logger.critical("Task %s Removed ", id)

    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
        """
        [self.remove_task(task.id, forceStop) for task in self.tasks]

    def get_task(self, id):
        """
        returns the task with given id
        """
        return self.tasks[id]

    def start_task(self, id):
        """Starts the task in line"""
        try:
            self.logger.critical("Starting task with id %d", id)
            self.tasks[id].connect(self.connector)
            self.tasks[id].start()
        except Exception as inst:
            self.logger.critical("Error while starting task %s", str(inst))

    def stop_task(self, id):
        """Forcefully Stops the task with the given id """
        if self.tasks[id].isRunning:
            self.tasks[id].shutdown()

    def _on_task_exited(self, args, kargs):
        """Handles the event when the task exited (whether force stopped or because it was finished """
        self._task_shutdown()
        self.logger.critical("Task Finished ,%g remaining tasks",
                             len(self.tasks))

    def _task_shutdown(self):
        """helper function for cleaning up after a task ended """
        self.currentTask.disconnect()
        self.currentTask.events.OnExited -= self._on_task_exited
        self.currentTask = None
        del self.tasks[0]
class EnvironmentManager(object):
    """
    Class acting as a central access point for all the functionality of environments
    """
    envPath = None
    environments = {}
    idCounter = 1

    def __init__(self, envPath):
        self.logger = log.PythonLoggingObserver(
            "dobozweb.core.components.environments.environmentManager")
        self.path = EnvironmentManager.envPath
        self.idCounter = EnvironmentManager.idCounter

        self._signal_channel = "environment_manager"
        self.signalHandler = SignalHander(self._signal_channel)

    @classmethod
    @defer.inlineCallbacks
    def setup(cls, *args, **kwargs):
        """Retrieve all existing environments from disk"""
        maxFoundId = 1
        for fileDir in os.listdir(EnvironmentManager.envPath):
            if os.path.isdir(os.path.join(EnvironmentManager.envPath,
                                          fileDir)):
                envName = fileDir
                envPath = os.path.join(EnvironmentManager.envPath, envName)
                dbPath = os.path.join(envPath, envName) + ".db"
                if os.path.exists(dbPath):
                    Registry.DBPOOL = adbapi.ConnectionPool(
                        "sqlite3", database=dbPath, check_same_thread=False)

                    @defer.inlineCallbacks
                    def addEnv(env, maxFoundId):
                        EnvironmentManager.environments[env[0].id] = env[0]
                        yield env[0].setup()
                        if env[0].id > maxFoundId:
                            maxFoundId = env[0].id

                        defer.returnValue(maxFoundId)

                    maxFoundId = yield Environment.find().addCallback(
                        addEnv, maxFoundId)
                    EnvironmentManager.idCounter = maxFoundId + 1
                    #temporary: this should be recalled from db from within the environments ?

        log.msg("Environment manager setup correctly",
                system="environement manager",
                logLevel=logging.CRITICAL)

    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)

    def stop(self):
        """
        Shuts down the environment manager and everything associated with it : ie EVERYTHING !!
        Should not be called in most cases
        """
        pass

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

    @defer.inlineCallbacks
    def add_environment(self,
                        name="home_test",
                        description="Add Description here",
                        status="frozen"):
        """
        Add an environment to the list of managed environements : 
        Automatically creates a new folder and launches the new environement auto creation
        Params:
        EnvName: the name of the environment
        description:a short description of the environment
        status: either frozen or live : whether the environment is active or not
        """
        envPath = os.path.join(self.path, name)
        dbpath = os.path.join(envPath, name) + ".db"

        #if such an environment does not exist, add it
        doCreate = True
        if name in os.listdir(self.path):
            if os.path.exists(os.path.join(self.path, name, dbpath)):
                doCreate = False
        else:
            os.mkdir(envPath)

        if doCreate:
            Registry.DBPOOL = adbapi.ConnectionPool("sqlite3",
                                                    database=dbpath,
                                                    check_same_thread=False)

            env = Environment(path=envPath,
                              name=name,
                              description=description,
                              status=status)
            yield self._generateDatabase()
            yield env.save()
            """rather horrid hack of sorts, required to have different, sequential id in the different dbs"""
            self.force_id(self.idCounter)
            Registry.DBPOOL.close()
            Registry.DBPOOL = adbapi.ConnectionPool("sqlite3",
                                                    database=dbpath,
                                                    check_same_thread=False)

            def addEnv(env):
                self.environments[env[0].id] = env[0]
                self.idCounter += 1

            yield Environment.find().addCallback(addEnv)

            env = self.environments[self.idCounter - 1]
            self.signalHandler.send_message("environment.created", self, env)
            log.msg("Adding environment named:",
                    name,
                    " description:",
                    description,
                    "with id",
                    env.id,
                    system="environment manager",
                    logLevel=logging.CRITICAL)
            defer.returnValue(env)
        else:
            raise EnvironmentAlreadyExists()

        defer.returnValue(None)

    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(filter, envsList):
            if filter:
                #return [env for env in self.environments if getattr(env, "id") in filter["id"]]
                #return [env for env in self.environments if [True for key in filter.keys() if getattr(env, key)in filter[key]]]

                return WrapperList(data=[
                    env for env in envsList if filter_check(env, filter)
                ],
                                   rootType="environments")
            else:
                return WrapperList(data=envsList, rootType="environments")

        d.addCallback(get, self.environments.values())
        reactor.callLater(0.5, d.callback, filter)
        return d

    def get_environment(self, envId):
        env = self.environments.get(envId)
        if env is None:
            raise EnvironmentNotFound()
        return env

    def update_environment(self, id, name, description, status):
        #print("updating env",id,name,description,status)
        return self.environments[id].update(name, description, status)

    def remove_environment(self, id):
        """
        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
        """
        d = defer.Deferred()

        def remove(id, envs, path):
            try:
                Registry.DBPOOL.close()
                envName = envs[id].name
                envPath = os.path.join(path, envName)
                #self.environments[envName].shutdown()
                del envs[id]
                if os.path.isdir(envPath):
                    shutil.rmtree(envPath)
                    #self.logger.critical("Removed and deleted envrionment: '%s' at : '%s'",envName,envPath)
                    log.msg("Removed environment ",
                            envName,
                            "with id ",
                            id,
                            system="environment manager",
                            logLevel=logging.CRITICAL)
            except:
                pass
                #should raise  specific exception
                #raise Exception("failed to delete env")

        d.addCallback(remove, self.environments, self.path)
        reactor.callLater(0, d.callback, id)
        return d

    @defer.inlineCallbacks
    def clear_environments(self):
        """
        Removes & deletes ALL the environments, should be used with care
        """
        print(self.environments)
        for env in self.environments.values():
            yield self.remove_environment(env.id)
        defer.returnValue(None)

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

    @defer.inlineCallbacks
    def force_id(self, id):
        query = '''UPDATE environments SET id =''' + str(
            id) + ''' where id=1'''
        yield Registry.DBPOOL.runQuery(query)
        defer.returnValue(None)

    @defer.inlineCallbacks
    def _generateMasterDatabase(self):
        yield Registry.DBPOOL.runQuery('''CREATE TABLE environments(
             id INTEGER PRIMARY KEY ,
             name TEXT,
             status TEXT NOT NULL DEFAULT "Live",
             description TEXT
             )''')

    @defer.inlineCallbacks
    def _generateDatabase(self):
        yield Registry.DBPOOL.runQuery('''CREATE TABLE addons(
             id INTEGER PRIMARY KEY,
             name TEXT,
             description TEXT,
             active boolean NOT NULL default true           
             )''')
        yield Registry.DBPOOL.runQuery('''CREATE TABLE environments(
             id INTEGER PRIMARY KEY,
             name TEXT,
             description TEXT,
             status TEXT NOT NULL DEFAULT "Live"
             )''')

        yield Registry.DBPOOL.runQuery('''CREATE TABLE nodes(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             environment_id INTEGER NOT NULL,
             type TEXT NOT NULL ,
             name TEXT,          
             description TEXT,
             recipe TEXT,
            FOREIGN KEY(environment_id) REFERENCES environments(id) 
             )''')

        #FOREIGN KEY(node_id) REFERENCES nodes(id)
        yield Registry.DBPOOL.runQuery('''CREATE TABLE drivers(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             node_id INTEGER NOT NULL,
             driverType TEXT NOT NULL,
             deviceType TEXT NOT NULL ,
             deviceId TEXT,
             options BLOB  ,
             FOREIGN KEY(node_id) REFERENCES nodes(id)      
             )''')

        yield Registry.DBPOOL.runQuery('''CREATE TABLE tasks(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             environment_id INTEGER NOT NULL,
             name TEXT,          
             description TEXT,
             type TEXT,
             params TEXT,
             FOREIGN KEY(environment_id) REFERENCES environments(id)  
             )''')
        yield Registry.DBPOOL.runQuery('''CREATE TABLE actions(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             task_id INTEGER NOT NULL,
             actionType TEXT,          
             params TEXT,
             FOREIGN KEY(task_id) REFERENCES tasks(id)
             )''')
        yield Registry.DBPOOL.runQuery('''CREATE TABLE conditions(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             task_id INTEGER NOT NULL,
             name TEXT,          
             description TEXT,
             FOREIGN KEY(task_id) REFERENCES tasks(id)
             )''')

        yield Registry.DBPOOL.runQuery('''CREATE TABLE sensors(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             node_id INTEGER NOT NULL,
             captureType TEXT NOT NULL,
             captureMode TEXT NOT NULL DEFAULT "Analog",
             type TEXT NOT NULL ,
             realName TEXT NOT NULL,
             name TEXT,
             description TEXT,
             FOREIGN KEY(node_id) REFERENCES nodes(id)
             )''')

        yield Registry.DBPOOL.runQuery('''CREATE TABLE readings(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             node_id INTEGER NOT NULL,
             sensor_id INTEGER NOT NULL,
             data INTEGER NOT NULL,
             dateTime TEXT NOT NULL,
             FOREIGN KEY(node_id) REFERENCES nodes(id),
             FOREIGN KEY(sensor_id) REFERENCES sensors(id)
             )''')
        """this should be in the master db, but will have to do for now (only support for one environment,
        because of the limitations of twistar: one db )"""

        yield Registry.DBPOOL.runQuery('''CREATE TABLE updates(
             id INTEGER PRIMARY KEY AUTOINCREMENT,
             type TEXT NOT NULL,
             name TEXT NOT NULL,
             description TEXT,
             version TEXT NOT NULL,
             downloadUrl TEXT NOT NULL,
             tags TEXT NOT NULL,
             img TEXT NOT NULL,
             file TEXT NOT NULL,
             fileHash TEXT NOT NULL,
             downloaded TEXT NOT NULL,
             installed TEXT NOT NULL,
             enabled TEXT NOT NULL
             
             )''')

        defer.returnValue(None)