def addNode2Looper(self, node, position, looper_pos=0): ''' Add a looper child node to a looper node. ''' if not self.nodes: raise PsysmonError('No collection nodes available.') cur_node = self.nodes[position] if isinstance(cur_node, psysmon.core.packageNodes.LooperCollectionNode): # Add the node to the looper node. node.parentCollection = self cur_node.add_child(node, position=looper_pos) else: raise PsysmonError( 'The selected collection node is not a looper node.')
def removeNodeFromCollection(self, position): '''Remove a node from the active collection. Parameters ---------- position : Integer The position of the node to remove. Raises ------ PsysmonError : :class:`PsysmonError` Error raised when no active collection is present. ''' if self.activeCollection: self.activeCollection.popNode(position) else: raise PsysmonError('No active collection found!')
def executeNode(self, position): '''Execute the node at *position* of the active collection. Executing a node means calling the :meth:`~psysmon.core.packageNodes.CollectionNode.execute` method of the :class:`~psysmon.core.packageNodes.CollectionNode` instance. Parameters ---------- position : Integer The position of the node to edit. Raises ------ PsysmonError : :class:`PsysmonError` Error raised when no active collection is present. ''' if self.activeCollection: self.activeCollection.executeNode(position) else: raise PsysmonError('No active collection found!')
def getNodeFromCollection(self, position): '''Get the node at *position* from the active collection. Parameters ---------- position : Integer The position of the node to get. Returns ------- collectionNode : :class:`~psysmon.core.packageNodes.Collection` The collection node at *position* in the active collection. Raises ------ PsysmonError : :class:`PsysmonError` Error raised when no active collection is present. ''' if self.activeCollection: return self.activeCollection[position] else: raise PsysmonError('No active collection found!')
def addNode2Collection(self, node, position): '''Add a collection node to the active collection. The *node* is added to the currently active collection at *position* using the :meth:`~psysmon.core.base.Collection.addNode` method of the :class:`~psysmon.core.base.Collection` class. Parameters ---------- nodeTemplate : :class:`~psysmon.core.packageNodes.CollectionNode` The node to be added to the collection. position : Integer The position before which to add the node to the collection. -1 to add it at the end of the collection (default). Raises ------ PsysmonError : :class:`~psysmon.core.util.PsysmonError` Error raised when no active collection is present. ''' if self.activeCollection: self.activeCollection.addNode(node, position) else: raise PsysmonError('No active collection found!')
def createPsysmonProject(self, name, base_dir, db_host, user_name, user_pwd, author_name, author_uri, agency_name, agency_uri): '''Create a pSysmon project. The pSysmon project is the starting point when working with pSysmon. After creating an instance of the :class:`~psysmon.core.project.Project` class, the project is initialized by the following steps: - Connect to the database. If this fails, a PsysmonError is raised. - Create the project directory structure in the project base directory. - Create the project database structure. - Initialize the project for the currently active user. - Save the project. Parameters ---------- name : String The name of the project. baseDir : String The base directory of the pSysmon project. Inside the base directory, the pSysmon project directory is created. dbHost : String The database host on which the mysql server for the project is running. user : String The pSysmon user related to the project as the *admin*. userPwd : String The password of *user*. Returns ------- projectCreated : Boolean Indicates if the project has been created succesfully. (True: project created; False: project not created) Raises ------ PsysmonError : :class:`~psysmon.core.util.PsysmonError` Error while connecting to the database. ''' # Create the admin user for the project. admin_user = psysmon.core.project.User(user_name = user_name, user_mode = 'admin', user_pwd = user_pwd, author_name = author_name, author_uri = author_uri, agency_name = agency_name, agency_uri = agency_uri ) # Create the project instance. self.project = psysmon.core.project.Project(psybase = self, name = name, user = admin_user, base_dir = base_dir, dbHost = db_host) # When creating a project, set the active user to the user creating # the project (which is the *admin* user). self.project.activeUser = self.project.user[0] try: self.project.connect2Db() except Exception as e: msg = "Can't connect to the database.\n The database returned the following message:\n%s" % e raise PsysmonError(msg) # If the connection fails, don't go on with the project creation. self.project.createDirectoryStructure() self.project.createDatabaseStructure(self.packageMgr.packages) # By default add a psysmon Database waveclient with the name 'main # client'. Add the waveclient only, if the required packages are # present. required_packages = ['obspyImportWaveform', 'geometry'] available_packages = [x for x in self.packageMgr.packages.keys() if x in required_packages] if required_packages == available_packages: waveclient = PsysmonDbWaveClient('db client', self.project) self.project.addWaveClient(waveclient) self.project.defaultWaveclient = 'db client' self.project.save_json() return True
def executeCollection(self, project): '''Execute the active collection. Start a new process to execute the currently active collection. A deep copy of the collection instance is create and this copy is executed. This is done to prevent runtime interactions when editing the collection node properties after a collection has been executed. The start of the execution is logged and a state.collection.execution message is sent to notify eventual listeners of the starting of the execution. Parameters ---------- project : :class:`Project` The pSysmon project. Raises ------ PsysmonError : :class:`PsysmonError` Error raised when no active collection is present. ''' def processChecker(process, procName): from time import sleep # The time interval to check for process messages [s]. checkInterval = 2 # The timeout limit. After this timeout the process is # marked as "not responding". The timeout interval should # be larger than the process's heartbeat interval. [s] timeout = 10 procRunning = True isZombie = False self.logger.debug("Checking process...") lastResponse = 0 while procRunning: #self.logger.debug("Waiting for message...") procStatus = proc.poll() #self.logger.debug('procStatus: %s', procStatus) if procStatus != None: procRunning = False #self.logger.debug('Process %d has stopped with return code %s.', proc.pid, procStatus) msgTopic = 'state.collection.execution' msg['state'] = 'stopped' msg['pid'] = proc.pid msg['procName'] = procName msg['curTime'] = datetime.now() CallAfter(pub.sendMessage, msgTopic, msg=msg) else: #self.logger.debug('Process %d is still running.', proc.pid) msgTopic = 'state.collection.execution' msg['state'] = 'running' msg['pid'] = proc.pid msg['procName'] = procName msg['curTime'] = datetime.now() CallAfter(pub.sendMessage, msgTopic, msg=msg) sleep(checkInterval) # Here is some code using the pipe and the heartbeat of the # collection. I think this caused some unexpected crashes of # the GUI. Might be some event loop race conditions. #if parentEnd.poll(checkInterval): #msg = parentEnd.recv() ##print msg #self.logger.debug("Received message: [%s]: %s" % (msg['state'], msg['msg'])) # # # Send the message to the system. #msgTopic = "state.collection.execution" # msg['isError'] = False ##pub.sendMessage(msgTopic, msg) #lastResponse = 0 #if msg['state'] == 'stopped': # procRunning = False # else: #lastResponse += checkInterval # self.logger.debug("No message received.") #if lastResponse > timeout: #procRunning = False # isZombie = True #self.logger.debug("End checking process %d.", proc.pid) if self.activeCollection: if not project.threadMutex: project.threadMutex = thread.allocate_lock() col2Proc = copy.deepcopy(self.activeCollection) curTime = datetime.now() timeStampString = datetime.strftime(curTime, '%Y%m%d_%H%M%S_%f') processName = col2Proc.name + "_" + timeStampString col2Proc.procName = col2Proc.name + "_" + timeStampString msg = "Executing collection " + col2Proc.name + "with process name: " + processName + "." self.logger.info(msg) msgTopic = "state.collection.execution" msg = {} msg['state'] = 'starting' msg['startTime'] = curTime msg['isError'] = False msg['pid'] = None msg['procName'] = col2Proc.procName pub.sendMessage(msgTopic, msg=msg) #(parentEnd, childEnd) = multiprocessing.Pipe() self.logger.debug("process name: %s" % col2Proc.procName) #thread.start_new_thread(processChecker, (p, parentEnd, project.threadMutex)) # Store all the needed data in a temporary file. #import tempfile import shelve #tmpDir = tempfile.gettempdir() filename = os.path.join( project.tmpDir, col2Proc.procName + '.ced') # ced for Collection Execution Data db = shelve.open(filename, flag='n') db['project'] = project db['collection'] = col2Proc db['package_directories'] = project.psybase.packageMgr.packageDirectories db['waveclient'] = [(x.name, x.mode, x.pickle_attributes) for x in project.waveclient.itervalues()] db['project_server'] = project.psybase.project_server db.close() # Start the collection using the cecClient as a subprocess. cecPath = os.path.dirname(os.path.abspath(psysmon.core.__file__)) #proc = subprocess.Popen([sys.executable, os.path.join(cecPath, 'cecSubProcess.py'), filename, col2Proc.procName], # stdout=subprocess.PIPE) proc = subprocess.Popen([ sys.executable, os.path.join(cecPath, 'cecSubProcess.py'), filename, col2Proc.procName ]) msgTopic = "state.collection.execution" msg = {} msg['state'] = 'started' msg['startTime'] = curTime msg['isError'] = False msg['pid'] = proc.pid msg['procName'] = col2Proc.procName pub.sendMessage(msgTopic, msg=msg) thread.start_new_thread(processChecker, (proc, col2Proc.procName)) else: raise PsysmonError('No active collection found!')