class Librarian(traits.HasTraits): """Librarian provides a way of writing useful information into the log folder for eagle logs. It is designed to make the information inside an eagle log easier to come back to. It mainly writes default strings into the comments file in the log folder""" logType = traits.Enum("important","debug","calibration") typeCommitButton = traits.Button("save") axisList = AxisSelector() purposeBlock = EntryBlock(fieldName="What is the purpose of this log?") explanationBlock = EntryBlock(fieldName = "Explain what the data shows (important parameters that change, does it make sense etc.)?") additionalComments = EntryBlock(fieldName = "Anything Else?") traits_view = traitsui.View( traitsui.VGroup( traitsui.Item("logFolder",show_label=False, style="readonly"), traitsui.HGroup(traitsui.Item("logType",show_label=False),traitsui.Item("typeCommitButton",show_label=False)), traitsui.Item("axisList",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("purposeBlock",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("explanationBlock",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("additionalComments",show_label=False, editor=traitsui.InstanceEditor(),style='custom') ) , resizable=True , kind ="live" ) def __init__(self, **traitsDict): """Librarian object requires the log folder it is referring to. If a .csv file is given as logFolder argument it will use parent folder as the logFolder""" super(Librarian, self).__init__(**traitsDict) if os.path.isfile(self.logFolder): self.logFolder = os.path.split(self.logFolder)[0] else: logger.debug("found these in %s: %s" %(self.logFolder, os.listdir(self.logFolder) )) self.logFile = os.path.join(self.logFolder, os.path.split(self.logFolder)[1]+".csv") self.commentFile = os.path.join(self.logFolder, "comments.txt") self.axisList.commentFile = self.commentFile self.axisList.logFile = self.logFile self.purposeBlock.commentFile = self.commentFile self.explanationBlock.commentFile = self.commentFile self.additionalComments.commentFile = self.commentFile def _typeCommitButton_fired(self): logger.info("saving axes info starting") timeStamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") blockDelimiterStart = "__Log Type__<start>" blockDelimiterEnd = "__Log Type__<end>" fullString = "\n"+blockDelimiterStart+"\n"+timeStamp+"\n"+self.logType+"\n"+blockDelimiterEnd+"\n" with open(self.commentFile, "a+") as writeFile: writeFile.write(fullString) logger.info("saving axes info finished")
def make_plugin_view(model_name, model_nodes, selection_view, model_view): node_label = '=' + model_name container_nodes = [ _traitsui.TreeNode( node_for=[CalcContainer], label=node_label, children='', auto_open=True, menu=[], ), _traitsui.TreeNode( node_for=[CalcContainer], label=node_label, children='calculations', auto_open=True, menu=[], ), ] plugin_tree = _traitsui.TreeEditor( nodes=container_nodes + model_nodes, # refresh='controller.update_tree', selected='controller.selected_object', editable=False, hide_root=True, ) plugin_view = _traitsui.View( _traitsui.Group( # Left side tree _traitsui.Item('controller.model', editor=plugin_tree, show_label=False), # Right side _traitsui.Group( # Upper right side data set selection panel selection_view, # Lower right side calc settings panel _traitsui.Group( _traitsui.Item( 'controller.edit_node', editor=_traitsui.InstanceEditor(view=model_view), style='custom', show_label=False), show_border=True, ), orientation='vertical', ), _traitsui.Spring(width=10), orientation='horizontal', ), resizable=True, buttons=['OK'], ) return plugin_view
def navigation_sliders(data_axes, title=None): """Raises a windows with sliders to control the index of DataAxis Parameters ---------- data_axes : list of DataAxis instances """ class NavigationSliders(t.HasTraits): pass nav = NavigationSliders() view_tuple = () for axis in data_axes: name = str(axis).replace(" ", "_") nav.add_class_trait(name, axis) nav.trait_set([name, axis]) view_tuple += ( tui.Item(name, style = "custom", editor = tui.InstanceEditor( view=tui.View( tui.Item( "index", show_label=False, # The following is commented out # due to a traits ui bug #editor=tui.RangeEditor(mode="slider"), ), ), ), ), ) view = tui.View(tui.VSplit(view_tuple), title="Navigation sliders" if title is None else title) nav.edit_traits(view=view)
class ADC(traits.HasTraits): refreshTime = traits.Float( 0.1, desc="how often to update the frequencies in seconds") logFile = traits.File rpiADCLogFolder = traits.String averageN = traits.Int(100) VMax = traits.Enum(3.3, 5.0) channelList = traits.List( [1, 3, 5, 6], desc="list of channels to show frequencies for and query") channelValues = [(0, 'Ch 0'), (1, 'Ch 1'), (2, 'Ch 2'), (3, 'Ch 3'), (4, 'Ch 4'), (5, 'Ch 5'), (6, 'Ch 6'), (7, 'Ch 7')] #THIS ONLY WORKS IF PyHWI can be found! it is in the python path manager for lab-Monitoring-0 connection = rpiADCClient.Connection() #if there are problems check the server is running on 192.168.0.111 icon_trait = pyface.image_resource.ImageResource('icons/wavemeter.png') #oscilloscope = None currentLocalTime = time.localtime() currentYear = currentLocalTime.tm_year currentMonth = currentLocalTime.tm_mon currentDay = currentLocalTime.tm_mday channel0 = ADCChannel(channelNumber=0, connection=connection, channelName="Na Cavity Lock", channelMessageHigh="Na Cavity Locked", channelMessageLow="Na Cavity Out of Lock", criticalValue=1.5, highIsGood=True, highSoundFile="NaLocked.wav", lowSoundFile="NaOutOfLock.wav") channel1 = ADCChannel(channelNumber=1, connection=connection, channelName="Li MOT Fluorescence", channelMessageHigh="MOT Loading", channelMessageLow="No MOT", criticalValue=0.1) channel2 = ADCChannel( channelNumber=2, connection=connection, channelName="Li MOT Power (stable)") #changed by Martin channel3 = ADCChannel(channelNumber=3, connection=connection, channelName="Li MOT (unstable)") channel4 = ADCChannel(channelNumber=4, connection=connection, channelName="Na MOT Power (stable)") channel5 = ADCChannel(channelNumber=5, connection=connection, channelName="Na MOT Flourescence") channel6 = ADCChannel(channelNumber=6, connection=connection, channelName="ZS light Power") channel7 = ADCChannel(channelNumber=7, connection=connection, channelName="disconnected") channels = { 0: channel0, 1: channel1, 2: channel2, 3: channel3, 4: channel4, 5: channel5, 6: channel6, 7: channel7 } def __init__(self, **traitsDict): """Called when object initialises. Starts timer etc. """ print "Instantiating GUI.." super(ADC, self).__init__(**traitsDict) self.connection.connect() self.oscilloscope = Oscilloscope(connection=self.connection, resolution=self.refreshTime, visibleChannels=self.channelList) self.start_timer() def start_timer(self): """Called in init if user selected live update mode, otherwise called from menu action. Every self.wizard.updateRateSeconds, self._refresh_data_action will be called""" print "Timer Object Started. Will update ADC Information every %s seconds" % self.refreshTime self.timer = Timer( float(self.refreshTime) * 1000, self._refresh_Visible_channels) def _refreshTime_changed(self): self.timer.setInterval(float(self.refreshTime) * 1000) print "will update ADC every %s seconds" % (float(self.refreshTime)) self.oscilloscope.resolution = self.refreshTime #use refresh time to set resolution of oscilloscope def _logFile_changed(self): self._create_log_file() def _channelList_changed(self): """push changes to visible channels in oscilloscope """ self.oscilloscope.visibleChannels = self.channelList def _logFile_default(self): """default log file has date stamp. log file is changed once a day """ print "choosing default log file" return os.path.join( self.rpiADCLogFolder, time.strftime("rpiADC-%Y-%m-%d.csv", self.currentLocalTime)) def getGroupFolder(self): """returns the location of the group folder. supports both linux and windows. assumes it is mounted to /media/ursa/AQOGroupFolder for linux""" if platform.system() == "Windows": groupFolder = os.path.join("\\\\ursa", "AQOGroupFolder") if platform.system() == "Linux": groupFolder = os.path.join("/media", "ursa", "AQOGroupFolder") return groupFolder def _rpiADCLogFolder_default(self): return os.path.join(self.getGroupFolder(), "Experiment Humphry", "Experiment Control And Software", "rpiADC", "data") def _create_log_file(self): if not os.path.exists(os.path.join(self.rpiADCLogFolder, self.logFile)): with open(self.logFile, 'a+') as csvFile: csvWriter = csv.writer(csvFile) csvWriter.writerow([ "epochSeconds", "Channel 0", "Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7" ]) def checkDateForFileName(self): """gets current date and time and checks if we should change file name if we should it creates the new file and the name""" #self.currentLocalTime was already changed in log Temperatures if self.currentLocalTime.tm_mday != self.currentDay: #the day has changed we should start a new log file! self.logFile = self._logFile_default() self._create_log_file() def _log_channels(self): self.currentLocalTime = time.localtime() self.checkDateForFileName() self.currentMonth = self.currentLocalTime.tm_mon self.currentDay = self.currentLocalTime.tm_mday if not os.path.exists(os.path.join(self.rpiADCLogFolder, self.logFile)): self._create_log_file() voltages = [self.channels[i]._voltage_get() for i in range(0, 8)] with open(self.logFile, 'a+') as csvFile: csvWriter = csv.writer(csvFile) csvWriter.writerow([time.time()] + voltages) def _refresh_Visible_channels(self): self.connection.getResults() #updates dictionary in connection object for channelNumber in self.channelList: channel = self.channels[channelNumber] channel._voltage_update() self.oscilloscope.updateArrays() self.oscilloscope.updateArrayPlotData() self._log_channels() settingsGroup = traitsui.VGroup( traitsui.Item("logFile", label="Log File"), traitsui.HGroup( traitsui.Item('refreshTime', label='refreshTime'), traitsui.Item( 'averageN', label='averaging Number', tooltip= "Number of measurements taken and averaged for value shown"), traitsui.Item('VMax', label='Maximum Voltage Setting', tooltip="Whether the box is set to 3.3V or 5V max")), ) selectionGroup = traitsui.Group( traitsui.Item('channelList', editor=traitsui.CheckListEditor(values=channelValues, cols=4), style='custom', label='Show')) groupLeft = traitsui.VGroup( traitsui.Item('channel0', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(0 in channelList)"), traitsui.Item('channel1', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(1 in channelList)"), traitsui.Item('channel2', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(2 in channelList)"), traitsui.Item('channel3', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(3 in channelList)")) groupRight = traitsui.VGroup( traitsui.Item('channel4', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(4 in channelList)"), traitsui.Item('channel5', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(5 in channelList)"), traitsui.Item('channel6', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(6 in channelList)"), traitsui.Item('channel7', editor=traitsui.InstanceEditor(), style='custom', show_label=False, visible_when="(7 in channelList)")) groupOscilloscope = traitsui.Group( traitsui.Item('oscilloscope', editor=traitsui.InstanceEditor(), style='custom', show_label=False)) groupAll = traitsui.VGroup( settingsGroup, selectionGroup, traitsui.VSplit(traitsui.HGroup(groupLeft, groupRight), groupOscilloscope)) traits_view = traitsui.View(groupAll, resizable=True, title="ADC Monitor", handler=ADCHandler(), icon=icon_trait)
class Librarian(traits.HasTraits): """Librarian provides a way of writing useful information into the log folder for eagle logs. It is designed to make the information inside an eagle log easier to come back to. It mainly writes default strings into the comments file in the log folder""" logType = traits.Enum("important", "debug", "calibration") writeToOneNoteButton = traits.Button("save") refreshInformation = traits.Button("refresh") saveImage = traits.Button("save plot") axisList = AxisSelector() purposeBlock = EntryBlock(fieldName="What is the purpose of this log?") resultsBlock = EntryBlock( fieldName= "Explain what the data shows (important parameters that change, does it make sense etc.)?" ) commentsBlock = EntryBlock(fieldName="Anything Else?") saveButton = traits.Button("Save") # notebooks = traits.Enum(values = "notebookNames") # we could let user select from a range of notebooks # notebookNames = traits.List notebookName = traits.String("Humphry's Notebook") sectionName = traits.String("Eagle Logs") logName = traits.String("") xAxis = traits.String("") yAxis = traits.String("") traits_view = traitsui.View(traitsui.VGroup( traitsui.Item("logName", show_label=False, style="readonly"), traitsui.Item("axisList", show_label=False, editor=traitsui.InstanceEditor(), style='custom'), traitsui.Item("purposeBlock", show_label=False, editor=traitsui.InstanceEditor(), style='custom'), traitsui.Item("resultsBlock", show_label=False, editor=traitsui.InstanceEditor(), style='custom'), traitsui.Item("commentsBlock", show_label=False, editor=traitsui.InstanceEditor(), style='custom'), traitsui.HGroup( traitsui.Item("writeToOneNoteButton", show_label=False), traitsui.Item("refreshInformation", show_label=False)), ), resizable=True, kind="live", title="Eagle OneNote") def __init__(self, **traitsDict): """Librarian object requires the log folder it is referring to. If a .csv file is given as logFolder argument it will use parent folder as the logFolder""" super(Librarian, self).__init__(**traitsDict) if os.path.isfile(self.logFolder): self.logFolder = os.path.split(self.logFolder)[0] else: logger.debug("found these in %s: %s" % (self.logFolder, os.listdir(self.logFolder))) self.logName = os.path.split(self.logFolder)[1] self.logFile = os.path.join(self.logFolder, os.path.split(self.logFolder)[1] + ".csv") self.axisList.logFile = self.logFile #needs a copy so it can calculate valid values self.axisList.masterList = self.axisList._masterList_default() self.axisList.masterListWithNone = self.axisList._masterListWithNone_default( ) if self.xAxis != "": self.axisList.xAxis = self.xAxis if self.yAxis != "": self.axisList.yAxis = self.yAxis self.eagleOneNote = oneNotePython.eagleLogsOneNote.EagleLogOneNote( notebookName=self.notebookName, sectionName=self.sectionName) logPage = self.eagleOneNote.setPage(self.logName) # # except Exception as e: # logger.error("failed to created an EagleOneNote Instance. This could happen for many reasons. E.g. OneNote not installed or most likely, the registry is not correct. See known bug and fix in source code of onenotepython module:%s" % e.message) if logPage is not None: #page exists self.purposeBlock.textBlock = self.eagleOneNote.getOutlineText( "purpose") self.resultsBlock.textBlock = self.eagleOneNote.getOutlineText( "results") self.commentsBlock.textBlock = self.eagleOneNote.getOutlineText( "comments") xAxis, yAxis, series = self.eagleOneNote.getParametersOutlineValues( ) try: self.axisList.xAxis, self.axisList.yAxis, self.axisList.series = xAxis, yAxis, series except Exception as e: logger.error( "error when trying to read analysis parameters: %s" % e.message) self.pageExists = True else: self.pageExists = False self.purposeBlock.textBlock = "" self.resultsBlock.textBlock = "" self.commentsBlock.textBlock = "" #could also reset axis list but it isn't really necessary def _writeToOneNoteButton_fired(self): """writes content of librarian to one note page """ if not self.pageExists: self.eagleOneNote.createNewEagleLogPage(self.logName, refresh=True, setCurrent=True) self.pageExists = True self.eagleOneNote.setOutline("purpose", self.purposeBlock.textBlock, rewrite=False) self.eagleOneNote.setOutline("results", self.resultsBlock.textBlock, rewrite=False) self.eagleOneNote.setOutline("comments", self.commentsBlock.textBlock, rewrite=False) self.eagleOneNote.setDataOutline(self.logName, rewrite=False) self.eagleOneNote.setParametersOutline(self.axisList.xAxis, self.axisList.yAxis, self.axisList.series, rewrite=False) self.eagleOneNote.currentPage.rewritePage() #now to get resizing done well we want to completely repull the XML and data #brute force method: self.eagleOneNote = oneNotePython.eagleLogsOneNote.EagleLogOneNote( notebookName=self.notebookName, sectionName=self.sectionName) logPage = self.eagleOneNote.setPage( self.logName) #this sets current page of eagleOneNote self.eagleOneNote.organiseOutlineSizes()
" 2. Remove the column with the missing values and re-import the data\n" " 3. Remove the row with the missing values and re-import the data") dlg.edit_traits(parent=self.win_handle, kind='modal') def _show_alignment_warning(self, ds_c, ds_s): dlg = ErrorMessage() dlg.err_msg = 'Consumer liking and sensory profiling data does not align' dlg.err_val = 'The Consumer liking data and descriptive analysis/sensory profiling data do not align. There are {0} rows in {1} and {2} rows in the {3}. Please select other data.'.format(ds_c.n_objs, ds_c.display_name, ds_s.n_objs, ds_s.display_name) dlg.edit_traits(parent=self.win_handle, kind='modal') selection_view = _traitsui.Group( _traitsui.Item('controller.comb', editor=_traitsui.InstanceEditor(), style='custom', show_label=False, width=250, height=150, ), label='Select data set', show_border=True, ) prefmap_plugin_view = make_plugin_view( 'Prefmap', prefmap_nodes, selection_view, prefmap_view) if __name__ == '__main__':
class PhysicsProperties(traits.HasTraits): selectedElement = traits.Instance(element.Element)#default of Li6 set in init species = traits.Enum(*element.names) massATU = traits.Float(22.9897692807,label="mass (u)", desc="mass in atomic mass units") decayRateMHz = traits.Float(9.7946,label = u"Decay Rate \u0393 (MHz)", desc= "decay rate/ natural line width of 2S1/2 -> 2P3/2") crossSectionSigmaPlus = traits.Float(1.6573163925E-13, label=u"cross section \u03C3 + (m^2)", desc = "resonant cross section 2S1/2 -> 2P3/2. Warning not accurate for 6Li yet") scatteringLength = traits.Float(62.0, label="scattering length (a0)") IsatSigmaPlus = traits.Float(6.260021, width=10, label=u"Isat (mW/cm^2)",desc = "I sat sigma + 2S1/2 -> 2P3/2") TOFFromVariableBool = traits.Bool(True, label = "Use TOF variable?", desc = "Attempt to read TOF variable from latestSequence.xml. If found update the TOF variable automatically") TOFVariableName = traits.String("ImagingTimeTOFLi",label="variable name:", desc = "The name of the TOF variable in Experiment Control") timeOfFlightTimems = traits.Float(4.0, label = "TOF Time (ms)", desc = "Time of Flight time in ms. Should match experiment control") trapGradientXFromVariableBool = traits.Bool(True, label = "Use MTGradientX variable?", desc = "Attempt to read MTGradientX variable from latestSequence.xml. If found update the TOF variable automatically") trapGradientXVariableName = traits.String("MagneticTrapEvaporation2GradientX",label="variable name:", desc = "The name of the trapGradientX variable in Experiment Control") trapGradientX = traits.Float(20.0, label="Trap Gradient (small) (G/cm)", desc = "gradient of trap before time of flight. Smaller of the anti helmholtz gradients" ) trapFrequencyXHz = traits.Float(100.0, label="Trap frequency X (Hz)", desc = "trap frequency in X direction in Hz") trapFrequencyYHz = traits.Float(100.0, label="Trap frequency Y (Hz)", desc = "trap frequency in Y direction in Hz") trapFrequencyZHz = traits.Float(100.0, label="Trap frequency Z (Hz)", desc = "trap frequency in Z direction in Hz") imagingDetuningLinewidths = traits.Float(0.0, label= u"imaging detuning (\u0393)", desc = "imaging light detuning from resonance in units of linewidths") inTrapSizeX = traits.Float(0.0, label="In trap Size X (pixels)", desc = "size of cloud in trap in x direciton in pixels. Use very short TOF to estimate" ) inTrapSizeY = traits.Float(0.0, label="In trap Size Y (pixels)", desc = "size of cloud in trap in y direciton in pixels. Use very short TOF to estimate" ) autoInTrapSizeBool = traits.Bool(False, label="Change TOFTime InTrap Calibration?", desc= "Allows user to change the TOF time for which the fit will automatically update the in trap size if the autoSetSize box is checked for Gaussian fit") inTrapSizeTOFTimems = traits.Float(0.2, label="In Trap Size TOFTime", desc= "If the TOF time is this value and the autoSetSize box is checked, then we will automatically update the size whenever the TOFTime equals this value" ) pixelSize = traits.Float(9.0, label=u"Pixel Size (\u03BCm)", desc = "Actual pixel size of the camera (excluding magnification)") magnification = traits.Float(0.5, label="Magnification", desc = "Magnification of the imaging system") binningSize = traits.Int(1, label=u"Binning Size (px)", desc = "Binning size; influences effective pixel size") latestSequenceFile = os.path.join("\\\\ursa","AQOGroupFolder","Experiment Humphry","Experiment Control And Software","currentSequence", "latestSequence.xml") secondLatestSequenceFile = os.path.join("\\\\ursa","AQOGroupFolder","Experiment Humphry","Experiment Control And Software","currentSequence", "secondLatestSequence.xml") traits_view = traitsui.View( traitsui.HGroup( traitsui.VGroup( traitsui.Item("species"), traitsui.Item("selectedElement",editor = traitsui.InstanceEditor(), style="custom", show_label=False), show_border=True, label = "Element Properties" ), traitsui.VGroup( traitsui.HGroup(traitsui.Item("TOFFromVariableBool"),traitsui.Item("TOFVariableName", visible_when="TOFFromVariableBool"),traitsui.Item("timeOfFlightTimems",style="readonly",visible_when="(TOFFromVariableBool)"),traitsui.Item("timeOfFlightTimems",visible_when="(not TOFFromVariableBool)")), traitsui.HGroup(traitsui.Item("trapGradientXFromVariableBool"),traitsui.Item("trapGradientXVariableName", visible_when="trapGradientXFromVariableBool"),traitsui.Item("trapGradientX",style="readonly",visible_when="(trapGradientXFromVariableBool)"),traitsui.Item("trapGradientX",visible_when="(not trapGradientXFromVariableBool)")), traitsui.Item("trapFrequencyXHz"), traitsui.Item("trapFrequencyYHz"), traitsui.Item("trapFrequencyZHz"), traitsui.Item("inTrapSizeX"), traitsui.Item("inTrapSizeY"), traitsui.HGroup(traitsui.Item("autoInTrapSizeBool"),traitsui.Item("inTrapSizeTOFTimems", visible_when="(autoInTrapSizeBool)")), label="Experiment Parameters", show_border=True ), traitsui.VGroup( traitsui.Item("imagingDetuningLinewidths"), traitsui.Item("pixelSize"), traitsui.Item("binningSize"), traitsui.Item("magnification"), label="Camera and Imaging", show_border=True ) ) ) def __init__(self, **traitsDict): super(PhysicsProperties, self).__init__(**traitsDict) # self.selectedElement = element.Li6#set default element self.species = element.Li6.nameID #pull some uselful variables from the physics constants dictionary for reference self.constants = scipy.constants.physical_constants self.u = self.constants["atomic mass constant"][0] self.bohrMagneton = self.constants["Bohr magneton"][0] self.bohrRadius = self.constants["Bohr radius"][0] self.kb = self.constants["Boltzmann constant"][0] self.joulesToKelvin = self.constants["joule-kelvin relationship"][0] self.hbar = self.constants["Planck constant over 2 pi"][0] self.h = self.constants["Planck constant"][0] self.joulesToHertz = self.constants["joule-hertz relationship"][0] self.hertzToKelvin =self.constants["hertz-kelvin relationship"][0] self.a0 = self.constants["Bohr radius"][0] def _species_changed(self): """update constants according to the changed species """ logger.debug("species changed to %s" % self.species) self.selectedElement = element.elements[self.species] def updatePhysics(self): try: logger.debug("attempting to update physics from xml") if os.path.exists(self.latestSequenceFile): modifiedTime = os.path.getmtime(self.latestSequenceFile) imageTime = self.selectedFileModifiedTime now = time.time() timeDiff = imageTime-modifiedTime timeDiff += 31 # ToDo: debug this strange time offset!! if timeDiff>300.0: #>5min logger.warning("Found a time difference of >5min between modification time of XML and of image. Are you sure the latest XML file is being updated? Check snake is running?") if timeDiff<0: logger.error("Found very fresh sequence file. Probably read already variables of next sequence?") logger.warning("Use second last sequence file instead..") self.tree = ET.parse(self.secondLatestSequenceFile) else: self.tree = ET.parse(self.latestSequenceFile) logger.warning("Age of sequence file: {}".format(timeDiff)) # for debugging, remove or reduce log level later ;P # logger.warning("Age of image file: {}".format(imageTime)) # logger.warning("Now = {}".format(now)) # for debugging, remove or reduce log level later ;P # logger.warning("ModifiedTime of xml = {}".format(modifiedTime)) # for debugging, remove or reduce log level later ;P self.root = self.tree.getroot() variables = self.root.find("variables") self.variables = {child[0].text:float(child[1].text) for child in variables} logger.debug("Read a TOF time of %s from variables in XML " % self.variables[self.TOFVariableName]) else: logger.error("Could not find latest xml File. cannot update physics.") return except Exception as e: logger.error("Error when trying to load XML %s" % e.message) return #update TOF Time if self.TOFFromVariableBool: logger.debug("attempting to update TOF time from xml") try: self.timeOfFlightTimems = self.variables[self.TOFVariableName]*1.0E3 except KeyError as e: logger.error("incorrect variable name. No variable %s found. Using default 1ms" % self.TOFVariableName ) self.timeOfFlightTimems = 1.0 if self.trapGradientXFromVariableBool: logger.debug("attempting to update trapGradientX from xml") try: self.trapGradientX = self.variables[self.trapGradientXVariableName] except KeyError: logger.error("incorrect variable name. No variable %s found. Using default 50G/cm" % self.trapGradientXVariableName ) self.trapGradientX = 50.0
class ImagePlotInspector(traits.HasTraits): #Traits view definitions: settingsGroup = traitsui.VGroup( traitsui.VGroup( traitsui.Item("watchFolderBool", label="Watch Folder?"), traitsui.HGroup(traitsui.Item("selectedFile", label="Select a File"), visible_when="not watchFolderBool"), traitsui.HGroup(traitsui.Item("watchFolder", label="Select a Directory"), visible_when="watchFolderBool"), traitsui.HGroup(traitsui.Item("searchString", label="Filename sub-string"), visible_when="watchFolderBool"), label="File Settings", show_border=True), traitsui.VGroup(traitsui.HGroup('autoRangeColor', 'colorMapRangeLow', 'colorMapRangeHigh'), traitsui.HGroup('horizontalAutoRange', 'horizontalLowerLimit', 'horizontalUpperLimit'), traitsui.HGroup('verticalAutoRange', 'verticalLowerLimit', 'verticalUpperLimit'), label="axis limits", show_border=True), traitsui.VGroup(traitsui.HGroup('object.model.scale', 'object.model.offset'), traitsui.HGroup( traitsui.Item('object.model.pixelsX', label="Pixels X"), traitsui.Item('object.model.pixelsY', label="Pixels Y")), traitsui.HGroup( traitsui.Item('object.model.ODCorrectionBool', label="Correct OD?"), traitsui.Item('object.model.ODSaturationValue', label="OD saturation value")), traitsui.HGroup( traitsui.Item('contourLevels', label="Contour Levels"), traitsui.Item('colormap', label="Colour Map")), traitsui.HGroup( traitsui.Item('fixAspectRatioBool', label="Fix Plot Aspect Ratio?")), traitsui.HGroup( traitsui.Item('updatePhysicsBool', label="Update Physics with XML?")), traitsui.HGroup( traitsui.Item("cameraModel", label="Update Camera Settings to:")), label="advanced", show_border=True), label="settings") plotGroup = traitsui.Group( traitsui.Item('container', editor=ComponentEditor(size=(800, 600)), show_label=False)) fitsGroup = traitsui.Group(traitsui.Item('fitList', style="custom", editor=traitsui.ListEditor( use_notebook=True, selected="selectedFit", deletable=False, export='DockWindowShell', page_name=".name"), label="Fits", show_label=False), springy=True) mainPlotGroup = traitsui.HSplit(plotGroup, fitsGroup, label="Image") fftGroup = traitsui.Group(label="Fourier Transform") physicsGroup = traitsui.Group(traitsui.Item( "physics", editor=traitsui.InstanceEditor(), style="custom", show_label=False), label="Physics") logFilePlotGroup = traitsui.Group(traitsui.Item( "logFilePlotObject", editor=traitsui.InstanceEditor(), style="custom", show_label=False), label="Log File Plotter") eagleMenubar = traitsmenu.MenuBar( traitsmenu.Menu( traitsui.Action(name='Save Image Copy As...', action='_save_image_as'), traitsui.Action(name='Save Image Copy', action='_save_image_default'), name="File", )) traits_view = traitsui.View(settingsGroup, mainPlotGroup, physicsGroup, logFilePlotGroup, buttons=traitsmenu.NoButtons, menubar=eagleMenubar, handler=EagleHandler, title="Experiment Eagle", statusbar="selectedFile", icon=pyface.image_resource.ImageResource( os.path.join('icons', 'eagles.ico')), resizable=True) model = CameraImage() physics = physicsProperties.physicsProperties.PhysicsProperties( ) #create a physics properties object logFilePlotObject = logFilePlot.LogFilePlot() fitList = model.fitList selectedFit = traits.Instance(fits.Fit) drawFitRequest = traits.Event drawFitBool = traits.Bool(False) # true when drawing a fit as well selectedFile = traits.File() watchFolderBool = traits.Bool(False) watchFolder = traits.Directory() searchString = traits.String( desc= "sub string that must be contained in file name for it to be shown in Eagle. Can be used to allow different instances of Eagle to detect different saved images." ) oldFiles = set() contourLevels = traits.Int(15) colormap = traits.Enum(colormaps.color_map_name_dict.keys()) autoRangeColor = traits.Bool(True) colorMapRangeLow = traits.Float colorMapRangeHigh = traits.Float horizontalAutoRange = traits.Bool(True) horizontalLowerLimit = traits.Float horizontalUpperLimit = traits.Float verticalAutoRange = traits.Bool(True) verticalLowerLimit = traits.Float verticalUpperLimit = traits.Float fixAspectRatioBool = traits.Bool(False) updatePhysicsBool = traits.Bool(True) cameraModel = traits.Enum("Custom", "ALTA0", "ANDOR0", "ALTA1", "ANDOR1") #--------------------------------------------------------------------------- # Private Traits #--------------------------------------------------------------------------- _image_index = traits.Instance(chaco.GridDataSource) _image_value = traits.Instance(chaco.ImageData) _cmap = traits.Trait(colormaps.jet, traits.Callable) #--------------------------------------------------------------------------- # Public View interface #--------------------------------------------------------------------------- def __init__(self, *args, **kwargs): super(ImagePlotInspector, self).__init__(*args, **kwargs) #self.update(self.model) self.create_plot() for fit in self.fitList: fit.imageInspectorReference = self fit.physics = self.physics self.selectedFit = self.fitList[0] self.logFilePlotObject.physicsReference = self.physics #self._selectedFile_changed() logger.info("initialisation of experiment Eagle complete") def create_plot(self): # Create the mapper, etc self._image_index = chaco.GridDataSource(scipy.array([]), scipy.array([]), sort_order=("ascending", "ascending")) image_index_range = chaco.DataRange2D(self._image_index) self._image_index.on_trait_change(self._metadata_changed, "metadata_changed") self._image_value = chaco.ImageData(data=scipy.array([]), value_depth=1) image_value_range = chaco.DataRange1D(self._image_value) # Create the contour plots #self.polyplot = ContourPolyPlot(index=self._image_index, self.polyplot = chaco.CMapImagePlot( index=self._image_index, value=self._image_value, index_mapper=chaco.GridMapper(range=image_index_range), color_mapper=self._cmap(image_value_range)) # Add a left axis to the plot left = chaco.PlotAxis(orientation='left', title="y", mapper=self.polyplot.index_mapper._ymapper, component=self.polyplot) self.polyplot.overlays.append(left) # Add a bottom axis to the plot bottom = chaco.PlotAxis(orientation='bottom', title="x", mapper=self.polyplot.index_mapper._xmapper, component=self.polyplot) self.polyplot.overlays.append(bottom) # Add some tools to the plot self.polyplot.tools.append( tools.PanTool(self.polyplot, constrain_key="shift", drag_button="middle")) self.polyplot.overlays.append( tools.ZoomTool(component=self.polyplot, tool_mode="box", always_on=False)) self.lineInspectorX = clickableLineInspector.ClickableLineInspector( component=self.polyplot, axis='index_x', inspect_mode="indexed", write_metadata=True, is_listener=False, color="white") self.lineInspectorY = clickableLineInspector.ClickableLineInspector( component=self.polyplot, axis='index_y', inspect_mode="indexed", write_metadata=True, color="white", is_listener=False) self.polyplot.overlays.append(self.lineInspectorX) self.polyplot.overlays.append(self.lineInspectorY) self.boxSelection2D = boxSelection2D.BoxSelection2D( component=self.polyplot) self.polyplot.overlays.append(self.boxSelection2D) # Add these two plots to one container self.centralContainer = chaco.OverlayPlotContainer(padding=0, use_backbuffer=True, unified_draw=True) self.centralContainer.add(self.polyplot) # Create a colorbar cbar_index_mapper = chaco.LinearMapper(range=image_value_range) self.colorbar = chaco.ColorBar( index_mapper=cbar_index_mapper, plot=self.polyplot, padding_top=self.polyplot.padding_top, padding_bottom=self.polyplot.padding_bottom, padding_right=40, resizable='v', width=30) self.plotData = chaco.ArrayPlotData( line_indexHorizontal=scipy.array([]), line_valueHorizontal=scipy.array([]), scatter_indexHorizontal=scipy.array([]), scatter_valueHorizontal=scipy.array([]), scatter_colorHorizontal=scipy.array([]), fitLine_indexHorizontal=scipy.array([]), fitLine_valueHorizontal=scipy.array([])) self.crossPlotHorizontal = chaco.Plot(self.plotData, resizable="h") self.crossPlotHorizontal.height = 100 self.crossPlotHorizontal.padding = 20 self.crossPlotHorizontal.plot( ("line_indexHorizontal", "line_valueHorizontal"), line_style="dot") self.crossPlotHorizontal.plot( ("scatter_indexHorizontal", "scatter_valueHorizontal", "scatter_colorHorizontal"), type="cmap_scatter", name="dot", color_mapper=self._cmap(image_value_range), marker="circle", marker_size=4) self.crossPlotHorizontal.index_range = self.polyplot.index_range.x_range self.plotData.set_data("line_indexVertical", scipy.array([])) self.plotData.set_data("line_valueVertical", scipy.array([])) self.plotData.set_data("scatter_indexVertical", scipy.array([])) self.plotData.set_data("scatter_valueVertical", scipy.array([])) self.plotData.set_data("scatter_colorVertical", scipy.array([])) self.plotData.set_data("fitLine_indexVertical", scipy.array([])) self.plotData.set_data("fitLine_valueVertical", scipy.array([])) self.crossPlotVertical = chaco.Plot(self.plotData, width=140, orientation="v", resizable="v", padding=20, padding_bottom=160) self.crossPlotVertical.plot( ("line_indexVertical", "line_valueVertical"), line_style="dot") self.crossPlotVertical.plot( ("scatter_indexVertical", "scatter_valueVertical", "scatter_colorVertical"), type="cmap_scatter", name="dot", color_mapper=self._cmap(image_value_range), marker="circle", marker_size=4) self.crossPlotVertical.index_range = self.polyplot.index_range.y_range # Create a container and add components self.container = chaco.HPlotContainer(padding=40, fill_padding=True, bgcolor="white", use_backbuffer=False) inner_cont = chaco.VPlotContainer(padding=40, use_backbuffer=True) inner_cont.add(self.crossPlotHorizontal) inner_cont.add(self.centralContainer) self.container.add(self.colorbar) self.container.add(inner_cont) self.container.add(self.crossPlotVertical) def initialiseFitPlot(self): """called if this is the first Fit Plot to be drawn """ xstep = 1.0 ystep = 1.0 self.contourXS = scipy.linspace(xstep / 2., self.model.pixelsX - xstep / 2., self.model.pixelsX - 1) self.contourYS = scipy.linspace(ystep / 2., self.model.pixelsY - ystep / 2., self.model.pixelsY - 1) logger.debug("contour initialise fit debug. xs shape %s" % self.contourXS.shape) logger.debug("contour initialise xs= %s" % self.contourXS) self._fit_value = chaco.ImageData(data=scipy.array([]), value_depth=1) self.lineplot = chaco.ContourLinePlot( index=self._image_index, value=self._fit_value, index_mapper=chaco.GridMapper( range=self.polyplot.index_mapper.range), levels=self.contourLevels) self.centralContainer.add(self.lineplot) self.plotData.set_data("fitLine_indexHorizontal", self.model.xs) self.plotData.set_data("fitLine_indexVertical", self.model.ys) self.crossPlotVertical.plot( ("fitLine_indexVertical", "fitLine_valueVertical"), type="line", name="fitVertical") self.crossPlotHorizontal.plot( ("fitLine_indexHorizontal", "fitLine_valueHorizontal"), type="line", name="fitHorizontal") logger.debug("initialise fit plot %s " % self.crossPlotVertical.plots) def addFitPlot(self, fit): """add a contour plot on top using fitted data and add additional plots to sidebars (TODO) """ logger.debug("adding fit plot with fit %s " % fit) if not fit.fitted: logger.error( "cannot add a fitted plot for unfitted data. Run fit first") return if not self.drawFitBool: logger.info("first fit plot so initialising contour plot") self.initialiseFitPlot() logger.info("attempting to set fit data") self.contourPositions = [ scipy.tile(self.contourXS, len(self.contourYS)), scipy.repeat(self.contourYS, len(self.contourXS)) ] #for creating data necessary for gauss2D function zsravelled = fit.fitFunc(self.contourPositions, *fit._getCalculatedValues()) # logger.debug("zs ravelled shape %s " % zsravelled.shape) self.contourZS = zsravelled.reshape( (len(self.contourYS), len(self.contourXS))) # logger.debug("zs contour shape %s " % self.contourZS.shape) # logger.info("shape contour = %s " % self.contourZS) self._fit_value.data = self.contourZS self.container.invalidate_draw() self.container.request_redraw() self.drawFitBool = True def update(self, model): logger.info("updating plot") # if self.selectedFile=="": # logger.warning("selected file was empty. Will not attempt to update plot.") # return if self.autoRangeColor: self.colorbar.index_mapper.range.low = model.minZ self.colorbar.index_mapper.range.high = model.maxZ self._image_index.set_data(model.xs, model.ys) self._image_value.data = model.zs self.plotData.set_data("line_indexHorizontal", model.xs) self.plotData.set_data("line_indexVertical", model.ys) if self.drawFitBool: self.plotData.set_data("fitLine_indexHorizontal", self.contourXS) self.plotData.set_data("fitLine_indexVertical", self.contourYS) self.updatePlotLimits() self._image_index.metadata_changed = True self.container.invalidate_draw() self.container.request_redraw() #--------------------------------------------------------------------------- # Event handlers #--------------------------------------------------------------------------- def _metadata_changed(self, old, new): """ This function takes out a cross section from the image data, based on the line inspector selections, and updates the line and scatter plots.""" if self.horizontalAutoRange: self.crossPlotHorizontal.value_range.low = self.model.minZ self.crossPlotHorizontal.value_range.high = self.model.maxZ if self.verticalAutoRange: self.crossPlotVertical.value_range.low = self.model.minZ self.crossPlotVertical.value_range.high = self.model.maxZ if self._image_index.metadata.has_key("selections"): selections = self._image_index.metadata["selections"] if not selections: #selections is empty list return #don't need to do update lines as no mouse over screen. This happens at beginning of script x_ndx, y_ndx = selections if y_ndx and x_ndx: self.plotData.set_data("line_valueHorizontal", self._image_value.data[y_ndx, :]) self.plotData.set_data("line_valueVertical", self._image_value.data[:, x_ndx]) xdata, ydata = self._image_index.get_data() xdata, ydata = xdata.get_data(), ydata.get_data() self.plotData.set_data("scatter_indexHorizontal", scipy.array([xdata[x_ndx]])) self.plotData.set_data("scatter_indexVertical", scipy.array([ydata[y_ndx]])) self.plotData.set_data( "scatter_valueHorizontal", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_valueVertical", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_colorHorizontal", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_colorVertical", scipy.array([self._image_value.data[y_ndx, x_ndx]])) if self.drawFitBool: self.plotData.set_data("fitLine_valueHorizontal", self._fit_value.data[y_ndx, :]) self.plotData.set_data("fitLine_valueVertical", self._fit_value.data[:, x_ndx]) else: self.plotData.set_data("scatter_valueHorizontal", scipy.array([])) self.plotData.set_data("scatter_valueVertical", scipy.array([])) self.plotData.set_data("line_valueHorizontal", scipy.array([])) self.plotData.set_data("line_valueVertical", scipy.array([])) self.plotData.set_data("fitLine_valueHorizontal", scipy.array([])) self.plotData.set_data("fitLine_valueVertical", scipy.array([])) def _colormap_changed(self): self._cmap = colormaps.color_map_name_dict[self.colormap] if hasattr(self, "polyplot"): value_range = self.polyplot.color_mapper.range self.polyplot.color_mapper = self._cmap(value_range) value_range = self.crossPlotHorizontal.color_mapper.range self.crossPlotHorizontal.color_mapper = self._cmap(value_range) # FIXME: change when we decide how best to update plots using # the shared colormap in plot object self.crossPlotHorizontal.plots["dot"][0].color_mapper = self._cmap( value_range) self.crossPlotVertical.plots["dot"][0].color_mapper = self._cmap( value_range) self.container.request_redraw() def _colorMapRangeLow_changed(self): self.colorbar.index_mapper.range.low = self.colorMapRangeLow def _colorMapRangeHigh_changed(self): self.colorbar.index_mapper.range.high = self.colorMapRangeHigh def _horizontalLowerLimit_changed(self): self.crossPlotHorizontal.value_range.low = self.horizontalLowerLimit def _horizontalUpperLimit_changed(self): self.crossPlotHorizontal.value_range.high = self.horizontalUpperLimit def _verticalLowerLimit_changed(self): self.crossPlotVertical.value_range.low = self.verticalLowerLimit def _verticalUpperLimit_changed(self): self.crossPlotVertical.value_range.high = self.verticalUpperLimit def _autoRange_changed(self): if self.autoRange: self.colorbar.index_mapper.range.low = self.minz self.colorbar.index_mapper.range.high = self.maxz def _num_levels_changed(self): if self.num_levels > 3: self.polyplot.levels = self.num_levels self.lineplot.levels = self.num_levels def _colorMapRangeLow_default(self): logger.debug("setting color map rangle low default") return self.model.minZ def _colorMapRangeHigh_default(self): return self.model.maxZ def _horizontalLowerLimit_default(self): return self.model.minZ def _horizontalUpperLimit_default(self): return self.model.maxZ def _verticalLowerLimit_default(self): return self.model.minZ def _verticalUpperLimit_default(self): return self.model.maxZ def _selectedFit_changed(self, selected): logger.debug("selected fit was changed") def _fixAspectRatioBool_changed(self): if self.fixAspectRatioBool: #using zoom range works but then when you reset zoom this function isn't called... # rangeObject = self.polyplot.index_mapper.range # xrangeValue = rangeObject.high[0]-rangeObject.low[0] # yrangeValue = rangeObject.high[1]-rangeObject.low[1] # logger.info("xrange = %s, yrange = %s " % (xrangeValue, yrangeValue)) # aspectRatioSquare = (xrangeValue)/(yrangeValue) # self.polyplot.aspect_ratio=aspectRatioSquare self.centralContainer.aspect_ratio = float( self.model.pixelsX) / float(self.model.pixelsY) #self.polyplot.aspect_ratio = self.model.pixelsX/self.model.pixelsY else: self.centralContainer.aspect_ratio = None #self.polyplot.aspect_ratio = None self.container.request_redraw() self.centralContainer.request_redraw() def updatePlotLimits(self): """just updates the values in the GUI """ if self.autoRangeColor: self.colorMapRangeLow = self.model.minZ self.colorMapRangeHigh = self.model.maxZ if self.horizontalAutoRange: self.horizontalLowerLimit = self.model.minZ self.horizontalUpperLimit = self.model.maxZ if self.verticalAutoRange: self.verticalLowerLimit = self.model.minZ self.verticalUpperLimit = self.model.maxZ def _selectedFile_changed(self): self.model.getImageData(self.selectedFile) if self.updatePhysicsBool: self.physics.updatePhysics() for fit in self.fitList: fit.fitted = False fit.fittingStatus = fit.notFittedForCurrentStatus if fit.autoFitBool: #we should automatically start fitting for this Fit fit._fit_routine( ) #starts a thread to perform the fit. auto guess and auto draw will be handled automatically self.update_view() #update log file plot if autorefresh is selected if self.logFilePlotObject.autoRefresh: try: self.logFilePlotObject.refreshPlot() except Exception as e: logger.error("failed to update log plot - %s...." % e.message) def _cameraModel_changed(self): """camera model enum can be used as a helper. It just sets all the relevant editable parameters to the correct values. e.g. pixels size, etc. cameras: "Andor Ixon 3838", "Apogee ALTA" """ logger.info("camera model changed") if self.cameraModel == "ANDOR0": self.model.pixelsX = 512 self.model.pixelsY = 512 self.physics.pixelSize = 16.0 self.physics.magnification = 2.0 self.searchString = "ANDOR0" elif self.cameraModel == "ALTA0": self.model.pixelsX = 768 self.model.pixelsY = 512 self.physics.pixelSize = 9.0 self.physics.magnification = 0.5 self.searchString = "ALTA0" elif self.cameraModel == "ALTA1": self.model.pixelsX = 768 self.model.pixelsY = 512 self.physics.pixelSize = 9.0 self.physics.magnification = 4.25 self.searchString = "ALTA1" elif self.cameraModel == "ANDOR1": self.model.pixelsX = 512 self.model.pixelsY = 512 self.physics.pixelSize = 16.0 self.physics.magnification = 2.0 self.searchString = "ANDOR1" else: logger.error("unrecognised camera model") self.refreshFitReferences() self.model.getImageData(self.selectedFile) def refreshFitReferences(self): """When aspects of the image change so that the fits need to have properties updated, it should be done by this function""" for fit in self.fitList: fit.endX = self.model.pixelsX fit.endY = self.model.pixelsY def _pixelsX_changed(self): """If pixelsX or pixelsY change, we must send the new arrays to the fit functions """ logger.info("pixels X Change detected") self.refreshFitReferences() self.update(self.model) self.model.getImageData(self.selectedFile) def _pixelsY_changed(self): """If pixelsX or pixelsY change, we must send the new arrays to the fit functions """ logger.info("pixels Y Change detected") self.refreshFitReferences() self.update(self.model) self.model.getImageData(self.selectedFile) @traits.on_trait_change('model') def update_view(self): if self.model is not None: self.update(self.model) def _save_image(self, originalFilePath, newFilePath): """given the original file path this saves a new copy to new File path """ shutil.copy2(originalFilePath, newFilePath) def _save_image_as(self): """ opens a save as dialog and allows user to save a copy of current image to a custom location with a custom name""" originalFilePath = str( self.selectedFile ) #so that this can't be affected by auto update after the dialog is open file_wildcard = str("PNG (*.png)|All files|*") default_directory = os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Data", "savedEagleImages") dialog = FileDialog(action="save as", default_directory=default_directory, wildcard=file_wildcard) dialog.open() if dialog.return_code == OK: self._save_image(originalFilePath, dialog.path) logger.debug("custom image copy made") def _save_image_default(self): head, tail = os.path.split(self.selectedFile) default_file = os.path.join("\\\\ursa", "AQOGroupFolder", "Experiment Humphry", "Data", "savedEagleImages", tail) self._save_image(self.selectedFile, default_file) logger.debug("default image copy made")
class Librarian(traits.HasTraits): """Librarian provides a way of writing useful information into the log folder for eagle logs. It is designed to make the information inside an eagle log easier to come back to. It mainly writes default strings into the comments file in the log folder""" sectionNames = traits.List(traits.Str) sectionName = traits.Enum(values="sectionNames",desc="Investigation this eagle Log is for. Corresponds to tab in OneNote") importance = traits.Range(1,3,1) availableUsers = traits.List(traits.Str) # loaded from json file user = traits.Enum(values='availableUsers') keywords = traits.String('') writeToOneNoteButton = traits.Button("save") refreshInformation = traits.Button("refresh") # saveImage = traits.Button("save plot") # removed, because there is the same functionality as the button of the Matplotlibify dialog axisList = AxisSelector() purposeBlock = EntryBlock(fieldName="What is the purpose of this log?") resultsBlock = EntryBlock(fieldName = "Explain what the data shows (important parameters that change, does it make sense etc.)?") commentsBlock = EntryBlock(fieldName = "Anything Else?") matplotlibifyReference = traits.Any()# logFilePlotReference = traits.Any()#traits.Instance(plotObjects.logFilePlot.LogFilePlot)#gives access to most of the required attributes logFilePlotsReference = traits.Any()#traits.Instance(logFilePlots.LogFilePlots)#refernce to logFilePlots object # notebooks = traits.Enum(values = "notebookNames") # we could let user select from a range of notebooks # notebookNames = traits.List notebookName = traits.String("Investigations") logName = traits.String("") xAxis = traits.String("") yAxis = traits.String("") traits_view = traitsui.View( traitsui.VGroup( traitsui.HGroup(traitsui.Item("sectionName",label="investigation"),traitsui.Item("user",label="user")), traitsui.Item("importance",label="importance",style='custom'), traitsui.Item("logName",show_label=False, style="readonly"), traitsui.Item("keywords",label='keywords'), traitsui.Item("axisList",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("purposeBlock",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("resultsBlock",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("commentsBlock",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.Item("matplotlibifyReference",show_label=False, editor=traitsui.InstanceEditor(),style='custom'), traitsui.HGroup( # traitsui.Item("saveImage",show_label=False), # see above why commented traitsui.Item("writeToOneNoteButton",show_label=False), traitsui.Item("refreshInformation",show_label=False) ), ) , resizable=True , kind ="live", title="Eagle OneNote", width=300, height=500 ) def __init__(self, **traitsDict): """Librarian object requires the log folder it is referring to. If a .csv file is given as logFolder argument it will use parent folder as the logFolder""" super(Librarian, self).__init__(**traitsDict) if os.path.isfile(self.logFolder): self.logFolder = os.path.split(self.logFolder)[0] else: logger.debug("found these in %s: %s" %(self.logFolder, os.listdir(self.logFolder) )) # load global settings filename = os.path.join("config","librarian.json") with open(filename, 'r') as f: settings = json.load( f ) self.availableUsers = settings["users"] import matplotlibify.matplotlibify self.matplotlibifyReference = matplotlibify.matplotlibify.Matplotlibify(logFilePlotReference=self.logFilePlotReference,logFilePlotsReference=self.logFilePlotsReference, logLibrarianReference=self) self.matplotlibifyReference.templatesFolder = os.path.join("\\\\ursa","AQOGroupFolder","Experiment Humphry","Experiment Control And Software","LogFilePlots","matplotlibify","templates") self.matplotlibifyReference.templateFile = os.path.join(self.matplotlibifyReference.templatesFolder,"matplotlibifyDefaultTemplate.py") self.matplotlibifyReference.generatedScriptLocation = os.path.join(self.matplotlibifyReference.templatesFolder) self.logName = os.path.split(self.logFolder)[1] self.logFile = os.path.join(self.logFolder, os.path.split(self.logFolder)[1]+".csv") self.axisList.logFile = self.logFile#needs a copy so it can calculate valid values self.axisList.masterList = self.axisList._masterList_default() self.axisList.masterListWithNone = self.axisList._masterListWithNone_default() if self.xAxis != "": self.axisList.xAxis = self.xAxis if self.yAxis != "": self.axisList.yAxis = self.yAxis self.eagleOneNote = eagleLogsOneNote.EagleLogOneNote(notebookName = self.notebookName, sectionName = self.sectionName) self.sectionNames = self._sectionNames_default() self.setupLogPage() # load local settings filename = os.path.join(self.logFolder,"logFilePlotSettings","librarian.json") if os.path.exists(filename): with open(filename, 'r') as f: settings = json.load( f ) self.sectionName = settings["section"] def setupLogPage(self): """setup logPage object and change text boxes in GUI accordingly """ logPage = self.eagleOneNote.setPage(self.logName) # # except Exception as e: # logger.error("failed to created an EagleOneNote Instance. This could happen for many reasons. E.g. OneNote not installed or most likely, the registry is not correct. See known bug and fix in source code of onenotepython module:%s" % e.message) if logPage is not None:#page exists self.purposeBlock.textBlock = self.eagleOneNote.getOutlineText("purpose") self.resultsBlock.textBlock = self.eagleOneNote.getOutlineText("results") self.commentsBlock.textBlock = self.eagleOneNote.getOutlineText("comments") xAxis,yAxis,series = self.eagleOneNote.getParametersOutlineValues() try: self.axisList.xAxis,self.axisList.yAxis,self.axisList.series = xAxis,yAxis,series except Exception as e: logger.error("error when trying to read analysis parameters: %s" % e.message) self.pageExists = True else: self.pageExists = False self.purposeBlock.textBlock = "" self.resultsBlock.textBlock = "" self.commentsBlock.textBlock = "" #could also reset axis list but it isn't really necessary def isUserInputGood(self): """runs a bunch of checks and provides a warning if user has written a bad eagle log. Returns True if the user has written a good eagle log and false if they havent""" issueStrings = [] if self.axisList.series == "None": issueStrings.append("You have no Series selected, are there really no series for this log? If there are multiple series note this down in the Comments box.") if len(self.purposeBlock.textBlock)<200: issueStrings.append("The purpose text of your librarian is less than 200 characters. This is too short! Describe the measurement so that you could remember what you are doing in 2 years time.") if len(self.resultsBlock.textBlock)<200: issueStrings.append("The results text of your librarian is less than 200 characters. This is too short! Describe the results so that you can understand what was learnt in 2 years time. If this doesn't need to be long why do you need to save the log?") numberOfIssues = len(issueStrings) if numberOfIssues == 0: return True else: finalString = "There are issues with your Eagle Log\nPlease check the below points. If you are happy to continue press OK, otherwise press cancel and correct these issues.\n" finalString+= "u\n\u2022".join(issueStrings) return traitsui.error(message=finalString) def _sectionNames_default(self): """returns the the list of section (tab) names in the oneNote """ if hasattr(self, "eagleOneNote"): return self.eagleOneNote.getSectionNames() else: return ['General'] def _sectionName_changed(self): purposeBlockTemp = self.purposeBlock resultsBlockTemp = self.resultsBlock commentsBlocktemp = self.commentsBlock if hasattr(self, "eagleOneNote"): self.eagleOneNote.sectionName=self.sectionName self.eagleOneNote.eagleLogPages = self.eagleOneNote.notebook.getPagesfromSectionName(self.sectionName) self.setupLogPage() self.purposeBlock = purposeBlockTemp self.resultsBlock = resultsBlockTemp self.commentsBlock = commentsBlocktemp def _writeToOneNoteButton_fired(self): """writes content of librarian to one note page """ isGoodLibrarian = self.isUserInputGood()#False and they clicked cancel so just return if not isGoodLibrarian: return if not self.pageExists: self.eagleOneNote.createNewEagleLogPage(self.logName, refresh=True, setCurrent=True) self.pageExists = True self.eagleOneNote.setOutline("purpose", self.purposeBlock.textBlock,rewrite=False) self.eagleOneNote.setOutline("results", self.resultsBlock.textBlock,rewrite=False) automatic_comments = self._generate_automatic_additional_comments() self.eagleOneNote.setOutline("comments", automatic_comments+self.commentsBlock.textBlock,rewrite=False) self.eagleOneNote.setDataOutline(self.logName, rewrite=False) self.eagleOneNote.setParametersOutline(self.axisList.xAxis, self.axisList.yAxis, self.axisList.series, rewrite=False) self.eagleOneNote.currentPage.rewritePage() #now to get resizing done well we want to completely repull the XML and data #brute force method: self.eagleOneNote = eagleLogsOneNote.EagleLogOneNote(notebookName = self.notebookName, sectionName = self.sectionName) logPage = self.eagleOneNote.setPage(self.logName)#this sets current page of eagleOneNote self.eagleOneNote.organiseOutlineSizes() self.saveLocalSettings() def saveImageToFile(self,name=None,oneNote=True): if name is None:#use automatic location logFolder,tail = os.path.split(self.logFile) #logName = tail.strip(".csv")+" - "+str(self.xAxis)+" vs "+str(self.yAxis) logName =str(self.xAxis)+" vs "+str(self.yAxis) filename = datetime.datetime.today().strftime("%Y-%m-%dT%H%M%S")+"-"+logName+".png" imageFileName =os.path.join(logFolder, filename) else: imageFileName = name logger.info("saving image: %s" % imageFileName) self.matplotlibifyReference.autoSavePlotWithMatplotlib(imageFileName) if oneNote: try: logger.info("attempting to save image to oneNote page") logName = tail.replace(".csv","")#this is what the name of the page should be in oneNote eagleOneNote = eagleLogsOneNote.EagleLogOneNote(notebookName =self.notebookName, sectionName =self.sectionName) logPage = eagleOneNote.setPage(logName) if logPage is None: logger.info("saving image to one note . page doesn't exist. creating page") eagleOneNote.createNewEagleLogPage(logName, refresh=True, setCurrent=True) logger.debug("attempting to save image") eagleOneNote.addImageToPlotOutline(imageFileName, (6.3*300,3.87*300),rewrite=True) if self.fitLogFileBool and self.fitPlot is not None and self.logFilePlotFitterReference.isFitted: #these conditions mean that we have the fit data we can write to one note with the image! logger.info("also writing fit data to one note") eagleOneNote.setOutline("plots", eagleOneNote.getOutlineText("plots")+self.logFilePlotFitterReference.modelFitResult.fit_report()) eagleOneNote.currentPage.rewritePage() #now to get resizing done well we want to completely repull the XML and data #brute force method: eagleOneNote = eagleLogsOneNote.EagleLogOneNote(notebookName = self.notebookName, sectionName = self.sectionName) logPage = eagleOneNote.setPage(logName) eagleOneNote.organiseOutlineSizes() except Exception as e: logger.error("failed to save the image to OneNote. error: %s " % e.message) def _saveImage_fired(self): self.logFilePlotReference.savePlotAsImage(self.matplotlibifyReference,self) def _generate_automatic_additional_comments(self): """generates some text that is always added to the beginning of additional comments """ return "This log was written by %s. Importance ranking is **%s**.\nKeywords: %s" % (self.user, self.importance, self.keywords) def saveLocalSettings(self): filename = os.path.join(self.logFolder,"logFilePlotSettings","librarian.json") settings = { "section": self.sectionName } with open(filename, 'w') as f: json.dump( settings, f )
class DACHandler(traits.HasTraits): connection = ADEval_client.Connection() rpiADC = rpiADCClient.Connection() def __init__(self): self.x = ad.ADEvalBC() self.connection.connect() #connect to ADEval #self.rpiADC.connect() # connect to rpiADC #self.start_timer() DACC1 = DACChannel("DAC A", 'A', connection, rpiADC) DACC2 = DACChannel("DAC B", 'B', connection, rpiADC) DACC3 = DACChannel("DAC C", 'C', connection, rpiADC) DACC4 = DACChannel("DAC D", 'D', connection, rpiADC) power_A = traits.Bool(desc="Power A") power_B = traits.Bool(desc="Power B") power_C = traits.Bool(desc="Power C") power_D = traits.Bool(desc="Power D") Power_switch = traits.Button() # def start_timer(self): # print "Timer started" # self.timer = Timer(1000.0, self.update_channels) # # def update_channels(self): # self.DACC1._adcVoltage_update() # self.DACC2._adcVoltage_update() # self.DACC3._adcVoltage_update() # self.DACC4._adcVoltage_update() groupLeft = traitsui.VGroup( traitsui.Item('DACC1', editor=traitsui.InstanceEditor(), style='custom', show_label=False, resizable=True), traitsui.Item('power_A'), traitsui.Item('DACC3', editor=traitsui.InstanceEditor(), style='custom', show_label=False), traitsui.Item('power_C'), ) groupRight = traitsui.VGroup( traitsui.Item('DACC2', editor=traitsui.InstanceEditor(), style='custom', show_label=False), traitsui.Item('power_B'), traitsui.Item('DACC4', editor=traitsui.InstanceEditor(), style='custom', show_label=False), traitsui.Item('power_D'), ) groupH = traitsui.HGroup( groupLeft, groupRight, ) groupall = traitsui.VGroup( groupH, traitsui.Item('Power_switch', show_label=False), ) def _Power_switch_fired(self): #print self.x.setPowerMode( [self.power_A, self.power_B, self.power_C, self.power_D] ) cmd = "cmd=" + self.x.setPowerMode( [self.power_A, self.power_B, self.power_C, self.power_D]) self.connection.send(cmd) traits_view = traitsui.View(groupall, resizable=True)
class ExperimentSnake(traits.HasTraits): """Main Experiment Snake GUI that sends arbitrary actions based on the experiment runner sequence and actions that have been set up.""" #mainLog = utilities.TextDisplay() mainLog = outputStream.OutputStream() statusString = traits.String("Press Start Snake to begin...") isRunning = traits.Bool(False) # true when the snake is running sequenceStarted = traits.Bool( False) # flashes true for ~1ms when sequence starts queue = traits.Int(0) variables = traits.Dict( key_trait=traits.Str, value_trait=traits.Float ) #dictionary mapping variable names in Exp control to their values in this sequence timingEdges = traits.Dict( key_trait=traits.Str, value_trait=traits.Float ) #dictionary mapping timing Edge names in Exp control to their values in this sequence statusList = [ ] #eventually will contain the information gathered from experiment Runner each time we poll startAction = traitsui.Action(name='start', action='_startSnake', image=pyface.image_resource.ImageResource( os.path.join('icons', 'start.png'))) stopAction = traitsui.Action(name='stop', action='_stopSnakeToolbar', image=pyface.image_resource.ImageResource( os.path.join('icons', 'stop.png'))) reloadHWAsAction = traitsui.Action( name='reload', action='_reloadHWAsToolbar', image=pyface.image_resource.ImageResource( os.path.join('icons', 'reload.png'))) connectionTimer = traits.Instance( Timer ) # polls the experiment runner and starts off callbacks at appropriate times statusStringTimer = traits.Instance( Timer) #updates status bar at regular times (less freque) getCurrentTimer = traits.Instance( Timer ) #waits for get current to return which marks the beginning of a sequence getCurrentThread = traits.Instance(SocketThread) connectionPollFrequency = traits.Float( 1000.0) #milliseconds defines accuracy you will perform callbacks at statusStringFrequency = traits.Float(2000.0) #milliseconds getCurrentFrequency = traits.Float( 1000.0) #milliseconds should be shorter than the sequence timeRunning = traits.Float(0.0) #how long the sequence has been running totalTime = traits.Float(0.0) # total length of sequence runnerHalted = traits.Bool(True) # true if runner is halted haltedCount = 0 progress = traits.Float(0.0) # % of cycle complete #progressBar = ProgressDialog() hardwareActions = traits.List(hardwareAction.hardwareAction.HardwareAction) examineVariablesDictionary = traits.Instance( variableDictionary.ExamineVariablesDictionary) xmlString = "" # STRING that will contain entire XML File menubar = traitsui.MenuBar( traitsui.Menu( traitsui.Action(name='Start Snake', action='_startSnake'), traitsui.Action(name='Stop Snake', action='_stopSnake'), traitsui.Action(name='Reload', action='_reloadHWAs'), traitsui.Menu(traitsui.Action(name='DEBUG', action='_changeLoggingLevelDebug'), traitsui.Action(name='INFO', action='_changeLoggingLevelInfo'), traitsui.Action(name='WARNING', action='_changeLoggingLevelWarning'), traitsui.Action(name='ERROR', action='_changeLoggingLevelError'), name="Log Level"), name='Menu')) toolbar = traitsui.ToolBar(startAction, stopAction, reloadHWAsAction) mainSnakeGroup = traitsui.VGroup( traitsui.Item('statusString', show_label=False, style='readonly'), traitsui.Item('mainLog', show_label=False, springy=True, style='custom', editor=traitsui.InstanceEditor())) hardwareActionsGroup = traitsui.Group(traitsui.Item( 'hardwareActions', show_label=False, style='custom', editor=traitsui.ListEditor(style="custom")), label="Hardware Actions", show_border=True) variableExaminerGroup = traitsui.Group(traitsui.Item( "examineVariablesDictionary", editor=traitsui.InstanceEditor(), style="custom", show_label=False), label="Variable Examiner") sidePanelGroup = traitsui.VSplit(hardwareActionsGroup, variableExaminerGroup) traits_view = traitsui.View(traitsui.HSplit(sidePanelGroup, mainSnakeGroup, show_labels=True), resizable=True, menubar=menubar, toolbar=toolbar, width=0.5, height=0.75, title="Experiment Snake", icon=pyface.image_resource.ImageResource( os.path.join('icons', 'snakeIcon.ico'))) def __init__(self, **traits): """ takes no arguments to construct the snake. Everything is done through GUI. Snake construction makes a ExperimentSnakeConnection object and writes to the main log window""" super(ExperimentSnake, self).__init__(**traits) self.connection = experimentRunnerConnection.Connection( ) #can override default ports and IP self.hardwareActions = [ hardwareAction.sequenceLoggerHWA.SequenceLogger( 0.0, snakeReference=self), hardwareAction.experimentTablesHWA.ExperimentTables( 0.0, snakeReference=self, enabled=False), hardwareAction.dlicEvapHWA.EvaporationRamp(1.0, snakeReference=self), #hardwareAction.dlicRFSweepHWA.DLICRFSweep(1.0, snakeReference = self,enabled=False), hardwareAction.dlicRFSweepLZHWA.DLICRFSweep(1.0, snakeReference=self, enabled=False), hardwareAction.dlicRFSweepLZWithPowerCtrlHWA.DLICRFSweep( 1.0, snakeReference=self, enabled=False), hardwareAction.dlicRFSweepLZWithPowerCtrl13PreparationHWA. DLICRFSweep(1.0, snakeReference=self, enabled=True), hardwareAction.dlicPiPulseHWA.DLICPiPulse(1.0, snakeReference=self, enabled=False), hardwareAction.evapAttenuationHWA.EvapAttenuation( 1.0, snakeReference=self), hardwareAction.greyMollassesOffsetFreqHWA.GreyMollassesOffset( 1.0, snakeReference=self, enabled=False), hardwareAction.evapAttenuation2HWA.EvapAttenuation( "EvapSnakeAttenuationTimeFinal", snakeReference=self, enabled=False), hardwareAction.picomotorPlugHWA.PicomotorPlug(1.0, snakeReference=self, enabled=False), hardwareAction.windFreakOffsetLockHWA.WindFreak( 0.0, snakeReference=self, enabled=False), hardwareAction.windFreakOffsetLockHighFieldImagingHWA.WindFreak( 0.0, snakeReference=self, enabled=True), hardwareAction.windFreakOffsetLock6ImagingHWA.WindFreak( 2.0, snakeReference=self, enabled=False), hardwareAction.windFreak6To1HWA.WindFreak(2.0, snakeReference=self, enabled=False), hardwareAction.windFreakOffsetLockLaser3.WindFreak( 3.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSEOMFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaZSEOMAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaSpecFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiImaging( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiImagingDetuning( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiPushPulseAttenuation( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiPushPulseDetuning( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaDarkSpotAOMFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaDarkSpotAOMAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaMOTFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaMOTAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaMOTEOMAtten( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaImagingDP( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiMOTRep( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiMOTCool( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelLiOpticalPump( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNa2to2OpticalPumpingFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNa2to2OpticalPumpingAtt( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaHighFieldImagingFreq( 1.0, snakeReference=self, enabled=False), hardwareAction.AOMChannelHWAs.AOMChannelNaHighFieldImagingAtt( 1.0, snakeReference=self, enabled=False), hardwareAction.digitalMultimeterCurrentMeasureHWA. DigitalMultimeterMeasurement(1.0, snakeReference=self, enabled=True), hardwareAction.MXGPiPulseHWA.PiPulse(1.0, snakeReference=self, enabled=False), hardwareAction.variableExplorerHWA.VariableExplorer( 2.0, snakeReference=self, enabled=False), hardwareAction.jds6600HWA.JDS6600HWA(1.0, snakeReference=self, enabled=False), hardwareAction.watchdogHWA.WatchdogHWA(18.0, snakeReference=self, enabled=True) ] introString = """Welcome to experiment snake.""" self.mainLog.addLine(introString, 1) def initialiseHardwareActions(self): for hdwAct in self.hardwareActions: if hdwAct.enabled: returnString = hdwAct.init() hdwAct.variablesReference = self.variables self.mainLog.addLine(returnString) def closeHardwareActions(self): """ this function is called when the user presses stop key. it should cleanly close or shutdown all hardware. user must appropriately implement the hardware action close function""" for hdwAct in self.hardwareActions: if hdwAct.initialised: returnString = hdwAct.close() self.mainLog.addLine(returnString) def _startSnake(self): """action call back from menu or toolbar. Simply starts the timer that polls the runner and makes the isRunning bool true """ self.mainLog.addLine("Experiment Snake Started", 1) self.isRunning = True self.getCurrentBlocking() self.initialiseHardwareActions() self.startTimers() def newSequenceStarted(self): """called by GetCurrent Thread at the beginning of every sequence """ if self.isRunning: #otherwise we have already stopped before new sequence began again self.getStatusUpdate() self.mainLog.addLine("New cycle started: %s" % self.statusList[0], 1) self.refreshExamineVariablesDictionary( ) # update the examine variables dictionary to reflect the latest values self.refreshVariableDependentCallbackTimes( ) # if a callback time is a timing edge name or variable name we must pull the value here else: self.mainLog.addLine("final connection closed") for hdwAct in self.hardwareActions: hdwAct.awaitingCallback = True def _stopSnakeToolbar(self): """if snake is stopped, addLine to main log and then run stopSnake """ self.mainLog.addLine( "Experiment Snake Stopped (you should still wait till the end of this sequence before continuing)", 1) self._stopSnake() def _reloadHWAsToolbar(self): """if snake is stopped, addLine to main log and then run stopSnake """ self.mainLog.addLine( "Experiment Snake Stopped (you should still wait till the end of this sequence before continuing)", 1) self._reloadHWAs() def _reloadHWAs(self): """if snake is stopped, addLine to main log and then run stopSnake """ self.mainLog.addLine("Reloading hardware actions (advanced feature)", 3) reload(hardwareAction.hardwareAction) reload(hardwareAction.sequenceLoggerHWA) reload(hardwareAction.dlicEvapHWA) reload(hardwareAction.dlicRFSweepHWA) reload(hardwareAction.dlicRFSweepHWA) reload(hardwareAction.evapAttenuationHWA) reload(hardwareAction.evapAttenuation2HWA) reload(hardwareAction.picomotorPlugHWA) reload(hardwareAction.windFreakOffsetLockHWA) #reload( hardwareAction.AOMChannelHWAs)#CAUSES REFERENCING PROBLEMS! reload(hardwareAction.experimentTablesHWA) reload(hardwareAction.windFreakOffsetLockHighFieldImagingHWA) reload(hardwareAction.greyMollassesOffsetFreqHWA) reload(hardwareAction.dlicRFSweepLZHWA) reload(hardwareAction.digitalMultimeterCurrentMeasureHWA) self.__init__() def stopTimers(self): """stops all timers with error catching """ try: #stop any previous timer, should only have 1 timer at a time if self.connectionTimer is not None: self.connectionTimer.stop() except Exception as e: logger.error( "couldn't stop current timer before starting new one: %s" % e.message) try: #stop any previous timer, should only have 1 timer at a time if self.statusStringTimer is not None: self.statusStringTimer.stop() except Exception as e: logger.error( "couldn't stop current timer before starting new one: %s" % e.message) try: #stop any previous timer, should only have 1 timer at a time if self.getCurrentTimer is not None: self.getCurrentTimer.stop() except Exception as e: logger.error( "couldn't stop current timer before starting new one: %s" % e.message) def _stopSnake(self): """Simply stops the timers, shuts down hardware and sets isRunning bool false """ self.stopTimers() self.closeHardwareActions() self.isRunning = False def startTimers(self): """This timer object polls the experiment runner regularly polling at any time""" #stop any previous timers self.stopTimers() #start timer self.connectionTimer = Timer(self.connectionPollFrequency, self.getStatus) time.sleep(0.1) self.statusStringTimer = Timer(self.statusStringFrequency, self.updateStatusString) time.sleep(0.1) self.getCurrentTimer = Timer(self.getCurrentFrequency, self.getCurrent) """Menu action function to change logger level """ logger.info("timers started") def getStatus(self): """calls the connection objects get status function and updates the statusList """ logger.debug("starting getStatus") try: self.getStatusUpdate() self.checkForCallback() except Exception as e: logger.error("error in getStatus Function") logger.error("error: %s " % e.message) self.mainLog.addLine( "error in getStatus Function. Error: %s" % e.message, 4) def getStatusUpdate(self): """Calls get status and updates times """ try: statusString = self.connection.getStatus() except socket.error as e: logger.error( "failed to get status . message=%s . errno=%s . errstring=%s " % (e.message, e.errno, e.strerror)) self.mainLog.addLine( "Failed to get status from Experiment Runner. message=%s . errno=%s . errstring=%s" % (e.message, e.errno, e.strerror), 3) self.mainLog.addLine( "Cannot update timeRunning - callbacks could be wrong this sequence!", 4) return self.statusList = statusString.split("\n") timeFormat = '%d/%m/%Y %H:%M:%S' timeBegin = datetime.datetime.strptime(self.statusList[2], timeFormat) timeCurrent = datetime.datetime.strptime(self.statusList[3], timeFormat) self.timeRunning = (timeCurrent - timeBegin).total_seconds() logger.debug("time Running = %s " % self.timeRunning) def checkForCallback(self): """if we've received a sequence, we check through all callback times and send off a callback on a hardware action if appropriate""" try: for hdwAct in [ hdwA for hdwA in self.hardwareActions if hdwA.enabled ]: #only iterate through enable hardware actions if hdwAct.awaitingCallback and self.timeRunning >= hdwAct.callbackTime: #callback should be started! try: logger.debug("attempting to callback %s " % hdwAct.hardwareActionName) hdwAct.setVariablesDictionary(self.variables) logger.debug("vars dictionary set to %s " % self.variables) callbackReturnString = hdwAct.callback() self.mainLog.addLine( "%s @ %s secs : %s" % (hdwAct.hardwareActionName, self.timeRunning, callbackReturnString), 2) hdwAct.awaitingCallback = False hdwAct.callbackCounter += 1 except Exception as e: logger.error( "error while performing callback on %s. see error message below" % (hdwAct.hardwareActionName)) logger.error("error: %s " % e.message) self.mainLog.addLine( "error while performing callback on %s. Error: %s" % (hdwAct.hardwareActionName, e.message), 4) except Exception as e: logger.error("error in checkForCallbackFunction") logger.error("error: %s " % e.message) self.mainLog.addLine( "error in checkForCallbackFunction. Error: %s" % e.message, 4) def getCurrent(self): """calls the connection objects get status function and updates the variables dictionary """ if self.getCurrentThread and self.getCurrentThread.isAlive(): #logger.debug( "getCurrent - already waiting - will not start new thread") #removed the above as it fills the log without any useful information self.sequenceStarted = False return else: logger.info("starting getCurrent Thread") self.getCurrentThread = SocketThread() self.getCurrentThread.snakeReference = self # for calling functions of the snake self.getCurrentThread.start() def getCurrentBlocking(self): """calls getCurrent and won't return until XML parsed. unlike above threaded function This is useful when starting up the snake so that we don't start looking for hardware events until a sequence has started and we have received XML""" self.mainLog.addLine("Waiting for next sequence to start") self.xmlString = self.connection.getCurrent( ) # only returns at the beginning of a sequence! Experiment runner then returns the entire XML file logger.debug("length of xml string = %s " % len(self.xmlString)) logger.debug("end of xml file is like [-30:]= %s" % self.xmlString[-30:]) try: root = ET.fromstring(self.xmlString) variables = root.find("variables") self.variables = { child[0].text: float(child[1].text) for child in variables } #timing edges dictionary : name--> value self.timingEdges = { timingEdge.find("name").text: float(timingEdge.find("value").text) for timingEdge in root.find("timing") } self.newSequenceStarted() except ET.ParseError as e: self.mainLog.addLine("Error. Could not parse XML: %s" % e.message, 3) self.mainLog.addLine( "Possible cause is that buffer is full. is XML length %s>= limit %s ????" % (len(self.xmlString), self.connection.BUFFER_SIZE_XML), 3) logger.error("could not parse XML: %s " % self.xmlString) logger.error(e.message) def updateStatusString(self): """update the status string with first element of return of GETSTATUS. similiar to experiment control and camera control. It also does the analysis of progress that doesn't need to be as accurate (e.g. progress bar)""" logger.info("starting update status string") self.statusString = self.statusList[ 0] + "- Time Running = %s " % self.timeRunning self.queue = int(self.statusList[1]) timeFormat = '%d/%m/%Y %H:%M:%S' timeBegin = datetime.datetime.strptime(self.statusList[2], timeFormat) timeEnd = datetime.datetime.strptime(self.statusList[4], timeFormat) self.timeTotal = (timeEnd - timeBegin).total_seconds() if self.timeRunning > self.timeTotal: self.haltedCount += 1 self.runnerHalted = True if self.haltedCount == 0: logger.critical("runner was stopped.") self.mainLog.addLine("Runner stopped!", 3) self.closeHardwareActions() else: if self.haltedCount > 0: self.initialiseHardwareActions() self.haltedCount = 0 self.runnerHalted = False self.progress = 100.0 * self.timeRunning / self.timeTotal def _examineVariablesDictionary_default(self): if len(self.hardwareActions) > 0: logger.debug( "returning first hardware action %s for examineVariablesDictionary default" % self.hardwareActions[0].hardwareActionName) return variableDictionary.ExamineVariablesDictionary( hdwA=self.hardwareActions[0] ) #default is the first in the list else: logger.warning( "hardwareActions list was empty. how should I populate variable examiner...?!." ) return None def updateExamineVariablesDictionary(self, hdwA): """Populates the examineVariablesDictionary Pane appropriately. It is passed the hdwA so that it can find the necessary variables""" self.examineVariablesDictionary.hdwA = hdwA self.examineVariablesDictionary.hardwareActionName = hdwA.hardwareActionName self.examineVariablesDictionary.updateDisplayList() logger.critical("examineVariablesDictionary changed") def refreshExamineVariablesDictionary(self): """calls the updateDisplayList function of examineVariables Dictionary this updates the values in the display list to the latest values in variables dictionary. useful for refereshing at the beginning of a sequence""" self.examineVariablesDictionary.updateDisplayList() logger.info("refreshed examine variables dictionary") def refreshVariableDependentCallbackTimes(self): """if a HWA is variable dependent call back time, we refresh the value using this function. THis should be called in each sequence""" [ hdwA.parseCallbackTime() for hdwA in self.hardwareActions if hdwA.callbackTimeVariableDependent ] def _changeLoggingLevelDebug(self): """Menu action function to change logger level """ logger.setLevel(logging.DEBUG) def _changeLoggingLevelInfo(self): """Menu action function to change logger level """ logger.setLevel(logging.INFO) def _changeLoggingLevelWarning(self): """Menu action function to change logger level """ logger.setLevel(logging.WARNING) def _changeLoggingLevelError(self): """Menu action function to change logger level """ logger.setLevel(logging.ERROR)