def refreshModeLabel(self): if 'automaticsolver' in self.parameters and self.parameters[ 'automaticsolver']: self.modeLabel.setText("<b>%s</b>" % _('AUTO ON')) elif 'assistedsolver' in self.parameters and self.parameters[ 'assistedsolver']: self.modeLabel.setText("<b>%s</b>" % _('ASSIST ON')) else: self.modeLabel.setText("<b>%s</b>" % _('MANUAL')) self.modeLabel.show()
def onStart(self): if not os.path.exists(os.path.join('..', 'inpout32.dll')): reply = QtWidgets.QMessageBox.question( None, _('Error'), _("The file '%s' was not found in the current directory. It will not be possible to send trigger events. Do you want to continue?" ) % 'inpout32.dll', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.No: self.parent().onEnd() return
def load_plugins(self): """Inform the Main() class with plugins information""" # For each plugin that is present in the ./Plugins directory for thisfile in os.listdir(PLUGINS_PATH): # If it is a python file... if thisfile.endswith(".py"): # Retrieve the plugin name plugin_name = thisfile.replace(".py", "") module = SourceFileLoader(plugin_name, os.path.join(self.working_directory, PLUGINS_PATH, thisfile)).load_module() # module = imp.load_source(plugin_name, os.path.join(self.working_directory, PLUGINS_PATH, thisfile)) # If the plugin has defined a Task class, log it if hasattr(module, "Task"): task = module.Task(self) # Check if a parameters dictionary is present if not hasattr(task, 'parameters'): print(_("Plugin '%s' is invalid (no parameters data)") % (plugin_name)) continue # Initialize a dictionary to store plugin information plugin_name = plugin_name.lower() self.PLUGINS_TASK[plugin_name] = {} self.PLUGINS_TASK[plugin_name]['class'] = task self.PLUGINS_TASK[plugin_name]['TIME_SINCE_UPDATE'] = 0 self.PLUGINS_TASK[plugin_name]['taskRunning'] = False self.PLUGINS_TASK[plugin_name]['taskPaused'] = False self.PLUGINS_TASK[plugin_name]['taskVisible'] = False # Store potential plugin information if 'taskupdatetime' in task.parameters: self.PLUGINS_TASK[plugin_name]["UPDATE_TIME"] = task.parameters['taskupdatetime'] else: self.PLUGINS_TASK[plugin_name]["UPDATE_TIME"] = None if hasattr(task, "keyEvent"): self.PLUGINS_TASK[plugin_name]["RECEIVE_KEY"] = True else: self.PLUGINS_TASK[plugin_name]["RECEIVE_KEY"] = False self.PLUGINS_TASK[plugin_name]["NEED_LOG"] = True if hasattr(task, "onLog") else False task.hide() else: print(_("Plugin '%s' is not recognized") % plugin_name)
def validateScenario(self, scenario_content): """Check that the scenario follows a set of criteria. Output the corresponding boolean value""" # Browse the loaded task, ignoring the __main__ one for checktask in self.loadedTasks: if checktask == "__main__": continue else: howmanyentries = 0 entries = [] for k in scenario_content.keys(): if checktask in scenario_content[k]: for t in scenario_content[k][checktask]: howmanyentries += 1 entries.append(t) # Does the scenario contains one or more commands for the task at hand ? if howmanyentries == 0: self.showCriticalMessage(_("No entry has been found for the '%s' plugin. Check the scenario file") % checktask ) return False # Are the start/stop commands present ? for thiscommand in ['start']: if thiscommand not in [thisentry[0] for thisentry in entries]: self.showCriticalMessage(_("The '%s' plugin does not admit a %s command. Please fix that") % (checktask, thiscommand)) return False # Check that the last command of the scenario is an 'end' try: lasttime, lasttask = [(k, v) for k, v in sorted(scenario_content.items())][-1] lasttask, lastcmd = [(k, v) for k, v in lasttask.items()][-1] # Is there more than one task? if (len(scenario_content[lasttime].keys()) > 1): raise Exception() if 'end' not in lastcmd[0]: raise Exception() except: self.showCriticalMessage(_("The scenario should terminate with a 'end' command")) return False # Check there is at least one task in the scenario # Do not take into account the special case of the "__main__" task if len(self.loadedTasks) <= 1: self.showCriticalMessage(_("No task is started!")) return False return True
def readSample(self): res = iViewXAPI.iV_GetSample(byref(sampleData)) self.samples_to_test.append(res) if len(self.samples_to_test ) == self.test_window * systemData.samplerate: if (float(self.samples_to_test.count(1)) / len(self.samples_to_test)) < 1 - self.sample_threshold: self.parent().showCriticalMessage( _("Too many bad samples during the last %s seconds") % +str(self.test_window)) self.samples_to_test = [] self.sampleLog.getSmiStamp(sampleData.timestamp) self.fromSample = { 'gazeLX': sampleData.leftEye.gazeX, 'gazeLY': sampleData.leftEye.gazeY, 'positionLX': sampleData.leftEye.eyePositionX, 'positionLY': sampleData.leftEye.eyePositionY, 'positionLZ': sampleData.leftEye.eyePositionZ, 'diamL': sampleData.leftEye.diam, 'gazeRX': sampleData.rightEye.gazeX, 'gazeRY': sampleData.rightEye.gazeY, 'positionRX': sampleData.rightEye.eyePositionX, 'positionRY': sampleData.rightEye.eyePositionY, 'positionRZ': sampleData.rightEye.eyePositionZ, 'diamR': sampleData.rightEye.diam } sample = [ self.fromSample[entry] for entry in self.parameters['getFromSample'] ] self.sampleLog.addLine(sample)
def setParameterVariable(self, task, taskclass, variable, value): """Set a variable to its value, after having convert it to the correct type""" current = getattr(taskclass, PARAMETERS_VARIABLE) if not current: return False command = variable.split("-") for e in range(0, len(command) - 1): # range(0,0) = [] current = current.get(command[e], None) t = type(current[command[-1]]) if current[command[-1]] is None: print( _("Warning: None Value in self.parameters. This should not happen!" )) # Must test booleen first because booleen are also int (e.g., True == 1 is True) if isinstance(current[command[-1]], bool): if value.lower() == 'true': current[command[-1]] = True elif value.lower() == 'false': current[command[-1]] = False elif isinstance(current[command[-1]], int): current[command[-1]] = int(value) elif isinstance(current[command[-1]], float): current[command[-1]] = float(value) elif isinstance(current[command[-1]], str) or current[command[-1]] is None: current[command[-1]] = value else: try: current[command[-1]] = ast.literal_eval(value) except: self.showCriticalMessage( _("Unable to evaluate a value! This should not happen!")) # Retrieve changing value that are handled by MATB.py (e.g., title, taskupdatetime) if variable == 'title': self.PLUGINS_TASK[task]['ui_label'].setText(value) elif variable == 'taskupdatetime' and isinstance( current[command[-1]], int): self.PLUGINS_TASK[task]['UPDATE_TIME'] = int(value)
def onStart(self): # Define a QLabel object to potentially display automation mode self.modeFont = QtGui.QFont("sans-serif", int(self.height() / 35.), QtGui.QFont.Bold) self.modeLabel = QtWidgets.QLabel(self) self.modeLabel.setGeometry( QtCore.QRect(0.60 * self.width(), 0.60 * self.height(), 0.40 * self.width(), 0.40 * self.height())) self.modeLabel.setAlignment(QtCore.Qt.AlignCenter) self.modeLabel.setFont(self.modeFont) self.parameters['displaytitle'] = True # Set a WTrack Qt object self.widget = WTrack.WTrack(self, self.parameters['equalproportions']) # Create a layout for the widget layout = QtWidgets.QGridLayout() # Add the WTrack object to the layout layout.addWidget(self.widget) self.setLayout(layout) pygame.joystick.init() # Check for a joystick device if pygame.joystick.get_count() == 0: self.parent().showCriticalMessage( _("Please plug a joystick for the '%s' task!") % (self.parameters['title'])) else: self.my_joystick = pygame.joystick.Joystick(0) self.my_joystick.init() # Log some task information once self.buildLog(["STATE", "TARGET", "X", str(0.5)]) self.buildLog(["STATE", "TARGET", "Y", str(0.5)]) self.buildLog([ "STATE", "TARGET", "RADIUS", str(self.parameters['targetradius']) ]) msg = _('AUTO') if self.parameters['automaticsolver'] else _('MANUAL') self.buildLog(["STATE", "", "MODE", msg])
def onUpdate(self): if self.parameters['displayautomationstate']: self.refreshModeLabel() else: self.modeLabel.hide() if self.parameters['resetperformance'] is not None: if self.parameters['resetperformance'] in ['last', 'global']: for this_index in self.performance[self.parameters['resetperformance']]: self.performance[self.parameters['resetperformance']][this_index] = 0 else: self.parent().showCriticalMessage(_("%s : wrong argument in sysmon;resetperformance") % self.parameters['resetperformance']) self.parameters['resetperformance'] = None # For each light button, refresh name for index, k in enumerate(self.parameters['lights'].keys()): self.parameters['lights'][k]['ui'].light.setText(self.parameters['lights'][k]['name']) # For each scale gauge, refresh name for k in sorted(self.parameters['scales'].keys()): self.parameters['scales'][k]['ui'].label.setText(self.parameters['scales'][k]['name']) # 1. Check failures only if no failure is already occuring # (currently prevents double-failure !) if len(self.currentFailure) == 0: for lights_or_scales in ['lights', 'scales']: for thisGauge in self.parameters[lights_or_scales].keys(): # If a failure is to be initiated if (lights_or_scales == 'scales' and self.parameters[lights_or_scales][thisGauge]['failure'] in ['up', 'down']) or (lights_or_scales == 'lights' and self.parameters[lights_or_scales][thisGauge]['failure']): # Start it... self.startFailure(lights_or_scales, thisGauge) # ...and leave the loop break # 2. Vary position of each scale, depending on its state (up, down, no) for thisScale in self.parameters['scales'].keys(): self.parameters['scales'][thisScale][ 'position'] = self.computeNextPosition(thisScale) # 3. Refresh visual display for thisScale in self.parameters['scales'].keys(): if 'ui' in self.parameters['scales'][thisScale]: self.parameters['scales'][thisScale][ 'ui'].style = self.parameters['scalestyle'] self.parameters['scales'][thisScale][ 'ui'].position = self.parameters['scales'][thisScale]['position'] for thisLight in self.parameters['lights'].keys(): if 'ui' in self.parameters['lights'][thisLight]: self.parameters['lights'][thisLight]['ui'].refreshState( self.parameters['lights'][thisLight]['on'])
def __init__(self, parent): super(Task, self).__init__(parent) # SCHEDULING PARAMETERS ### self.parameters = { 'title': 'Scheduling', 'taskplacement': "topright", 'taskupdatetime': 1000 } # Potentially translate task title self.parameters['title'] = _(self.parameters['title'])
def showCriticalMessage(self, msg): """Display a critical message (msg) in a QMessageBox Qt object before exiting""" flags = QtWidgets.QMessageBox.Abort flags |= QtWidgets.QMessageBox.StandardButton.Ignore result = QtWidgets.QMessageBox.critical( self, VERSIONTITLE + " " + _("Error"), msg, flags) if result == QtWidgets.QMessageBox.Abort: self.onEnd() sys.exit()
def __init__(self, parent): super(Task, self).__init__(parent) # COMMUNICATIONS PARAMETERS ### self.parameters = { 'title': 'Communications', 'taskplacement': "bottomleft", 'taskupdatetime': 50, 'callsignregex': '[A-Z][A-Z][A-Z]\d\d\d', 'owncallsign': '', 'othercallsign': [], 'othercallsignnumber': 5, 'airbandminMhz': 108, 'airbandmaxMhz': 137, 'airbandminvariationMhz': 5, 'airbandmaxvariationMhz': 6, 'radiostepMhz': 0.1, 'voicegender': 'male', 'voiceidiom': 'french', 'radioprompt': '', 'promptlist': ['NAV_1', 'NAV_2', 'COM_1', 'COM_2'], 'automaticsolver': False, 'displayautomationstate': False, } # Potentially translate task title self.parameters['title'] = _(self.parameters['title']) # Preallocate a dictionary to handle radios information self.parameters['radios'] = {} for this_destination in ['own', 'other']: self.parameters['radios'][this_destination] = {} for r, this_radio in enumerate(self.parameters['promptlist']): self.parameters['radios'][this_destination][r] = { 'name': this_radio, 'currentfreq': None, 'targetfreq': None, 'lasttarget': None, 'index': r } self.parameters['frequencyresolutionKhz'] = 100 # Use list to learn about already created callsign/radio... to minimize # repetitions self.letters = string.ascii_uppercase self.digits = string.digits self.lastradioselected = '' # Set a boolean to handle automatic solving self.automaticsolving = False
def __init__(self, parent): super(Task, self).__init__(parent) # RESMAN PARAMETERS ### self.parameters = { 'taskplacement': 'bottommid', 'taskupdatetime': 2000, 'title': 'Resources management', 'heuristicsolver': False, 'assistedsolver': False, 'displayautomationstate': False, 'pumpcoloroff' : '#AAAAAA', 'pumpcoloron' : '#00FF00', 'pumpcolorfailure' : '#FF0000', 'tolerancelevel':500, 'displaytolerance':True, 'resetperformance':None, 'pump': {'1': {'flow': 800, 'state': 0, 'keys': [QtCore.Qt.Key_1], 'hide': 0}, '2': {'flow': 600, 'state': 0, 'keys': [QtCore.Qt.Key_2, 233], 'hide': 0}, '3': {'flow': 800, 'state': 0, 'keys': [QtCore.Qt.Key_3], 'hide': 0}, '4': {'flow': 600, 'state': 0, 'keys': [QtCore.Qt.Key_4],'hide': 0}, '5': {'flow': 600, 'state': 0, 'keys': [QtCore.Qt.Key_5], 'hide': 0}, '6': {'flow': 600, 'state': 0, 'keys': [QtCore.Qt.Key_6], 'hide': 0}, '7': {'flow': 400, 'state': 0, 'keys': [QtCore.Qt.Key_7, 232], 'hide': 0}, '8': {'flow': 400, 'state': 0, 'keys': [QtCore.Qt.Key_8], 'hide': 0}}, 'tank': { 'a': {'level': 2500, 'max': 4000, 'target': 2500, 'depletable': 1, 'lossperminute': 800, 'hide': 0}, 'b': {'level': 2500, 'max': 4000, 'target': 2500, 'depletable': 1, 'lossperminute': 800, 'hide': 0}, 'c': {'level': 1000, 'max': 2000, 'target': None, 'depletable': 1, 'lossperminute': 0, 'hide': 0}, 'd': {'level': 1000, 'max': 2000, 'target': None, 'depletable': 1, 'lossperminute': 0, 'hide': 0}, 'e': {'level': 3000, 'max': 4000, 'target': None, 'depletable': 0, 'lossperminute': 0, 'hide': 0}, 'f': {'level': 3000, 'max': 4000, 'target': None, 'depletable': 0, 'lossperminute': 0, 'hide': 0} } } self.performance = { 'total' : {}, 'last' : {} } for this_cat in self.performance: for this_tank in self.parameters['tank']: if self.parameters['tank'][this_tank]['target'] is not None: self.performance[this_cat][this_tank+'_in'] = 0 self.performance[this_cat][this_tank+'_out'] = 0 # Potentially translate task title self.parameters['title'] = _(self.parameters['title'])
def __init__(self, parent): super(Task, self).__init__(parent) self.resman = None # PUMPSTATUS PARAMETERS ### self.parameters = { 'taskplacement': 'bottomright', 'taskupdatetime': 1000, 'title': 'Pump status' } # Potentially translate task title self.parameters['title'] = _(self.parameters['title'])
def scenarioUpdateTime(self): """Increment time (h,m,s) and get the corresponding string chain (H:MM:SS)""" m, s = divmod(self.totalElapsedTime_ms / 1000.0, 60) h, m = divmod(m, 60) if h > 9: self.showCriticalMessage(_("Timing overflow. This should not happen!")) s = "%d:%02d:%02d" % (h, m, s) # If scenarioTimeStr need to be updated (1 second passed), update it # and try to execute scenario contents again (see the executeScenario fucntion above) if s != self.scenarioTimeStr: self.scenarioTimeStr = s self.executeScenario(self.scenarioTimeStr)
def scheduler(self): """Manage the passage of time. Block time during pauses""" current_time_microsec = self.default_timer() elapsed_time_ms = (current_time_microsec - self.last_time_microsec) * 1000.0 if elapsed_time_ms < MAIN_SCHEDULER_INTERVAL: return self.last_time_microsec = current_time_microsec # The main experiment is in pause, so do not increment time if self.experiment_pause or not self.experiment_running: return # Time increment in case the experiment is running self.totalElapsedTime_ms += elapsed_time_ms # If experiment is effectively running, browse plugins and refresh them (execute their onUpdate() method) as a function of their own UPDATE_TIME for plugin_name in self.PLUGINS_TASK: if plugin_name in self.loadedTasks: if self.PLUGINS_TASK[plugin_name][ "UPDATE_TIME"] is not None and not self.PLUGINS_TASK[ plugin_name]['taskPaused']: self.PLUGINS_TASK[plugin_name][ "TIME_SINCE_UPDATE"] += elapsed_time_ms if self.PLUGINS_TASK[plugin_name][ "TIME_SINCE_UPDATE"] >= self.PLUGINS_TASK[ plugin_name]["UPDATE_TIME"]: self.PLUGINS_TASK[plugin_name]["TIME_SINCE_UPDATE"] = 0 if hasattr(self.PLUGINS_TASK[plugin_name]["class"], "onUpdate"): self.PLUGINS_TASK[plugin_name]["class"].onUpdate() else: self.showCriticalMessage( _("Plugin '%s' requires an onUpdate() function!" ) % plugin_name) # Potentially logs an arbitrary message in 'messagetolog' if len(self.parameters['messagetolog']) > 0: msg = ["MAIN", "LOG", self.parameters['messagetolog']] self.mainLog.addLine(msg) self.parameters['messagetolog'] = '' self.scenarioUpdateTime()
def __init__(self, parent): super(Task, self).__init__(parent) self.my_joystick = None # TRACK PARAMETERS ### self.parameters = { 'taskplacement': 'topmid', 'taskupdatetime': 20, 'title': 'Tracking', 'cursorcolor': '#0000FF', 'cursorcoloroutside': '#0000FF', 'automaticsolver': False, 'displayautomationstate': True, 'assistedsolver': False, 'targetradius': 0.1, 'joystickforce': 1.0, 'cutofffrequency': 0.06, 'equalproportions': True, 'resetperformance': None, } self.performance = { 'total': { 'time_in_ms': 0, 'time_out_ms': 0, 'points_number': 0, 'deviation_mean': 0 }, 'last': { 'time_in_ms': 0, 'time_out_ms': 0, 'points_number': 0, 'deviation_mean': 0 } } # Potentially translate task title self.parameters['title'] = _(self.parameters['title'])
def onStart(self): # Retrieve scenario events for the tracking and the communication tasks self.tracking_schedule, self.communication_schedule = self.getSchedule( ) # Get the time of the last event maxTime_sec = max([ self.dateStringToSecondInteger(this_time) for this_time, value in self.parent().scenariocontents.items() ]) # Set a Qt layout layout = QtWidgets.QGridLayout() # Set a WScheduler Qt object self.widget = WScheduler.WScheduler(self, self.tracking_schedule, self.communication_schedule, maxTime_sec, _('Elapsed Time')) layout.addWidget(self.widget) self.setLayout(layout) # Compute tasks progression as a function of current scenario time self.widget.getProgression(self.parent().scenarioTimeStr)
def __init__(self, scenario_fullpath): super(Main, self).__init__(parent=None) self.registeredTaskTimer = [] self.parameters = { 'showlabels': True, 'allowescape': True, 'messagetolog': '' } # Preallocate a dictionary to store plugins information self.PLUGINS_TASK = {} # Store working directory and scenario names # Use correct directory if running in cxFreeze (frozen) if getattr(sys, 'frozen', False): self.working_directory = os.getcwd() else: self.working_directory = os.path.dirname(os.path.abspath(__file__)) self.scenario_shortfilename = os.path.split(scenario_fullpath)[1] self.scenario_directory = os.path.split(scenario_fullpath)[0] self.scales_directory = os.path.join(self.working_directory, SCALES_PATH) self.instructions_directory = os.path.join(self.working_directory, INSTRUCTIONS_PATH) # Check that the plugins folder exists if not os.path.exists(os.path.join(self.working_directory, PLUGINS_PATH)): self.showCriticalMessage( _("Plugins directory does not exist. Check that its name is correct")) # Create a ./Logs folder if it does not exist if not os.path.exists(os.path.join(self.working_directory, LOGS_PATH)): os.mkdir(os.path.join(self.working_directory, LOGS_PATH)) # Create a filename for the log file # Corresponds to : scenario name + date + .log LOG_FILE_NAME = os.path.join(self.scenario_shortfilename.replace(".txt", "").replace( " ", "_") + "_" + datetime.datetime.now().strftime("%Y%m%d_%H%M") + ".log") self.LOG_FILE_PATH = os.path.join(self.working_directory, LOGS_PATH, LOG_FILE_NAME) # Initialize a Logger instance with this log filename (see Helpers/Logger.py) self.mainLog = Logger.Logger(self, self.LOG_FILE_PATH) self.mainLog.addLine(['MAIN', 'INFO', 'SCENARIO', 'FILENAME', self.scenario_shortfilename]) # Initialize two timing variables self.scenarioTimeStr = None self.totalElapsedTime_ms = 0 # Compute screen dimensions # Screen index can be changed just below screen_idx = 0 # Here, you can change the screen index screen = QtGui.QGuiApplication.screens()[screen_idx] self.screen_width = screen.geometry().width() self.screen_height = screen.geometry().height() # screen_widths = [QtWidgets.QApplication.desktop().screenGeometry(i).width() for i in range(0, QtWidgets.QApplication.desktop().screenCount())] # # self.screen_index = screen_widths.index(self.screen_width) # self.screen_index).height() # # # Get current screen # current_screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos()) #centerPoint = QtWidgets.QApplication.desktop().screenGeometry(current_screen).center() #self.move(centerPoint) self.setFixedSize(self.screen_width, self.screen_height) # Log the computed screen size values self.mainLog.addLine( ['MAIN', 'INFO', 'SCREENSIZE', 'WIDTH', str(self.screen_width)]) self.mainLog.addLine( ['MAIN', 'INFO', 'SCREENSIZE', 'HEIGHT', str(self.screen_height)]) # The following dictionary contains all the information about sizes and placements. # control_top/left is the top/left margin # control_height/width defines the plugin height/width self.placements = { 'fullscreen': {'control_top': 0, 'control_left': 0, 'control_height': self.screen_height, 'control_width': self.screen_width}, 'topleft': {'control_top': 0, 'control_left': 0, 'control_height': self.screen_height / 2, 'control_width': self.screen_width * (7.0 / 20.1)}, 'topmid': {'control_top': 0, 'control_left': self.screen_width * ( 5.9 / 20.1), 'control_height': self.screen_height / 2, 'control_width': self.screen_width * (10.7 / 20.1)}, 'topright': {'control_top': 0, 'control_left': self.screen_width * (5.9 / 20.1) + self.screen_width * ( 10.6 / 20.1), 'control_height': self.screen_height / 2, 'control_width': self.screen_width * (3.6 / 20.1)}, 'bottomleft': {'control_top': self.screen_height / 2, 'control_left': 0, 'control_height': self.screen_height / 2, 'control_width': self.screen_width * (5.9 / 20.1)}, 'bottommid': {'control_top': self.screen_height / 2, 'control_left': self.screen_width * ( 5.9 / 20.1), 'control_height': self.screen_height / 2, 'control_width': self.screen_width * (10.7 / 20.1)}, 'bottomright': {'control_top': self.screen_height / 2, 'control_left': self.screen_width * (5.9 / 20.1) + self.screen_width * (10.6 / 20.1), 'control_height': self.screen_height / 2, 'control_width': self.screen_width * (3.6 / 20.1)} } # Turn off Caps Lock and on Num Lock (for resman)... if possible (only Windows until now) if platform.system() == "Windows": self.turnKey(0x14, False) # Caps Lock Off self.turnKey(0x90, True) # Num Lock On # Preallocate variables to handle experiment pauses self.experiment_pause = False self.experiment_running = True # Initialize plugins self.load_plugins() self.place_plugins_on_screen() self.loadedTasks = [] # Load scenario file self.scenariocontents = self.loadScenario(scenario_fullpath)
def getCommand(self, lineNumber, lineContent): """Parse lineContent to time, task and command variables. There are 3 possible syntax: 0:00:00;end => call onEnd in main script 0:00:00;track;start => call onStart in the track plugins 0:00:00;track;variable;value=> modify the parameters in the track plugins """ # Retrieve line content, removing white space and using semi-colon as delimiter lineList = lineContent.strip().split(';') # Check if the length of lineList is correct if not 1 < len(lineList) < 5: self.showCriticalMessage(_("Error. Number of value is incorrect. See line")+" " + str(lineNumber) + ' (' + str(lineContent) + ')') return None, None, None # Manage the special case of main (0:00:00;start) elif len(lineList) == 2 or lineList[1] in self.parameters.keys(): lineList.insert(1, "__main__") time, task, command = lineList[0], lineList[1], lineList[2:] # manage deprecated command: if task=="sysmon" and command[0]=="feedbackduration": command[0] = "feedbacks-positive-duration" if task == "__main__": taskclass = self elif task in self.PLUGINS_TASK: taskclass = self.getPluginClass(task) else: self.showCriticalMessage( _("'%s' plugin: unknown\n\nLINE: %s") % (task, str(lineNumber))) return None, None, None if len(time)!=7: self.showCriticalMessage( _("'%s' plugin: wrong time format\n\nLINE: %s") % (task, str(lineNumber))) # When only one command, concatenate it with the 'on' chain (e.g. start leads to onStart) # onCommand functions are called into the plugins if len(command) == 1: functionname = "on" + command[0].capitalize() # If the onCommand does not exist... if not hasattr(taskclass, functionname) and functionname not in ["onStart", "onStop", "onHide", "onPause", "onShow", "onResume"]: # signal it. errorcaller = "" if task != "__main__": errorcaller = "' in '" + task self.showCriticalMessage( _("'%s' not found!\n\nLINE: %s") % (functionname + errorcaller, str(lineNumber))) return None, None, None else: return time, task, command # For the other variables, check that there are corrects (available in the plugin) else: # if taskclass == self: # self.showCriticalMessage( # _("The main script parameters should not be called, use a task instead!\n\nLINE: %s") % str(lineNumber)) # return None, None, None if not hasattr(taskclass, PARAMETERS_VARIABLE): self.showCriticalMessage( _("'%s' should have a parameters dictionary!\n\nLINE: %s") % (task, str(lineNumber))) return None, None, None if not self.testParameterVariable(task, taskclass, command[0]): self.showCriticalMessage( _("Variable '%s' unknown in task '%s'\n\nLINE: %s") % (str(command [0]), task, str(lineNumber))) return None, None, None return time, task, command
def __init__(self, parent): super(Task, self).__init__(parent) # SYSMON PARAMETERS ### self.parameters = { 'title': 'System monitoring', 'taskplacement': 'topleft', 'taskupdatetime': 200, 'alerttimeout': 10000, 'automaticsolver': False, 'automaticsolverdelay': 1 * 1000, 'displayautomationstate': False, 'allowanykey': False, 'scalesnumofboxes': 11, 'safezonelength': 3, 'feedback' : True, 'feedbackduration': 1.5 * 1000, 'scalestyle' : 1, # could be defined at the scale level 'resetperformance':None, 'lights': { '1': {'name': 'F5', 'failure': False, 'on': True, 'default': 'on', 'oncolor': "#009900", 'keys': [QtCore.Qt.Key_F5]}, '2': {'name': 'F6', 'failure': False, 'on': False, 'default': 'off', 'oncolor': "#FF0000", 'keys': [QtCore.Qt.Key_F6]} }, 'scales': { '1': {'name': 'F1', 'failure': 'no', 'keys': [QtCore.Qt.Key_F1]}, '2': {'name': 'F2', 'failure': 'no', 'keys': [QtCore.Qt.Key_F2]}, '3': {'name': 'F3', 'failure': 'no', 'keys': [QtCore.Qt.Key_F3]}, '4': {'name': 'F4', 'failure': 'no', 'keys': [QtCore.Qt.Key_F4]} } } self.performance = { 'total' : {'hit_number': 0, 'miss_number':0, 'fa_number':0}, 'last' : {'hit_number': 0, 'miss_number':0, 'fa_number':0} } # Potentially translate task title self.parameters['title'] = _(self.parameters['title']) # Set the initial position of the cursor (middle) for this_scale in self.parameters['scales'].keys(): self.parameters['scales'][this_scale][ 'position'] = self.parameters['scalesnumofboxes'] / 2 # Define two failures zones (up, down) totalRange = range(1, self.parameters['scalesnumofboxes'] - 1) centerPosition = totalRange[len(totalRange) / 2] self.zones = { 'up': [k for k in totalRange if k < (centerPosition - (self.parameters['safezonelength'] - 1) / 2)], 'down': [k for k in totalRange if k > (centerPosition + (self.parameters['safezonelength'] - 1) / 2)] } # Define the "safe" zone (no) self.zones['no'] = [k for k in totalRange if k not in self.zones['up'] if k not in self.zones['down']] # Set a list of accepted keys depending on lights and scales parameters scales_keys = [self.parameters['scales'][id]["keys"][0] for id in self.parameters['scales'].keys()] lights_keys = [self.parameters['lights'][id]["keys"][0] for id in self.parameters['lights'].keys()] self.accepted_keys = scales_keys + lights_keys
def place_plugins_on_screen(self): """Compute size and position of each plugin, in a 2 x 3 canvas, as a function of the taskplacement variable of each plugin""" # Compute some sizes as a function of screen height LABEL_HEIGHT = self.screen_height / 27 font_size_pt = int(LABEL_HEIGHT / (5 / 2)) # Adapt top margin and height to the presence/absence of plugin labels if self.parameters['showlabels']: for k in self.placements.keys(): self.placements[k]['control_top'] += LABEL_HEIGHT self.placements[k]['control_height'] -= LABEL_HEIGHT # Browse plugins to effectively size and place them for plugin_name in self.PLUGINS_TASK: plugin = self.getPluginClass(plugin_name) # Check if the plugin has a taskplacement parameter if 'taskplacement' not in plugin.parameters or not plugin.parameters['taskplacement']: print(_("Plugin '%s' has no placement data. It will not be displayed") % plugin_name) continue # If so, retrieve it placement = plugin.parameters['taskplacement'] # Plugin placement must match one value of the self.placement dictionary if placement in self.placements.keys(): self.control_top = self.placements[placement]['control_top'] self.control_left = self.placements[placement]['control_left'] self.control_height = self.placements[ placement]['control_height'] self.control_width = self.placements[ placement]['control_width'] # If the plugin is not displayed in fullscreen, log information about its area of interest (AOI) if placement != 'fullscreen': thisPlacement = self.placements[placement] AOIx = [int(thisPlacement['control_left']), int( thisPlacement['control_left'] + thisPlacement['control_width'])] AOIy = [int(thisPlacement['control_top']), int( thisPlacement['control_top'] + thisPlacement['control_height'])] self.mainLog.addLine( ['MAIN', 'INFO', plugin_name.upper(), 'AOI_X', AOIx]) self.mainLog.addLine( ['MAIN', 'INFO', plugin_name.upper(), 'AOI_Y', AOIy]) # For each non-fullscreen plugin, show its label if needed if self.parameters['showlabels']: self.PLUGINS_TASK[plugin_name]['ui_label'] = QtWidgets.QLabel(self) self.PLUGINS_TASK[plugin_name]['ui_label'].setStyleSheet("font: " + str(font_size_pt) + "pt \"MS Shell Dlg 2\"; background-color: black; color: white;") self.PLUGINS_TASK[plugin_name]['ui_label'].setAlignment(QtCore.Qt.AlignCenter) self.PLUGINS_TASK[plugin_name]['ui_label'].resize(self.control_width, LABEL_HEIGHT) self.PLUGINS_TASK[plugin_name]['ui_label'].move(self.control_left, self.control_top - LABEL_HEIGHT) else: self.showCriticalMessage( _("Placement '%s' is not recognized!") % placement) # Resize, place and show the plugin itself plugin.resize(self.control_width, self.control_height) plugin.move(self.control_left, self.control_top) plugin.show()
def onUpdate(self): if self.parameters['displayautomationstate']: self.refreshModeLabel() else: self.modeLabel.hide() if self.parameters['resetperformance'] is not None: if self.parameters['resetperformance'] in ['last', 'global']: for i in self.performance[self.parameters['resetperformance']]: self.performance[ self.parameters['resetperformance']][i] = 0 else: self.parent().showCriticalMessage( _("%s : wrong argument in track;resetperformance") % self.parameters['resetperformance']) self.parameters['resetperformance'] = None # Preallocate x and y input variables x_input, y_input = 0, 0 # Compute next cursor coordinates (x,y) current_X, current_Y = self.widget.moveCursor() # If automatic solver : always correct cursor position if self.parameters['automaticsolver']: x_input, y_input = self.widget.getAutoCompensation() # Else record manual compensatory movements else: # Retrieve potentials joystick inputs (x,y) x_input, y_input = self.joystick_input() # If assisted solver : correct cursor position only if joystick # inputs something if self.parameters['assistedsolver']: if any([this_input != 0 for this_input in [x_input, y_input]]): x_input, y_input = self.widget.getAutoCompensation() # Modulate cursor position with potentials joystick inputs current_X += x_input current_Y += y_input # Refresh the display self.widget.refreshCursorPosition(current_X, current_Y) # Constantly log the cursor coordinates self.buildLog(["STATE", "CURSOR", "X", str(current_X)]) self.buildLog(["STATE", "CURSOR", "Y", str(current_Y)]) # Record performance for perf_cat, perf_val in self.performance.items(): if self.widget.isCursorInTarget(): perf_val['time_in_ms'] += self.parameters['taskupdatetime'] else: perf_val['time_out_ms'] += self.parameters['taskupdatetime'] current_deviation = self.widget.returnAbsoluteDeviation() perf_val['points_number'] += 1 perf_val['deviation_mean'] = perf_val['deviation_mean'] * ( (perf_val['points_number'] - 1) / float(perf_val['points_number'])) + current_deviation * ( float(1) / perf_val['points_number'])
else: print("{}:{}".format(_(title),_(msg))) sys.exit() # Ensure that Pyside2, pygame, rstr, psutil and wave are available try: from PySide2 import QtCore, QtWidgets, QtGui #we need to hide pygame message in order to precisely control the output log os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" import pygame import rstr import psutil import wave except Exception as e: OSCriticalErrorMessage(_("Error"), _("Please check that all required libraries are installed:\n\n"+str(e))) class Main(QtWidgets.QMainWindow): def __init__(self, scenario_fullpath): super(Main, self).__init__(parent=None) self.registeredTaskTimer = [] self.parameters = { 'showlabels': True, 'allowescape': True, 'messagetolog': '' } # Preallocate a dictionary to store plugins information
def onStart(self): # Prepare a dict for each task that records performance for this_plugin in self.parent().PLUGINS_TASK: if hasattr(self.parent().PLUGINS_TASK[this_plugin]['class'], 'performance'): self.performances[this_plugin] = dict() # Retrieve performance values since last feedback, store it in a dict for this_plugin in self.performances: self.performances[this_plugin]['infos'] = self.parent( ).PLUGINS_TASK[this_plugin]['class'].performance # Prepare text object what_location = self.parent( ).PLUGINS_TASK[this_plugin]['class'].parameters['taskplacement'] placement = self.parent().placements[what_location] self.performances[this_plugin]['uis'] = dict() self.performances[this_plugin]['uis'][ 'perf_label'] = QtWidgets.QLabel(self) self.performances[this_plugin]['uis']['perf_label'].setGeometry( QtCore.QRect(placement['control_left'], placement['control_top'], placement['control_width'], placement['control_height'])) self.performances[this_plugin]['uis']['perf_label'].setAlignment( QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter) self.performances[this_plugin]['uis']['perf_label'].setFont( self.mainFont) text = str() if this_plugin == 'resman': for this_tank in ['a', 'b']: perf = round( self.performances[this_plugin]['infos']['last'][ this_tank + '_in'] / (self.performances[this_plugin]['infos']['last'][ this_tank + '_in'] + self.performances[this_plugin] ['infos']['last'][this_tank + '_out']) * 100, 1) if (self.performances[this_plugin]['infos'] ['last'][this_tank + '_in'] + self.performances[this_plugin]['infos'] ['last'][this_tank + '_out']) != 0 else 100 this_text = _( "%s tank: on average, you have maintained this tank at a correct level %s%% of time" ) % (this_tank.upper(), perf) text += this_text + '\r\n' self.performances[this_plugin]['uis'][ 'perf_label'].setWordWrap(1) self.performances[this_plugin]['uis']['perf_label'].setText( text) elif this_plugin == 'sysmon': hit = self.performances[this_plugin]['infos']['last'][ 'hit_number'] miss = self.performances[this_plugin]['infos']['last'][ 'miss_number'] rate = self.performances[this_plugin]['infos']['last'][ 'hit_number'] / ( self.performances[this_plugin]['infos']['last'] ['hit_number'] + self.performances[this_plugin] ['infos']['last']['miss_number']) if ( self.performances[this_plugin]['infos']['last'] ['hit_number'] + self.performances[this_plugin] ['infos']['last']['miss_number']) != 0 else 1 rate = round(rate * 100, 1) text = _( "On average, you corrected %s out of %s of failures (%s%%)" ) % (hit, hit + miss, rate) self.performances[this_plugin]['uis'][ 'perf_label'].setWordWrap(1) self.performances[this_plugin]['uis']['perf_label'].setText( text) elif this_plugin == 'track': time_in_rate = float( self.performances[this_plugin]['infos']['total'] ['time_in_ms']) / ( self.performances[this_plugin]['infos']['total'] ['time_in_ms'] + self.performances[this_plugin] ['infos']['total']['time_out_ms']) if ( self.performances[this_plugin]['infos']['total'] ['time_in_ms'] + self.performances[this_plugin] ['infos']['total']['time_out_ms']) != 0 else 1 perc = round(time_in_rate * 100, 1) text = _("On average, you spent %s%% of time inside the target" ) % perc self.performances[this_plugin]['uis'][ 'perf_label'].setWordWrap(1) self.performances[this_plugin]['uis']['perf_label'].setText( text) # Pause all, hide all the running task, but not their ui_label self.parent().onPause(hide_ui=False) for this_plugin in self.performances: self.performances[this_plugin]['uis']['perf_label'].show() self.show() self.feedback_timer.start(self.parameters['feedbackduration'] * 1000)
def getSchedule(self): """Read self.parent().scenariocontents. Schedules show manual mode phases""" schedules = dict(track=[], communications=[]) for this_task in schedules.keys(): this_dict = { key: value for key, value in self.parent().scenariocontents.items() if this_task in value } if any([['start'] in value[this_task] for key, value in this_dict.items()]): starttime = [ key for key, value in this_dict.items() if ['start'] in value[this_task] ][0] this_start = [ key for key, value in this_dict.items() if ['start'] in value[this_task] ][0] try: endtime = [ key for key, value in this_dict.items() if 'stop' in value[this_task][0] ][0] except: self.parent().showCriticalMessage( _("Can't compute schedule. No 'stop' signal found in scenario for the %s task" ) % (this_task)) # Browse scenario content in sorted order # While automaticsolver is OFF : manual/assisted mode is ON for this_time in sorted(this_dict): if ['automaticsolver', 'True' ] in this_dict[this_time][this_task] or [ 'automaticsolver', 'False' ] in this_dict[this_time][this_task]: if ['automaticsolver', 'True'] in this_dict[this_time][this_task]: this_end = this_time elif ['automaticsolver', 'False'] in this_dict[this_time][this_task]: this_start = this_time if 'this_start' in locals() and 'this_end' in locals( ) and this_start < this_end: schedules[this_task].append([ self.dateStringToSecondInteger(this_start), self.dateStringToSecondInteger(this_end) ]) if this_start < endtime and this_start != starttime: schedules[this_task].append([ self.dateStringToSecondInteger(this_start), self.dateStringToSecondInteger(endtime) ]) if len(schedules[this_task]) == 0: schedules[this_task].append([ self.dateStringToSecondInteger(starttime), self.dateStringToSecondInteger(endtime) ]) return schedules['track'], schedules['communications']
def LoadScales(self, scalefile): # Create dictionary to store scales information self.scales = {} # Load scales from file if not scalefile: self.parent().showCriticalMessage("No file to load!") return filename = os.path.join(self.parent().scales_directory, scalefile) if not os.path.exists(filename): self.parent().showCriticalMessage( _("Unable to find the scale file: '%s'") % filename) return self.scalesfile = open(filename, 'r') scale_number = -1 for this_line in self.scalesfile: match = re.match(self.regex_scale_pattern, this_line) if match: scale_number += 1 linecontent = this_line.split(';') self.scales[scale_number] = {} self.scales[scale_number]['label'] = linecontent[0] self.scales[scale_number]['title'] = linecontent[1] self.scales[scale_number]['minimumLabel'] = linecontent[ 2].split('/')[0] self.scales[scale_number]['maximumLabel'] = linecontent[ 2].split('/')[1] self.scales[scale_number]['minimumValue'] = int( linecontent[3].split('/')[0]) self.scales[scale_number]['maximumValue'] = int( linecontent[3].split('/')[1]) self.scales[scale_number]['defaultValue'] = int( linecontent[3].split('/')[2].replace('\n', '')) if not self.scales[scale_number]['minimumValue'] < self.scales[ scale_number]['defaultValue'] < self.scales[ scale_number]['maximumValue']: self.parent().showCriticalMessage( _("Error in scales. Default value must be comprised between minimum and maximum. See in file: " ) + self.parameters['filename']) if len(self.scales) == 0: self.parent().showCriticalMessage( _("Error in scales. No correctly formatted line found in the following file:" ) + self.parameters['filename']) self.scalesfile.close() scale_width = self.screen_width / 3 scale_height = (self.screen_height - (len(self.scales) * (50 + 30 + 40))) / 2 scale_height = min(40, scale_height) # Minimal height is 40 current_y = 0 self.layout = QtWidgets.QVBoxLayout(self) for scale_number in self.scales.keys(): self.scales[scale_number]['uis'] = dict() unicode_string = self.scales[scale_number]['title'] self.scales[scale_number]['uis'][ 'questionLabel'] = QtWidgets.QLabel(unicode_string) self.scales[scale_number]['uis']['questionLabel'].setFont( self.mainFont) self.scales[scale_number]['uis']['questionLabel'].setWordWrap(True) self.scales[scale_number]['uis']['questionLabel'].setAlignment( QtCore.Qt.AlignCenter) unicode_string = self.scales[scale_number]['minimumLabel'] self.scales[scale_number]['uis'][ 'minimumLabel'] = QtWidgets.QLabel(unicode_string) self.scales[scale_number]['uis']['minimumLabel'].setAlignment( QtCore.Qt.AlignRight) self.scales[scale_number]['uis']['minimumLabel'].setFont( self.scaleFont) self.scales[scale_number]['uis']['slider'] = QtWidgets.QSlider( QtCore.Qt.Horizontal) self.scales[scale_number]['uis']['slider'].setMinimum( self.scales[scale_number]['minimumValue']) self.scales[scale_number]['uis']['slider'].setMaximum( self.scales[scale_number]['maximumValue']) self.scales[scale_number]['uis']['slider'].setTickInterval(1) self.scales[scale_number]['uis']['slider'].setTickPosition( QtWidgets.QSlider.TicksBothSides) self.scales[scale_number]['uis']['slider'].setValue( self.scales[scale_number]['defaultValue']) self.scales[scale_number]['uis']['slider'].setSingleStep(1) self.scales[scale_number]['uis']['slider'].setMaximumWidth( 0.5 * self.screen_width) unicode_string = self.scales[scale_number]['maximumLabel'] self.scales[scale_number]['uis'][ 'maximumLabel'] = QtWidgets.QLabel(unicode_string) self.scales[scale_number]['uis']['maximumLabel'].setAlignment( QtCore.Qt.AlignLeft) self.scales[scale_number]['uis']['maximumLabel'].setFont( self.scaleFont) hbox = QtWidgets.QHBoxLayout() hbox.addWidget(self.scales[scale_number]['uis']['questionLabel']) self.layout.addLayout(hbox) hbox = QtWidgets.QHBoxLayout() for this_element in ['minimumLabel', 'slider', 'maximumLabel']: vbox = QtWidgets.QVBoxLayout() vbox.addWidget(self.scales[scale_number]['uis'][this_element]) vbox.setAlignment(QtCore.Qt.AlignVCenter) vbox.setContentsMargins(10, 0, 10, 0) hbox.addLayout(vbox) self.layout.addLayout(hbox) self.layout.addStretch(1) self.questionnairebtn = QtWidgets.QPushButton(_('Validate')) self.questionnairebtn.setMaximumWidth(0.25 * self.screen_width) self.questionnairebtn.clicked.connect(self.onClick) hbox = QtWidgets.QHBoxLayout() hbox.addWidget(self.questionnairebtn) self.layout.addLayout(hbox)
def onUpdate(self): if self.parameters['displayautomationstate']: self.refreshModeLabel() else: self.modeLabel.hide() if self.parameters['resetperformance'] is not None: if self.parameters['resetperformance'] in ['last', 'global']: for i in self.performance[self.parameters['resetperformance']]: self.performance[ self.parameters['resetperformance']][i] = 0 elif self.parameters['resetperformance'] is not None: self.parent().showCriticalMessage( _("%s : wrong argument in sysmon;resetperformance") % self.parameters['resetperformance']) self.parameters['resetperformance'] = None # For each light button, refresh name for thisLight, lightValues in self.parameters['lights'].items(): lightValues['ui'].light.setText(lightValues['name']) # For each scale gauge, refresh name for thisScale, scaleValues in self.parameters['scales'].items(): scaleValues['ui'].label.setText(scaleValues['name']) # 1. Check failures only if no failure is already occuring # (currently prevents double-failure !) if len(self.currentFailure) == 0: for gauge_type in ['lights', 'scales']: for gauge, gaugeValue in self.parameters[gauge_type].items(): # If a failure is to be initiated if (gauge_type == 'scales' and gaugeValue['failure'] in ['up', 'down']) or (gauge_type == 'lights' and gaugeValue['failure']): # Start it... self.startFailure(gauge_type, gauge) # ...and leave the loop break # 2. Vary position of each scale, depending on its state (up, down, no) for thisScale, scaleValues in self.parameters['scales'].items(): scaleValues['position'] = self.computeNextPosition(thisScale) # 3. Refresh visual display if 'ui' in scaleValues: scaleValues['ui'].style = self.parameters['scalestyle'] scaleValues['ui'].position = scaleValues['position'] for thisLight, lightValues in self.parameters['lights'].items(): if 'ui' in lightValues: lightValues['ui'].refreshState(lightValues['on']) # 4. Check for arbitrary feedbacks for f, v in self.parameters['feedbacks'].items(): if v['trigger'] != 0: ui_idx = str(v['trigger']) if any([ ui_idx == idx for idx, val in self.parameters['scales'].items() ]): trigger_ui = self.parameters['scales'][ui_idx]['ui'] self.trigger_feedback(trigger_ui, f) v['trigger'] = None
def onUpdate(self): if self.parameters['displayautomationstate']: self.refreshModeLabel() if self.parameters['resetperformance'] is not None: if self.parameters['resetperformance'] in ['last', 'global']: for this_index in self.performance[self.parameters['resetperformance']]: self.performance[self.parameters['resetperformance']][this_index] = 0 else: self.parent().showCriticalMessage(_("%s : wrong argument in resman;resetperformance") % self.parameters['resetperformance']) self.parameters['resetperformance'] = None time_resolution = (self.parameters['taskupdatetime'] / 1000) / 60. # 0. Compute automatic actions if heuristicsolver activated, three heuristics # Browse only woorking pumps (state != -1) if self.parameters['heuristicsolver'] or self.parameters['assistedsolver']: for this_pump in [pump for pump in self.parameters['pump'].keys() if self.parameters['pump'][pump]['state'] != -1]: fromtank = self.parameters['pump'][this_pump]['ui'].fromTank_label totank = self.parameters['pump'][this_pump]['ui'].toTank_label # 0.1. Systematically activate pumps draining non-depletable tanks if not self.parameters['tank'][fromtank]['depletable'] and self.parameters['pump'][this_pump]['state'] == 0 : self.parameters['pump'][this_pump]['state'] = 1 # 0.2. Activate/deactivate pump whose target tank is too low/high # "Too" means level is out of a tolerance zone around the target level (2500 +/- 150) if self.parameters['tank'][totank]['target'] is not None: if self.parameters['tank'][totank]['level'] <= self.parameters['tank'][totank]['target'] - 150: self.parameters['pump'][this_pump]['state'] = 1 elif self.parameters['tank'][totank]['level'] >= self.parameters['tank'][totank]['target'] + 150: self.parameters['pump'][this_pump]['state'] = 0 # 0.3. Equilibrate between the two A/B tanks if sufficient level if self.parameters['tank'][fromtank]['target'] is not None and self.parameters['tank'][totank]['target'] is not None: if self.parameters['tank'][fromtank]['level'] >= self.parameters['tank'][totank]['target'] >= self.parameters['tank'][totank]['level']: self.parameters['pump'][this_pump]['state'] = 1 else: self.parameters['pump'][this_pump]['state'] = 0 # 1. Deplete tanks A and B for thisTank in ['a', 'b']: volume = int(self.parameters['tank'][ thisTank]['lossperminute'] * time_resolution) volume = min(volume, self.parameters['tank'][thisTank][ 'level']) # If level less than volume, deplete only available level self.parameters['tank'][thisTank]['level'] -= volume # 2. For each pump for pumpNumber in self.parameters['pump'].keys(): # 2.a Transfer flow if pump is ON if self.parameters['pump'][pumpNumber]['state'] == 1: fromtank, totank = self.parameters['pump'][pumpNumber]['ui'].fromTank_label, self.parameters['pump'][pumpNumber]['ui'].toTank_label # Compute volume volume = int(self.parameters['pump'][ pumpNumber]['flow']) * time_resolution # Check if this volume is available volume = min( volume, self.parameters['tank'][fromtank]['level']) # Drain it from tank (if its capacity is limited)... if self.parameters['tank'][fromtank]['depletable']: self.parameters['tank'][fromtank]['level'] -= int(volume) # ...to tank (if it's not full) volume = min(volume, self.parameters['tank'][totank][ 'max'] - self.parameters['tank'][totank]['level']) self.parameters['tank'][totank]['level'] += int(volume) # 2.b Modify flows according to pump states elif self.parameters['pump'][pumpNumber]['state'] != 1 or self.parameters['pump'][pumpNumber]['hide']: # (OFF | FAIL => 0) if self.parameters['pump'][pumpNumber]['state'] == -1 and not self.parameters['pump'][pumpNumber]['failLogged']: self.buildLog(["STATE", "PUMP" + pumpNumber, "FAIL"]) self.parameters['pump'][pumpNumber]['failLogged'] = True if self.parameters['pump'][pumpNumber]['state'] == 0 and self.parameters['pump'][pumpNumber]['failLogged']: self.parameters['pump'][pumpNumber]['failLogged'] = False self.buildLog(["STATE", "PUMP" + pumpNumber, "OFF"]) # 3. For each tank for thisTank in self.parameters['tank'].keys(): pumps_to_deactivate = [] # If it is full, select incoming pumps for deactivation if self.parameters['tank'][thisTank]['level'] >= self.parameters['tank'][thisTank]['max']: pumps_to_deactivate = [self.parameters['pump'].keys()[i] for i in range( 0, len(self.parameters['pump'])) if self.parameters['pump'][self.parameters['pump'].keys()[i]]['ui'].toTank_label == thisTank] # Likewise, if it is empty, select outcome pumps for deactivation elif self.parameters['tank'][thisTank]['level'] <= 0: pumps_to_deactivate = [self.parameters['pump'].keys()[i] for i in range( 0, len(self.parameters['pump'])) if self.parameters['pump'][self.parameters['pump'].keys()[i]]['ui'].fromTank_label == thisTank] # Deactivate selected pumps if not on failure for thisPump in pumps_to_deactivate: if not self.parameters['pump'][thisPump]['state'] == -1: # if not Fail self.parameters['pump'][thisPump]['state'] = 0 self.buildLog(["STATE", "PUMP" + thisPump, "OFF"]) # 4. Refresh visual information for thisPump in self.parameters['pump'].keys(): self.parameters['pump'][thisPump]['ui'].changeState(self.parameters['pump'][thisPump]['state'], self.parameters['pump'][thisPump]['hide']) for thisTank in self.parameters['tank'].keys(): self.parameters['tank'][thisTank]['ui'].refreshLevel(self.parameters['tank'][thisTank]['level']) # 5. Log tank level if a target is set for thisTank in self.parameters['tank'].keys(): if self.parameters['tank'][thisTank]['target'] is not None: self.buildLog(["STATE", "TANK" + thisTank.upper(), "LEVEL", str(self.parameters['tank'][thisTank]['level'])]) for this_cat in self.performance: local_dev = abs(self.parameters['tank'][thisTank]['level'] - self.parameters['tank'][thisTank]['target']) if local_dev <= self.parameters['tolerancelevel']: self.performance[this_cat][thisTank.lower()+'_in']+=1 else: self.performance[this_cat][thisTank.lower()+'_out']+=1 self.update()
def __init__(self, parent): super(Task, self).__init__(parent) # SYSMON PARAMETERS ### self.parameters = { 'title': 'System monitoring', 'taskplacement': 'topleft', 'taskupdatetime': 200, 'alerttimeout': 10000, 'automaticsolver': False, 'automaticsolverdelay': 1 * 1000, 'displayautomationstate': False, 'allowanykey': False, 'scalesnumofboxes': 11, # Must be odd (to admit a middle position) 'safezonelength': 3, 'scalestyle': 1, # could be defined at the scale level 'resetperformance': None, 'feedbacks': { 'positive': { 'active': True, 'color': '#ffff00', 'duration': 1.5 * 1000, 'trigger': 0 }, 'negative': { 'active': True, 'color': '#ff0000', 'duration': 1.5 * 1000, 'trigger': 0 } }, 'lights': { '1': { 'name': 'F5', 'failure': False, 'on': True, 'default': 'on', 'oncolor': "#009900", 'keys': [QtCore.Qt.Key_F5] }, '2': { 'name': 'F6', 'failure': False, 'on': False, 'default': 'off', 'oncolor': "#FF0000", 'keys': [QtCore.Qt.Key_F6] } }, 'scales': { '1': { 'name': 'F1', 'failure': 'no', 'keys': [QtCore.Qt.Key_F1] }, '2': { 'name': 'F2', 'failure': 'no', 'keys': [QtCore.Qt.Key_F2] }, '3': { 'name': 'F3', 'failure': 'no', 'keys': [QtCore.Qt.Key_F3] }, '4': { 'name': 'F4', 'failure': 'no', 'keys': [QtCore.Qt.Key_F4] } } } self.performance = { 'total': { 'hit_number': 0, 'miss_number': 0, 'fa_number': 0 }, 'last': { 'hit_number': 0, 'miss_number': 0, 'fa_number': 0 } } # Potentially translate task title self.parameters['title'] = _(self.parameters['title']) # Set the initial position of the cursor (middle) for thisScale, scaleValue in self.parameters['scales'].items(): scaleValue['position'] = self.parameters['scalesnumofboxes'] / 2 # Define two failures zones (up, down) totalRange = tuple(range(self.parameters['scalesnumofboxes'])) self.zones = dict() self.zones['no'] = [int(median(totalRange))] while len(self.zones['no']) < self.parameters['safezonelength']: self.zones['no'].insert(0, min(self.zones['no']) - 1) self.zones['no'].insert(len(self.zones['no']), max(self.zones['no']) + 1) self.zones['up'] = [r for r in totalRange if r < min(self.zones['no'])] self.zones['down'] = [ r for r in totalRange if r > max(self.zones['no']) ] # Set a list of accepted keys depending on lights and scales parameters scales_keys = [ v['keys'][0] for s, v in self.parameters['scales'].items() ] lights_keys = [ v['keys'][0] for l, v in self.parameters['lights'].items() ] self.accepted_keys = scales_keys + lights_keys
def onStart(self): if not iViewXAPI: self.parent().showCriticalMessage( _("Unable to load the eyetracker library!")) return self.parent().onPause() try: self.adress_iviewx = socket.gethostbyname(self.parameters['smiip']) print(self.adress_iviewx) except (socket.error, EOFError): self.parent().showCriticalMessage( _("Unable to find the eyetracker computer in the network!")) self.parent().onResume() return try: adress_local = socket.gethostbyname(socket.gethostname()) except (socket.error, EOFError): self.parent().showCriticalMessage( _("Unable to find the eyetracker computer in the network!")) self.parent().onResume() return res = 0 i = 0 while res != 1 and i < self.parameters['connectmaxtries']: # Just in case the previous one did not stop anything try: iViewXAPI.iV_Disconnect() except: pass res = iViewXAPI.iV_Connect(self.adress_iviewx, self.parameters['smisendport'], adress_local, self.parameters['smireceiveport']) i += 1 if res != 1: self.parent().showCriticalMessage( _("Unable to connect to the eyetracker!")) self.parent().onResume() return res = iViewXAPI.iV_GetSystemInfo(byref(systemData)) self.taskUpdateTime = float(1000 / systemData.samplerate) # Calibration calibrationData = CCalibration(5, 1, 0, 0, 1, 30, 230, 1, 10) res = iViewXAPI.iV_SetupCalibration(byref(calibrationData)) # Calibration loop calibration_accepted = False deviation_threshold = 0.5 # Maximum deviation angle accepted for calibration while not calibration_accepted: res = iViewXAPI.iV_Calibrate() res = iViewXAPI.iV_Validate() accuracyData = CAccuracy(0, 0, 0, 0) res = iViewXAPI.iV_GetAccuracy(byref(accuracyData), 1) meanDeviation = ( accuracyData.deviationXLeft + accuracyData.deviationXRight + accuracyData.deviationYLeft + accuracyData.deviationYRight) / 4 if meanDeviation <= deviation_threshold: calibration_accepted = True print('Mean deviation -> ' + str(meanDeviation)) tempDict = { 'sampleRate (Hz)': str(systemData.samplerate), 'iViewX_version': str(systemData.iV_MajorVersion) + "." + str(systemData.iV_MinorVersion) + "." + str(systemData.iV_Buildnumber), 'API_version': str(systemData.API_MajorVersion) + "." + str(systemData.API_MinorVersion) + "." + str(systemData.API_Buildnumber), 'deviationXLeft': str(accuracyData.deviationXLeft), 'deviationXRight': str(accuracyData.deviationXRight), 'deviationYLeft': str(accuracyData.deviationYLeft), 'deviationYRight': str(accuracyData.deviationYRight), 'header': '\t'.join( [thisInfo for thisInfo in self.parameters['getFromSample']]) } SMILOG_FILE_PATH = self.parent().LOG_FILE_PATH[:-4] + '_ET.log' self.sampleLog = Logger.Logger(self.parent(), SMILOG_FILE_PATH, tempDict) iViewXAPI.iV_SetLogger(1, "iViewX_log.txt") res = iViewXAPI.iV_SaveCalibration("save_calibration") try: res = iViewXAPI.iV_StopRecording() except: pass res = iViewXAPI.iV_StartRecording() if res != 1: self.parent().showCriticalMessage( _("Unable to start recording the eyetracker!")) return self.parent().onResume()