Esempio n. 1
0
class AnalysisDefinitionElement(object):
    def __init__(self):
        self.name = ''
        self.enabled = True
        self.evaluation = None
        self.fitfunctionName = None
        self.pushVariables = SequenceDict()
        self.fitfunction = None
        self.fitfunctionCache = dict()

    def __setstate__(self, state):
        self.__dict__ = state
        self.__dict__.setdefault('name', '')

    stateFields = [
        'name', 'enabled', 'evaluation', 'fitfunctionName', 'pushVariables',
        'fitfunction', 'fitfunctionCache'
    ]

    def __eq__(self, other):
        return isinstance(other, self.__class__) and tuple(
            getattr(self, field) for field in self.stateFields) == tuple(
                getattr(other, field) for field in self.stateFields)

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        return hash(tuple(getattr(self, field) for field in self.stateFields))

    def pushVariableValues(self):
        """get all push variable values that are within the bounds, no re-evaluation"""
        pushVarValues = list()
        failedList = []
        for pushvar in list(self.pushVariables.values()):
            pushRecord, failedRecord = pushvar.pushRecord()
            failedList.extend(failedRecord)
            pushVarValues.extend(pushRecord)
        return pushVarValues, failedList

    def updatePushVariables(self, replacements):
        for pushvar in self.pushVariables.values():
            pushvar.evaluate(replacements)
Esempio n. 2
0
class AnalysisDefinitionElement(object):
    def __init__(self):
        self.name = ''
        self.enabled = True
        self.evaluation = None
        self.fitfunctionName = None
        self.pushVariables = SequenceDict()
        self.fitfunction = None
        self.fitfunctionCache = dict()
        
    def __setstate__(self, state):
        self.__dict__ = state
        self.__dict__.setdefault('name', '')
        
    stateFields = ['name', 'enabled', 'evaluation', 'fitfunctionName', 'pushVariables', 'fitfunction', 'fitfunctionCache'] 
        
    def __eq__(self, other):
        return isinstance(other, self.__class__) and tuple(getattr(self, field) for field in self.stateFields)==tuple(getattr(other, field) for field in self.stateFields)

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        return hash(tuple(getattr(self, field) for field in self.stateFields))
    
    def pushVariableValues(self):
        """get all push variable values that are within the bounds, no re-evaluation"""
        pushVarValues = list()
        failedList = []
        for pushvar in list(self.pushVariables.values()):
            pushRecord, failedRecord = pushvar.pushRecord()
            failedList.extend( failedRecord )
            pushVarValues.extend( pushRecord )
        return pushVarValues, failedList
    
    def updatePushVariables(self, replacements ):
        for pushvar in self.pushVariables.values():
            pushvar.evaluate( replacements )
class VoltageGlobalAdjust(VoltageGlobalAdjustForm, VoltageGlobalAdjustBase):
    updateOutput = QtCore.pyqtSignal(object, object)
    outputChannelsChanged = QtCore.pyqtSignal(object)
    _channelParams = {}

    def __init__(self, config, globalDict, parent=None):
        VoltageGlobalAdjustForm.__init__(self)
        VoltageGlobalAdjustBase.__init__(self, parent)
        self.config = config
        self.configname = 'VoltageGlobalAdjust.Settings'
        self.settings = self.config.get(self.configname, Settings())
        self.globalAdjustDict = SequenceDict()
        self.myLabelList = list()
        self.myBoxList = list()
        self.historyCategory = 'VoltageGlobalAdjust'
        self.adjustHistoryName = None
        self.globalDict = globalDict
        self.adjustCache = self.config.get(self.configname + ".cache", dict())
        self.savedValue = defaultdict(lambda: None)
        self.displayValueObservable = defaultdict(lambda: Observable())

    def setupUi(self, parent):
        VoltageGlobalAdjustForm.setupUi(self, parent)
        self.gainBox.setValue(self.settings.gain)
        self.gainBox.valueChanged.connect(self.onGainChanged)
        self.tableModel = VoltageGlobalAdjustTableModel(
            self.globalAdjustDict, self.globalDict)
        self.tableView.setModel(self.tableModel)
        self.tableView.setSortingEnabled(True)  # triggers sorting
        self.delegate = MagnitudeSpinBoxDelegate(self.globalDict)
        self.tableView.setItemDelegateForColumn(1, self.delegate)

    def onGainChanged(self, gain):
        self.settings.gain = gain
        self.updateOutput.emit(self.globalAdjustDict, self.settings.gain)

    def setupGlobalAdjust(self, name, adjustDict):
        if name != self.adjustHistoryName:
            self.adjustCache[self.adjustHistoryName] = [
                v.data for v in list(self.globalAdjustDict.values())
            ]
            self.settings.gainCache[
                self.adjustHistoryName] = self.settings.gain
            self.settings.gain = self.settings.gainCache.get(
                name, self.settings.gain)
            if name in self.adjustCache:
                for data in self.adjustCache[name]:
                    if data[0] in adjustDict:
                        adjustDict[data[0]].data = data
            self.adjustHistoryName = name
        self.globalAdjustDict = adjustDict
        for name, adjust in self.globalAdjustDict.items():
            try:
                adjust.valueChanged.connect(self.onValueChanged,
                                            QtCore.Qt.UniqueConnection)
            except:
                pass
        self.tableModel.setGlobalAdjust(adjustDict)
        self.outputChannelsChanged.emit(self.outputChannels())
        self.gainBox.setValue(self.settings.gain)
        #self.updateOutput.emit(self.globalAdjustDict, self.settings.gain)

    def onValueChanged(self, name, value, string, origin):
        if origin == 'recalculate':
            self.tableModel.valueRecalcualted(name)
        self.globalAdjustDict[name]._value = float(
            self.globalAdjustDict[name]._value)
        self.updateOutput.emit(self.globalAdjustDict, self.settings.gain)

    def saveConfig(self):
        self.config[self.configname] = self.settings
        self.adjustCache[self.adjustHistoryName] = [
            v.data for v in list(self.globalAdjustDict.values())
        ]
        self.config[self.configname + ".cache"] = self.adjustCache

    def setValue(self, channel, value):
        self.globalAdjustDict[channel].value = value
        return value

    def getValue(self, channel):
        return self.globalAdjustDict[channel].value

    def currentValue(self, channel):
        return self.globalAdjustDict[channel].value

    def saveValue(self, channel):
        self.savedValue[channel] = self.globalAdjustDict[channel].value

    def restoreValue(self, channel):
        if self.savedValue[channel] is not None:
            self.globalAdjustDict[channel].value = self.savedValue[channel]
        return True

    def strValue(self, channel):
        adjust = self.globalAdjustDict[channel]
        return adjust.string if adjust.hasDependency else None

    def setStrValue(self, channel, value):
        pass

    def outputChannels(self):
        self._outputChannels = dict(
            ((channelName,
              VoltageOutputChannel(
                  self,
                  None,
                  channelName,
                  self.globalDict,
              )) for channelName in self.globalAdjustDict.keys()))
        return self._outputChannels
Esempio n. 4
0
class VoltageBlender(QtCore.QObject):
    dataChanged = QtCore.pyqtSignal(int, int, int, int)
    dataError = QtCore.pyqtSignal(object)
    shuttlingOnLine = QtCore.pyqtSignal(float)
    
    def __init__(self, globalDict, dacController):
        logger = logging.getLogger(__name__)
        super(VoltageBlender, self).__init__()
        self.dacController = dacController

        if hardwareObjName==FPGAObjName and hardwareDict:
            self.hardware = FPGAHardware(self.dacController)
        elif hardwareObjName==NIObjName and hardwareDict:
            self.hardware = NIHardware()
        else:
            self.hardware = NoneHardware()

        self.itf = itfParser()
        self.lines = list()  # a list of lines with numpy arrays
        self.adjustDict = SequenceDict()  # names of the lines presented as possible adjusts
        self.adjustLines = []
        self.lineGain = 1.0
        self.globalGain = 1.0
        self.lineno = 0
        self.mappingpath = None
        self.outputVoltage = None
        self.electrodes = None
        self.aoNums = None
        self.dsubNums = None
        self.tableHeader = list()
        self.globalDict = globalDict
        self.adjustGain = 1.0
        self.localAdjustVoltages = list()
        self.uploadedDataHash = None
        
    def currentData(self):
        return self.electrodes, self.aoNums, self.dsubNums, self.outputVoltage
    
    def loadMapping(self, path):
        path = project.findFile(path)
        self.itf.eMapFilePath = path
        self.mappingpath = path
        self.electrodes, self.aoNums, self.dsubNums = self.itf._getEmapData()
        self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3)
    
    def loadVoltage(self, path):
        channelCount = self.hardware.channelCount if self.hardware else 0
        self.itf.open(path)
        self.lines = list()
        for _ in range(self.itf.getNumLines()):
            line = self.itf.eMapReadLine() 
            for index, value in enumerate(line):
                if math.isnan(value): line[index]=0
            line = numpy.append( line, [0.0]*max(0, channelCount-len(line)))
            self.lines.append( line )
        self.tableHeader = self.itf.tableHeader
        self.itf.close()
        self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3)

    def loadGlobalAdjust(self, path):
        channelCount = self.hardware.channelCount if self.hardware else 0
        self.adjustLines = list()
        self.adjustDict = SequenceDict()
        itf = itfParser()
        itf.eMapFilePath = self.mappingpath
        itf.open(path)
        for _ in range(itf.getNumLines()):
            line = itf.eMapReadLine() 
            for index, value in enumerate(line):
                if math.isnan(value): line[index]=0
            line = numpy.append( line, [0.0]*max(0, channelCount-len(line)))
            self.adjustLines.append( line )
        for name, value in itf.meta.items():
            try:
                if int(value)<len(self.adjustLines):
                    self.adjustDict[name] = AdjustValue(name=name, line=int(value), globalDict=self.globalDict)
            except ValueError:
                pass   # if it's not an int we will ignore it here
        itf.close()
        self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3)
        
    def loadLocalAdjust(self, localAdjustList, forceupdate=list() ):
        channelCount = self.hardware.channelCount if self.hardware else 0        
        for index, record in enumerate(localAdjustList):
            path = record.path
            if index in forceupdate or record.solutionPath != record.path:
                if os.path.exists(path):
                    linelist = list()
                    itf = itfParser()
                    itf.eMapFilePath = self.mappingpath
                    itf.open(path)
                    for _ in range(itf.getNumLines()):
                        line = itf.eMapReadLine() 
                        for index, value in enumerate(line):
                            if math.isnan(value): line[index]=0
                        line = numpy.append( line, [0.0]*max(0, channelCount-len(line)))
                        linelist.append( line )
                    itf.close()
                    record.solution = linelist
                    record.solutionPath = path
                else:
                    logging.getLogger(__name__).warning("Local Adjust file '{0}' not found".format(path))
        if self.electrodes:
            self.dataChanged.emit(0, 0, len(self.electrodes)-1, 3)
        self.localAdjustVoltages = localAdjustList
    
    def setAdjust(self, adjust, gain):
        self.adjustDict = adjust
        self.adjustGain = float(gain)
        self.applyLine(self.lineno, self.lineGain, self.globalGain)
        
    def setLocalAdjust(self, localAdjustDict ):
        self.localAdjustVoltages = localAdjustDict
        self.applyLine(self.lineno, self.lineGain, self.globalGain)
    
    def applyLine(self, lineno, lineGain, globalGain, updateHardware=True ):
        if updateHardware:
            line = self.calculateLine(float(lineno), float(lineGain), float(globalGain))
            try:
                self.hardware.applyLine(line)
                self.outputVoltage = line
                self.lineno = lineno
                self.dataChanged.emit(0, 1, len(self.electrodes)-1, 1)
            except (HardwareException, DACControllerException) as e:
                logging.getLogger(__name__).exception("Exception")
                outOfRange = line>10
                outOfRange |= line<-10
                self.dataError.emit(outOfRange.tolist())
        else:
            self.lineno = lineno
            
    def calculateLine(self, lineno, lineGain, globalGain):
        line = self.blendLines(lineno, lineGain)
        localadjustline = self.blendLocalAdjustLines(lineno)
        self.lineGain = lineGain
        self.globalGain = globalGain
        #self.lineno = lineno
        line = self.adjustLine( line )
        if len(localadjustline) > 0:
            line = numpy.add( line, localadjustline )
        line *= self.globalGain
        return line
            
    def shuttle(self, definition, cont):
        logger = logging.getLogger(__name__)
        if not self.hardware.nativeShuttling:
            for start, _, edge, _ in definition:
                fromLine, toLine = (edge.startLine, edge.stopLine) if start==edge.startName else (edge.stopLine, edge.startLine)
                for line in numpy.linspace(fromLine, toLine, edge.steps+2, True):
                    self.applyLine(line, self.lineGain, self.globalGain)
                    logger.debug( "shuttling applied line {0}".format( line ) )
            self.shuttlingOnLine.emit(line)
        else:  # this stuff does not work yet
            if definition:
                logger.info( "Starting finite shuttling" )
                globaladjust = [0]*len(self.lines[0])
                self.adjustLine(globaladjust)
                self.hardware.shuttlePath( [(index, start!=edge.startName, True) for start, _, edge, index in definition] )
                start, _, edge, _ = definition[-1]
                self.shuttleTo = edge.startLine if start!=edge.startName else edge.stopLine
                self.shuttlingOnLine.emit(self.shuttleTo)
                self.outputVoltage = line = self.calculateLine( float(self.shuttleTo), float(self.lineGain), float(self.globalGain) )
                self.dataChanged.emit(0, 1, len(self.electrodes)-1, 1)
                        
    def adjustLine(self, line):
        offset = numpy.zeros(len(line))
        for adjust in self.adjustDict.values():
            offset += self.adjustLines[adjust.line] * float(adjust.floatValue)
        offset *= self.adjustGain
        return (line+offset)
            
    def blendLines(self, lineno, lineGain):
        if self.lines:
            left = int(math.floor(lineno))
            right = int(math.ceil(lineno))
            convexc = lineno-left
            return (self.lines[left]*(1-convexc) + self.lines[right]*convexc)*lineGain
        return None
    
    def blendLocalAdjustLines(self, lineno):
        channelCount = self.hardware.channelCount if self.hardware else 0
        left = int(math.floor(lineno))
        right = int(math.ceil(lineno))
        convexc = lineno-left
        result = numpy.zeros(channelCount)
        for record in self.localAdjustVoltages:
            if record.solution:
                if isfunction(record.gain.value):
                    result = numpy.add(result, (record.solution[left]*(1-convexc)*record.gain.value(left) + record.solution[right]*convexc*record.gain.value(right)))
                else:
                    result = numpy.add(result, (record.solution[left]*(1-convexc) + record.solution[right]*convexc)*record.gainValue)
        return result
            
    def close(self):
        self.hardware.close()

    def writeShuttleLookup(self, edgeList, address=0):
        if(self.dacController.isOpen):
            self.dacController.writeShuttleLookup(edgeList, address)
    
    def writeData(self, shuttlingGraph):
        towrite = list()
        startline = 1
        currentline = startline
        lineGain = float(self.lineGain)
        globalGain = float(self.globalGain)
        if shuttlingGraph:
            for edge in shuttlingGraph:
                towrite.extend( [self.calculateLine(lineno, lineGain, globalGain ) for lineno in edge.iLines() ] )
                edge.interpolStartLine = currentline
                currentline = startline+len(towrite)
                edge.interpolStopLine = currentline
            data = self.dacController.writeVoltages(1, towrite )
            self.dacController.verifyVoltages(1, data )
            self.uploadedDataHash = self.shuttlingDataHash()

    stateFields = ('lineGain', 'globalGain', 'adjustGain')
    def shuttlingDataHash(self):
        h = hash(itertools.chain((getattr(self, field).to_tuple() for field in self.stateFields),
                                 (q.to_tuple() for q in self.adjustDict.values()),
                                 (q.to_tuple() for q in self.localAdjustVoltages)))
        logging.getLogger(__name__).info("Shuttling Hash: {0:x}".format(h))
        return h
    
    def shuttlingDataValid(self):
        return self.shuttlingDataHash()==self.uploadedDataHash
        
    def trigger(self):
        self.dacController.triggerShuttling()
Esempio n. 5
0
class VoltageBlender(QtCore.QObject):
    dataChanged = QtCore.pyqtSignal(int, int, int, int)
    dataError = QtCore.pyqtSignal(object)
    shuttlingOnLine = QtCore.pyqtSignal(float)

    def __init__(self, globalDict, dacController):
        logger = logging.getLogger(__name__)
        super(VoltageBlender, self).__init__()
        self.dacController = dacController

        if hardwareObjName == FPGAObjName and hardwareDict:
            self.hardware = FPGAHardware(self.dacController)
        elif hardwareObjName == NIObjName and hardwareDict:
            self.hardware = NIHardware()
        else:
            self.hardware = NoneHardware()

        self.itf = itfParser()
        self.lines = list()  # a list of lines with numpy arrays
        self.adjustDict = SequenceDict(
        )  # names of the lines presented as possible adjusts
        self.adjustLines = []
        self.lineGain = 1.0
        self.globalGain = 1.0
        self.lineno = 0
        self.mappingpath = None
        self.outputVoltage = None
        self.electrodes = None
        self.aoNums = None
        self.dsubNums = None
        self.tableHeader = list()
        self.globalDict = globalDict
        self.adjustGain = 1.0
        self.localAdjustVoltages = list()
        self.uploadedDataHash = None

    def currentData(self):
        return self.electrodes, self.aoNums, self.dsubNums, self.outputVoltage

    def loadMapping(self, path):
        path = project.findFile(path)
        self.itf.eMapFilePath = path
        self.mappingpath = path
        self.electrodes, self.aoNums, self.dsubNums = self.itf._getEmapData()
        self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3)

    def loadVoltage(self, path):
        channelCount = self.hardware.channelCount if self.hardware else 0
        self.itf.open(path)
        self.lines = list()
        for _ in range(self.itf.getNumLines()):
            line = self.itf.eMapReadLine()
            for index, value in enumerate(line):
                if math.isnan(value): line[index] = 0
            line = numpy.append(line, [0.0] * max(0, channelCount - len(line)))
            self.lines.append(line)
        self.tableHeader = self.itf.tableHeader
        self.itf.close()
        self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3)

    def loadGlobalAdjust(self, path):
        channelCount = self.hardware.channelCount if self.hardware else 0
        self.adjustLines = list()
        self.adjustDict = SequenceDict()
        itf = itfParser()
        itf.eMapFilePath = self.mappingpath
        itf.open(path)
        for _ in range(itf.getNumLines()):
            line = itf.eMapReadLine()
            for index, value in enumerate(line):
                if math.isnan(value): line[index] = 0
            line = numpy.append(line, [0.0] * max(0, channelCount - len(line)))
            self.adjustLines.append(line)
        for name, value in itf.meta.items():
            try:
                if int(value) < len(self.adjustLines):
                    self.adjustDict[name] = AdjustValue(
                        name=name, line=int(value), globalDict=self.globalDict)
            except ValueError:
                pass  # if it's not an int we will ignore it here
        itf.close()
        self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3)

    def loadLocalAdjust(self, localAdjustList, forceupdate=list()):
        channelCount = self.hardware.channelCount if self.hardware else 0
        for index, record in enumerate(localAdjustList):
            path = record.path
            if index in forceupdate or record.solutionPath != record.path:
                if os.path.exists(path):
                    linelist = list()
                    itf = itfParser()
                    itf.eMapFilePath = self.mappingpath
                    itf.open(path)
                    for _ in range(itf.getNumLines()):
                        line = itf.eMapReadLine()
                        for index, value in enumerate(line):
                            if math.isnan(value): line[index] = 0
                        line = numpy.append(line, [0.0] *
                                            max(0, channelCount - len(line)))
                        linelist.append(line)
                    itf.close()
                    record.solution = linelist
                    record.solutionPath = path
                else:
                    logging.getLogger(__name__).warning(
                        "Local Adjust file '{0}' not found".format(path))
        if self.electrodes:
            self.dataChanged.emit(0, 0, len(self.electrodes) - 1, 3)
        self.localAdjustVoltages = localAdjustList

    def setAdjust(self, adjust, gain):
        self.adjustDict = adjust
        self.adjustGain = float(gain)
        self.applyLine(self.lineno, self.lineGain, self.globalGain)

    def setLocalAdjust(self, localAdjustDict):
        self.localAdjustVoltages = localAdjustDict
        self.applyLine(self.lineno, self.lineGain, self.globalGain)

    def applyLine(self, lineno, lineGain, globalGain, updateHardware=True):
        if updateHardware:
            line = self.calculateLine(float(lineno), float(lineGain),
                                      float(globalGain))
            try:
                self.hardware.applyLine(line)
                self.outputVoltage = line
                self.lineno = lineno
                self.dataChanged.emit(0, 1, len(self.electrodes) - 1, 1)
            except (HardwareException, DACControllerException) as e:
                logging.getLogger(__name__).exception("Exception")
                outOfRange = line > 10
                outOfRange |= line < -10
                self.dataError.emit(outOfRange.tolist())
        else:
            self.lineno = lineno

    def calculateLine(self, lineno, lineGain, globalGain):
        line = self.blendLines(lineno, lineGain)
        localadjustline = self.blendLocalAdjustLines(lineno)
        self.lineGain = lineGain
        self.globalGain = globalGain
        #self.lineno = lineno
        line = self.adjustLine(line)
        if len(localadjustline) > 0:
            line = numpy.add(line, localadjustline)
        line *= self.globalGain
        return line

    def shuttle(self, definition, cont):
        logger = logging.getLogger(__name__)
        if not self.hardware.nativeShuttling:
            for start, _, edge, _ in definition:
                fromLine, toLine = (
                    edge.startLine,
                    edge.stopLine) if start == edge.startName else (
                        edge.stopLine, edge.startLine)
                for line in numpy.linspace(fromLine, toLine, edge.steps + 2,
                                           True):
                    self.applyLine(line, self.lineGain, self.globalGain)
                    logger.debug("shuttling applied line {0}".format(line))
            self.shuttlingOnLine.emit(line)
        else:  # this stuff does not work yet
            if definition:
                logger.info("Starting finite shuttling")
                globaladjust = [0] * len(self.lines[0])
                self.adjustLine(globaladjust)
                self.hardware.shuttlePath([
                    (index, start != edge.startName, True)
                    for start, _, edge, index in definition
                ])
                start, _, edge, _ = definition[-1]
                self.shuttleTo = edge.startLine if start != edge.startName else edge.stopLine
                self.shuttlingOnLine.emit(self.shuttleTo)
                self.outputVoltage = line = self.calculateLine(
                    float(self.shuttleTo), float(self.lineGain),
                    float(self.globalGain))
                self.dataChanged.emit(0, 1, len(self.electrodes) - 1, 1)

    def adjustLine(self, line):
        offset = numpy.zeros(len(line))
        for adjust in self.adjustDict.values():
            offset += self.adjustLines[adjust.line] * float(adjust.floatValue)
        offset *= self.adjustGain
        return (line + offset)

    def blendLines(self, lineno, lineGain):
        if self.lines:
            left = int(math.floor(lineno))
            right = int(math.ceil(lineno))
            convexc = lineno - left
            return (self.lines[left] *
                    (1 - convexc) + self.lines[right] * convexc) * lineGain
        return None

    def blendLocalAdjustLines(self, lineno):
        channelCount = self.hardware.channelCount if self.hardware else 0
        left = int(math.floor(lineno))
        right = int(math.ceil(lineno))
        convexc = lineno - left
        result = numpy.zeros(channelCount)
        for record in self.localAdjustVoltages:
            if record.solution:
                if isfunction(record.gain.data[1]):
                    result = numpy.add(
                        result, (record.solution[left] *
                                 (1 - convexc) * record.gainValue(left) +
                                 record.solution[right] * convexc *
                                 record.gainValue(right)))
                else:
                    result = numpy.add(
                        result,
                        (record.solution[left] *
                         (1 - convexc) + record.solution[right] * convexc) *
                        record.gainValue)
        return result

    def close(self):
        self.hardware.close()

    def writeShuttleLookup(self, edgeList, address=0):
        if (self.dacController.isOpen):
            self.dacController.writeShuttleLookup(edgeList, address)

    def writeData(self, shuttlingGraph):
        towrite = list()
        startline = 1
        currentline = startline
        lineGain = float(self.lineGain)
        globalGain = float(self.globalGain)
        if shuttlingGraph:
            for edge in shuttlingGraph:
                towrite.extend([
                    self.calculateLine(lineno, lineGain, globalGain)
                    for lineno in edge.iLines()
                ])
                edge.interpolStartLine = currentline
                currentline = startline + len(towrite)
                edge.interpolStopLine = currentline
            data = self.dacController.writeVoltages(1, towrite)
            self.dacController.verifyVoltages(1, data)
            self.uploadedDataHash = self.shuttlingDataHash()

    stateFields = ('lineGain', 'globalGain', 'adjustGain')

    def shuttlingDataHash(self):
        h = hash(
            itertools.chain((getattr(self, field).to_tuple()
                             for field in self.stateFields),
                            (q.to_tuple() for q in self.adjustDict.values()),
                            (q.to_tuple() for q in self.localAdjustVoltages)))
        logging.getLogger(__name__).info("Shuttling Hash: {0:x}".format(h))
        return h

    def shuttlingDataValid(self):
        return self.shuttlingDataHash() == self.uploadedDataHash

    def trigger(self):
        self.dacController.triggerShuttling()
Esempio n. 6
0
class ExperimentUi(WidgetContainerBase,WidgetContainerForm):
    levelNameList = ["debug", "info", "warning", "error", "critical"]
    levelValueList = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL]

    def __init__(self, config, project):
        self.config = config
        self.project = project
        super(ExperimentUi, self).__init__()
        self.settings = FPGASettings()
        self.loggingLevel = config.get('Settings.loggingLevel',logging.INFO)
        self.consoleMaximumLines = config.get('Settings.consoleMaximumLinesNew',100)
        self.consoleEnable = config.get('Settings.consoleEnable',False)
        self.shutterNameDict = config.get('Settings.ShutterNameDict', ChannelNameDict())
        if self.shutterNameDict.__class__.__name__ == 'ChannelNameMap':
            self.shutterNameDict = ChannelNameDict( self.shutterNameDict.names )
        if self.shutterNameDict.customDict.__class__.__name__ == 'ChannelNameMap':
            self.shutterNameDict = ChannelNameDict(self.shutterNameDict.customDict._fwd, self.shutterNameDict.defaultDict )

        self.shutterNameSignal = DataChanged()
        self.triggerNameDict = config.get('Settings.TriggerNameDict', ChannelNameDict())
        if self.triggerNameDict.__class__.__name__ == 'ChannelNameMap':
            self.triggerNameDict = ChannelNameDict( self.triggerNameDict.names )
        self.triggerNameSignal = DataChanged()
        if self.loggingLevel not in self.levelValueList: self.loggingLevel = logging.INFO
        self.dbConnection = project.dbConnection
        self.objectListToSaveContext = list()
        self.voltageControlWindow = None

        localpath = getProject().configDir+'/UserFunctions/'
        for filename in Path(localpath.replace('\\','/')).glob('**/*.py'):
            try:
                importlib.machinery.SourceFileLoader("CustomFunctions", str(filename).replace('\\','/')).load_module()
            except SyntaxError as e:
                SyntaxError('Failed to load {0}'.format(str(filename)))

    def __enter__(self):
        self.pulser = PulserHardware()
        return self
    
    def __exit__(self, excepttype, value, traceback):
        self.pulser.shutdown()
        return False
    
    def setupUi(self, parent):
        super(ExperimentUi,self).setupUi(parent)
        self.dockWidgetConsole.hide()
        self.loggerUi = LoggerLevelsUi(self.config)
        self.loggerUi.setupUi(self.loggerUi)
        self.loggerDock = QtWidgets.QDockWidget("Logging")
        self.loggerDock.setWidget(self.loggerUi)
        self.loggerDock.setObjectName("_LoggerDock")
        self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.loggerDock)
        self.loggerDock.hide()

        logger = logging.getLogger()
        self.exceptionToolBar.addWidget(ExceptionLogButton())

        self.warningLogButton = LogButton(messageIcon=":/petersIcons/icons/Warning.png", messageName="warnings")
        self.exceptionToolBar.addWidget(self.warningLogButton)
        qtWarningButtonHandler.textWritten.connect(self.warningLogButton.addMessage)

        # Setup Console Dockwidget
        self.levelComboBox.addItems(self.levelNameList)
        self.levelComboBox.currentIndexChanged[int].connect( self.setLoggingLevel )
        self.levelComboBox.setCurrentIndex( self.levelValueList.index(self.loggingLevel) )
        self.consoleClearButton.clicked.connect( self.onClearConsole )
        self.linesSpinBox.valueChanged.connect( self.onConsoleMaximumLinesChanged )
        self.linesSpinBox.setValue( self.consoleMaximumLines )
        self.checkBoxEnableConsole.stateChanged.connect( self.onEnableConsole )
        self.checkBoxEnableConsole.setChecked( self.consoleEnable )

        self.parent = parent
        self.tabDict = SequenceDict()

        if self.project.isEnabled('software', 'Memory Profiler'):
            self.memoryProfiler = MemoryProfiler(self)

        #determine if Voltages software is enabled and import class if it is
        self.voltagesEnabled = self.project.isEnabled('software', 'Voltages')
        if self.voltagesEnabled:
            from voltageControl.VoltageControl import VoltageControl

        #setup external parameters; import specific libraries if they are needed, popup warnings if selected hardware import fail
        import externalParameter.StandardExternalParameter
        import externalParameter.InterProcessParameters
        if self.project.isEnabled('hardware', 'Conex Motion'):
            try:
                import externalParameter.MotionParameter #@UnusedImport
            except ImportError: #popup on failed import
                importErrorPopup('Conex Motion')
        if self.project.isEnabled('hardware', 'APT Motion'):
            try:
                import externalParameter.APTInstruments  # @UnusedImport
                externalParameter.APTInstruments.loadDll(
                    list(self.project.hardware['APT Motion'].values())[0]['dllPath'])
            except Exception as e:  # popup on failed import
                importErrorPopup('APT Motion error {0}'.format(e))
        if self.project.isEnabled('hardware', 'Lab Brick'):
            try:
                import externalParameter.LabBrick  # @UnusedImport
                externalParameter.LabBrick.loadDll(
                    list(self.project.hardware['Lab Brick'].values())[0]['dllPath'])
            except Exception as e:  # popup on failed import
                importErrorPopup('Lab Brick error {0}'.format(e))
        from externalParameter.ExternalParameterBase import InstrumentDict

        # setup FPGAs
        self.setupFPGAs()

        # initialize PulseProgramUi
        pulserConfig = self.pulser.pulserConfiguration()
        self.shutterNameDict.defaultDict = pulserConfig.shutterBits if pulserConfig else dict()
        self.triggerNameDict.defaultDict = pulserConfig.triggerBits if pulserConfig else dict()
        self.counterNameDict = pulserConfig.counterBits if pulserConfig else dict()
        self.channelNameData = (self.shutterNameDict, self.shutterNameSignal, self.triggerNameDict, self.triggerNameSignal, self.counterNameDict )
        self.pulseProgramDialog = PulseProgramUi.PulseProgramSetUi(self.config,  self.channelNameData )
        self.pulseProgramDialog.setupUi(self.pulseProgramDialog)

        # Global Variables
        self.globalVariablesUi = GlobalVariablesUi(self.config)
        self.globalVariablesUi.setupUi(self.globalVariablesUi)
        self.globalVariablesDock = QtWidgets.QDockWidget("Global Variables")
        self.globalVariablesDock.setObjectName("Global Variables")
        self.globalVariablesDock.setWidget( self.globalVariablesUi )
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea , self.globalVariablesDock)

        self.measurementLog = MeasurementLogUi(self.config, self.dbConnection)
        self.measurementLog.setupUi(self.measurementLog)
        #self.measurementLogDock = QtWidgets.QDockWidget("Measurement Log")
        #self.measurementLogDock.setWidget( self.measurementLog )
        #self.measurementLogDock.setObjectName('_MeasurementLog')
        #self.addDockWidget( QtCore.Qt.BottomDockWidgetArea, self.measurementLogDock )

        self.preferencesUi = PreferencesUi(config, self)
        self.preferencesUi.setupUi(self.preferencesUi)
        self.preferencesUiDock = QtGui.QDockWidget("Print Preferences")
        self.preferencesUiDock.setWidget(self.preferencesUi)
        self.preferencesUiDock.setObjectName("_preferencesUi")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.preferencesUiDock)

        for widget, name in [(ScanExperiment.ScanExperiment(self.settings, self.pulser, self.globalVariablesUi,
                                                            "ScanExperiment", toolBar=self.experimentToolBar,
                                                            measurementLog=self.measurementLog,
                                                            callWhenDoneAdjusting=self.callWhenDoneAdjusting,
                                                            preferences=self.preferencesUi.preferences().printPreferences),
                              "Scan")
                             ]:
            widget.setupUi(widget, self.config)
            if hasattr(widget, 'setPulseProgramUi'):
                widget.setPulseProgramUi(self.pulseProgramDialog)
            if hasattr(widget, 'plotsChanged'):
                widget.plotsChanged.connect(self.initMenu)
            self.tabWidget.addTab(widget, name)
            self.tabDict[name] = widget
            widget.ClearStatusMessage.connect(self.statusbar.clearMessage)
            widget.StatusMessage.connect(self.statusbar.showMessage)
            widget.stashChanged.connect(self.onStashChanged)

        self.scanExperiment = self.tabDict["Scan"]

        self.shutterUi, self.shutterDockWidget = self.instantiateShutterUi(self.pulser, 'Shutters', "ShutterUi", self.config, self.globalVariablesUi.globalDict, self.shutterNameDict, self.shutterNameSignal)

        self.triggerUi = ShutterUi.TriggerUi(self.pulser, 'ShutterUi', 'trigger', self.config, (self.triggerNameDict, self.triggerNameSignal) )
        self.triggerUi.offColor =  QtGui.QColor(QtCore.Qt.white)
        self.triggerUi.setupUi(self.triggerUi)
        self.pulser.ppActiveChanged.connect( self.triggerUi.setDisabled )
        self.triggerDockWidget.setWidget( self.triggerUi )

        #AWGs
        enabledAWGDict = {displayName:className for displayName,className in AWGDevices.AWGDeviceDict.items()
                          if self.project.isEnabled('hardware', displayName)}
        self.AWGUiDict = dict()
        if enabledAWGDict:
            AWGIcon = QtGui.QIcon()
            AWGPixmap = QtGui.QPixmap(":/other/icons/AWG.png")
            AWGIcon.addPixmap(AWGPixmap)
            AWGButton = QtWidgets.QToolButton()
            AWGButton.setIcon(AWGIcon)
            self.toolBar.addWidget(AWGButton)
            if len(enabledAWGDict) > 1:
                menu = QtWidgets.QMenu("AWG")
                AWGButton.setMenu(menu)
                AWGButton.setPopupMode(QtWidgets.QToolButton.InstantPopup)
                menu.setIcon(AWGIcon)
                self.menuWindows.addMenu(menu)
            for displayName, className in enabledAWGDict.items():
                awgUi = AWGUi(getattr(AWGDevices, className), self.config, self.globalVariablesUi.globalDict, self.scanExperiment.pulseProgramUi)
                self.AWGUiDict[displayName] = awgUi
                awgUi.setupUi(awgUi)
                awgUi.varDictChanged.connect( partial(self.scanExperiment.updateScanTarget, displayName) )
                self.scanExperiment.updateScanTarget( displayName, awgUi.varAsOutputChannelDict )
                self.globalVariablesUi.valueChanged.connect( awgUi.evaluate )
                action = QtWidgets.QAction(AWGIcon, displayName, self)
                action.triggered.connect(partial(self.onAWG, displayName))
                if len(enabledAWGDict) > 1:
                    menu.addAction(action)
                else:
                    self.menuWindows.addAction(action)
                    AWGButton.clicked.connect(action.trigger)

        ParameterUi, self.pulserParameterUiDock = self.instantiateParametersUi(self.pulser, "Pulser Parameters", "PulserParameterUi", self.config, self.globalVariablesUi.globalDict)
        self.objectListToSaveContext.append(ParameterUi)

        self.DDSUi, self.DDSDockWidget = self.instantiateDDSUi(self.pulser, "DDS", "DDSUi", self.config, self.globalVariablesUi.globalDict)
        self.objectListToSaveContext.append(self.DDSUi)

        self.DACUi, self.DACDockWidget = self.instantiateDACUi(self.pulser, "DAC", "dacUi", self.config, self.globalVariablesUi.globalDict)
        self.objectListToSaveContext.append(self.DACUi)

#         self.DDSUi9910 = DDSUi9910.DDSUi(self.config, self.pulser )
#         self.DDSUi9910.setupUi(self.DDSUi9910)
#         self.DDS9910DockWidget.setWidget( self.DDSUi9910 )
#        self.pulser.ppActiveChanged.connect( self.DDSUi9910.setDisabled )
        #self.tabDict['Scan'].NeedsDDSRewrite.connect( self.DDSUi9910.onWriteAll )
        self.instantiateAuxiliaryPulsers()

        self.valueHistoryUi = ValueHistoryUi(self.config, self.dbConnection)
        self.valueHistoryUi.setupUi( self.valueHistoryUi )
        self.valueHistoryDock = QtWidgets.QDockWidget("Value History")
        self.valueHistoryDock.setWidget( self.valueHistoryUi )
        self.valueHistoryDock.setObjectName("_valueHistory")
        self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.valueHistoryDock )
        
        # tabify the dock widgets
        self.tabifyDockWidget( self.pulserParameterUiDock, self.preferencesUiDock)
        self.tabifyDockWidget( self.preferencesUiDock, self.triggerDockWidget )
        self.tabifyDockWidget( self.triggerDockWidget, self.shutterDockWidget)
        self.tabifyDockWidget( self.shutterDockWidget, self.DDSDockWidget )
        self.tabifyDockWidget( self.DDSDockWidget, self.DACDockWidget )
#        self.tabifyDockWidget( self.DDSDockWidget, self.DDS9910DockWidget )
#        self.tabifyDockWidget( self.DDS9910DockWidget, self.globalVariablesDock )
        self.tabifyDockWidget( self.DACDockWidget, self.globalVariablesDock )
        self.tabifyDockWidget( self.globalVariablesDock, self.valueHistoryDock )
        self.triggerDockWidget.hide()
        self.preferencesUiDock.hide()

        self.ExternalParametersSelectionUi = ExternalParameterSelection.SelectionUi(self.config, self.globalVariablesUi.globalDict, classdict=InstrumentDict)
        self.ExternalParametersSelectionUi.setupUi( self.ExternalParametersSelectionUi )
        self.ExternalParameterSelectionDock = QtWidgets.QDockWidget("Params Selection")
        self.ExternalParameterSelectionDock.setObjectName("_ExternalParameterSelectionDock")
        self.ExternalParameterSelectionDock.setWidget(self.ExternalParametersSelectionUi)
        self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.ExternalParameterSelectionDock)

        self.ExternalParametersUi = ExternalParameterUi.ControlUi(self.config, self.globalVariablesUi.globalDict)
        self.ExternalParametersUi.setupUi(self.ExternalParametersSelectionUi.outputChannels())

        self.ExternalParameterDock = QtWidgets.QDockWidget("Params Control")
        self.ExternalParameterDock.setWidget(self.ExternalParametersUi)
        self.ExternalParameterDock.setObjectName("_ExternalParameterDock")
        self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.ExternalParameterDock)
        self.ExternalParametersSelectionUi.outputChannelsChanged.connect( self.ExternalParametersUi.setupParameters )

        self.instrumentLoggingDisplay = InstrumentLoggingDisplay(self.config)
        self.instrumentLoggingDisplay.setupUi( self.ExternalParametersSelectionUi.inputChannels(), self.instrumentLoggingDisplay )
        self.instrumentLoggingDisplayDock = QtWidgets.QDockWidget("Params Reading")
        self.instrumentLoggingDisplayDock.setObjectName("_ExternalParameterDisplayDock")
        self.instrumentLoggingDisplayDock.setWidget(self.instrumentLoggingDisplay)
        self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.instrumentLoggingDisplayDock)
        self.ExternalParametersSelectionUi.inputChannelsChanged.connect( self.instrumentLoggingDisplay.setupParameters )
               
        self.ExternalParametersSelectionUi.outputChannelsChanged.connect( partial(self.scanExperiment.updateScanTarget, 'External') )               
        self.scanExperiment.updateScanTarget( 'External', self.ExternalParametersSelectionUi.outputChannels() )
        
        self.todoList = TodoList( self.tabDict, self.config, self.getCurrentTab, self.switchTab, self.globalVariablesUi )
        self.todoList.setupUi()
        self.todoListDock = QtWidgets.QDockWidget("Todo List")
        self.todoListDock.setWidget(self.todoList)
        self.todoListDock.setObjectName("_todoList")
        self.addDockWidget( QtCore.Qt.RightDockWidgetArea, self.todoListDock)
        self.tabifyDockWidget(self.valueHistoryDock, self.todoListDock)

        for name, widget in self.tabDict.items():
            if hasattr( widget, 'scanConfigurationListChanged' ) and widget.scanConfigurationListChanged is not None:
                widget.scanConfigurationListChanged.connect( partial( self.todoList.populateMeasurementsItem, name)  )
            if hasattr( widget, 'evaluationConfigurationChanged' ) and widget.evaluationConfigurationChanged is not None:
                widget.evaluationConfigurationChanged.connect( partial( self.todoList.populateEvaluationItem, name)  )
            if hasattr( widget, 'analysisConfigurationChanged' ) and widget.analysisConfigurationChanged is not None:
                widget.analysisConfigurationChanged.connect( partial( self.todoList.populateAnalysisItem, name)  )
       
        #tabify external parameters controls
        self.tabifyDockWidget(self.ExternalParameterSelectionDock, self.ExternalParameterDock)
        self.tabifyDockWidget(self.ExternalParameterDock, self.instrumentLoggingDisplayDock)
        
        self.tabWidget.currentChanged.connect(self.onCurrentChanged)
        self.actionClear.triggered.connect(self.onClear)
        self.actionPause.triggered.connect(self.onPause)

        #Save and load actions
        self.actionSave_GUI.triggered.connect(self.onSaveGUI)
        self.actionSave_GUI_Yaml.triggered.connect(self.onSaveGUIYaml)

        self.actionStart.triggered.connect(self.onStart)
        self.actionStop.triggered.connect(self.onStop)
        self.actionAbort.triggered.connect(self.onAbort)
        self.actionExit.triggered.connect(self.onClose)
        self.actionContinue.triggered.connect(self.onContinue)
        self.actionPulses.triggered.connect(self.onPulses)
        self.actionReload.triggered.connect(self.onReload)
        self.actionProject.triggered.connect( self.onProjectSelection)
        self.actionDocumentation.triggered.connect(self.onShowDocumentation)
        if self.voltagesEnabled:
            self.actionVoltageControl.triggered.connect(self.onVoltageControl)
        else:
            self.actionVoltageControl.setDisabled(True)
            self.actionVoltageControl.setVisible(False)
        self.actionScripting.triggered.connect(self.onScripting)
        self.actionUserFunctions.triggered.connect(self.onUserFunctionsEditor)
        self.actionMeasurementLog.triggered.connect(self.onMeasurementLog)
        self.actionDedicatedCounters.triggered.connect(self.showDedicatedCounters)
        self.actionLogic.triggered.connect(self.showLogicAnalyzer)
        self.currentTab = self.tabDict.at( min(len(self.tabDict)-1, self.config.get('MainWindow.currentIndex',0) ) )
        self.tabWidget.setCurrentIndex( self.config.get('MainWindow.currentIndex',0) )
        self.currentTab.activate()
        if hasattr( self.currentTab, 'stateChanged' ):
            self.currentTab.stateChanged.connect( self.todoList.onStateChanged )
        if 'MainWindow.State' in self.config:
            self.parent.restoreState(self.config['MainWindow.State'])
        self.initMenu()
        self.actionResume.setEnabled(False)
        if 'MainWindow.pos' in self.config:
            self.move(self.config['MainWindow.pos'])
        if 'MainWindow.size' in self.config:
            self.resize(self.config['MainWindow.size'])
        if 'MainWindow.isMaximized' in self.config:
            if self.config['MainWindow.isMaximized']:
                self.showMaximized()
        else:
            self.showMaximized()
            
        self.dedicatedCountersWindow = DedicatedCounters(self.config, self.dbConnection, self.pulser, self.globalVariablesUi, self.shutterUi,self.ExternalParametersUi.callWhenDoneAdjusting )
        self.dedicatedCountersWindow.setupUi(self.dedicatedCountersWindow)
        
        self.logicAnalyzerWindow = LogicAnalyzer(self.config, self.pulser, self.channelNameData )
        self.logicAnalyzerWindow.setupUi(self.logicAnalyzerWindow)

        if self.voltagesEnabled:
            try:
                self.voltageControlWindow = VoltageControl(self.config, self.globalVariablesUi.globalDict, self.dac)
                self.voltageControlWindow.setupUi(self.voltageControlWindow)
                self.voltageControlWindow.globalAdjustUi.outputChannelsChanged.connect( partial(self.scanExperiment.updateScanTarget, 'Voltage') )
                self.voltageControlWindow.localAdjustUi.outputChannelsChanged.connect( partial(self.scanExperiment.updateScanTarget, 'Voltage Local Adjust') )
                self.scanExperiment.updateScanTarget('Voltage', self.voltageControlWindow.globalAdjustUi.outputChannels())
                self.scanExperiment.updateScanTarget('Voltage Local Adjust', self.voltageControlWindow.localAdjustUi.outputChannels())
            except MyException.MissingFile as e:
                self.voltageControlWindow = None
                self.actionVoltageControl.setDisabled( True )
                logger.warning("Missing file - voltage subsystem disabled: {0}".format(str(e)))
            if self.voltageControlWindow:
                self.tabDict["Scan"].ppStartSignal.connect( self.voltageControlWindow.synchronize )   # upload shuttling data before running pule program
                self.dedicatedCountersWindow.autoLoad.setVoltageControl( self.voltageControlWindow )

        self.setWindowTitle("Experimental Control ({0})".format(self.project) )

        
        QtCore.QTimer.singleShot(60000, self.onCommitConfig )
        traceFilename, _ = DataDirectory.DataDirectory().sequencefile("Trace.log")
        LoggingSetup.setTraceFilename( traceFilename )
        errorFilename, _ = DataDirectory.DataDirectory().sequencefile("Error.log")
        LoggingSetup.setErrorFilename( errorFilename )
        
        # connect signals and slots for todolist and auto resume
        for name, widget in self.tabDict.items():
            if hasattr(widget,'onContinue'):
                self.dedicatedCountersWindow.autoLoad.ionReappeared.connect( widget.onContinue )
                
        # add PushDestinations
        for widget in self.tabDict.values():
            if hasattr(widget, 'addPushDestination'):
                widget.addPushDestination( 'External', self.ExternalParametersUi )
                
        # initialize ScriptingUi
        self.scriptingWindow = ScriptingUi(self)
        self.scriptingWindow.setupUi(self.scriptingWindow)

        # this is redundant in __init__ but this resolves issues with user-defined functions that reference NamedTraces
        localpath = getProject().configDir+'/UserFunctions/'
        for filename in Path(localpath.replace('\\','/')).glob('**/*.py'):
            try:
                importlib.machinery.SourceFileLoader("CustomFunctions", str(filename).replace('\\','/')).load_module()
            except SyntaxError as e:
                SyntaxError('Failed to load {0}'.format(str(filename)))

        # initialize NamedTraceUi
        self.userFunctionsEditor = UserFunctionsEditor(self, self.globalVariablesUi.globalDict)
        self.userFunctionsEditor.setupUi(self.userFunctionsEditor)

        # initialize StashButton
        self.actionStash.triggered.connect(self.onStash)
        self.actionResume.triggered.connect(self.onResume)
        self.stashButton = StashButtonControl(self.actionResume)
        self.stashButton.resume.connect(self.onResume)

    def instantiateParametersUi(self, pulser, windowName, configName, config, globalDict):
        ui = PulserParameterUi(pulser, config, configName, globalDict)
        ui.setupUi()
        uiDock = QtWidgets.QDockWidget(windowName)
        uiDock.setWidget(ui)
        uiDock.setObjectName(windowName)
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock)
        self.tabDict['Scan'].NeedsDDSRewrite.connect(ui.onWriteAll)
        return ui, uiDock

    def instantiateDDSUi(self, pulser, windowName, configName, config, globalDict):
        ui = DDSUi.DDSUi(pulser, config, configName, globalDict)
        ui.setupUi(ui)
        uiDock = QtWidgets.QDockWidget(windowName)
        uiDock.setWidget(ui)
        uiDock.setObjectName(windowName)
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock)
        self.globalVariablesUi.valueChanged.connect(ui.evaluate)
        pulser.ppActiveChanged.connect(ui.setDisabled)
        self.tabDict['Scan'].NeedsDDSRewrite.connect(ui.onWriteAll)
        return ui, uiDock

    def instantiateDACUi(self, pulser, windowName, configName, config, globalDict):
        ui = DACUi(pulser, config, configName, globalDict)
        ui.setupUi(ui)
        uiDock = QtWidgets.QDockWidget(windowName)
        uiDock.setObjectName(windowName)
        uiDock.setWidget(ui)
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock)
        pulser.ppActiveChanged.connect(ui.setDisabled)
        self.tabDict['Scan'].NeedsDDSRewrite.connect(ui.onWriteAll)
        return ui, uiDock

    def instantiateShutterUi(self, pulser, windowName, configName, config, globalDict, nameDict, nameSignal):
        ui = ShutterUi.ShutterUi(pulser, configName, 'shutter', self.config, (nameDict, nameSignal), size=49)
        ui.setupUi(ui, True)
        uiDock = QtWidgets.QDockWidget(windowName)
        uiDock.setObjectName(windowName)
        uiDock.setWidget(ui)
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, uiDock)
        pulser.ppActiveChanged.connect(ui.setDisabled)
        logger.debug("ShutterUi representation:" + repr(ui))
        return ui, uiDock

    def callWhenDoneAdjusting(self, callback):
        self.ExternalParametersUi.callWhenDoneAdjusting(callback)

    def onEnableConsole(self, state):
        self.consoleEnable = state==QtCore.Qt.Checked

    def onClearConsole(self):
        self.textEditConsole.clear()
        
    def onConsoleMaximumLinesChanged(self, maxlines):
        self.consoleMaximumLines = maxlines
        self.textEditConsole.document().setMaximumBlockCount(maxlines)
        
    def setLoggingLevel(self, index):
        self.loggingLevel = self.levelValueList[index]

    def showDedicatedCounters(self):
        self.dedicatedCountersWindow.show()
        self.dedicatedCountersWindow.setWindowState(QtCore.Qt.WindowActive)
        self.dedicatedCountersWindow.raise_()
        self.dedicatedCountersWindow.onStart() #Start displaying data immediately

    def showLogicAnalyzer(self):
        self.logicAnalyzerWindow.show()
        self.logicAnalyzerWindow.setWindowState(QtCore.Qt.WindowActive)
        self.logicAnalyzerWindow.raise_()

    def onVoltageControl(self):
        self.voltageControlWindow.show()
        self.voltageControlWindow.setWindowState(QtCore.Qt.WindowActive)
        self.voltageControlWindow.raise_()

    def onScripting(self):
        self.scriptingWindow.show()
        self.scriptingWindow.setWindowState(QtCore.Qt.WindowActive)
        self.scriptingWindow.raise_()

    def onUserFunctionsEditor(self):
        self.userFunctionsEditor.show()
        self.userFunctionsEditor.setWindowState(QtCore.Qt.WindowActive)
        self.userFunctionsEditor.raise_()

    def onAWG(self, displayName):
        awgUi = self.AWGUiDict[displayName]
        awgUi.show()
        awgUi.setWindowState(QtCore.Qt.WindowActive)
        awgUi.raise_()

    def onMeasurementLog(self):
        self.measurementLog.show()
        self.measurementLog.setWindowState(QtCore.Qt.WindowActive)
        self.measurementLog.raise_()
        
    def onClear(self):
        self.currentTab.onClear()
    
    def onSaveGUI(self, _):
        logger = logging.getLogger(__name__)
        self.currentTab.onSave()
        filename, _ = DataDirectory.DataDirectory().sequencefile("configuration.db")
        logger.info( "Saving config to "+filename )
        self.saveConfig()
        self.config.saveConfig(filename)

    def onSaveGUIYaml(self, _):
        self.currentTab.onSave()
        logger.info("Saving config")
        yamlfilename, _ = DataDirectory.DataDirectory().sequencefile("configuration.yaml")
        self.saveConfig()
        self.config.saveConfig(yamlfile=yamlfilename)

    def onCommitConfig(self):
        logger = logging.getLogger(__name__)
        self.currentTab.onSave()
        logger.debug( "Committing config" )
        self.saveConfig()
        self.config.commitToDatabase()
        QtCore.QTimer.singleShot(60000, self.onCommitConfig )      
            
    def onStart(self, checked=False, globalOverrides=list()):
        self.currentTab.onStart(globalOverrides)

    def onStash(self):
        if hasattr(self.currentTab, 'onStash'):
            self.currentTab.onStash()

    def onStashChanged(self, stash):
        self.actionResume.setEnabled(len(stash)>0)
        self.stashButton.onStashChanged(stash)

    def onResume(self, index=-1):
        if hasattr(self.currentTab, 'onResume'):
            self.currentTab.onResume(index)

    def onPause(self):
        self.currentTab.onPause()
    
    def onStop(self):
        self.currentTab.onStop()
        
    def onAbort(self):
        self.currentTab.onStop(reason='aborted')
        
    def onContinue(self):
        if hasattr(self.currentTab,'onContinue'):
            self.currentTab.onStop()
        else:
            self.statusbar.showMessage("continue not implemented")    
            
    def onReload(self):
        logger = logging.getLogger(__name__)
        logger.debug( "OnReload" )
        self.currentTab.onReload()
    
    def switchTab(self, name):
        self.tabWidget.setCurrentWidget( self.tabDict[name] )
        self.onCurrentChanged(self.tabDict.index(name))  # this gets called later, but we need it to run now in order to switch scans from the todolist
    
    def onCurrentChanged(self, index):
        if self.tabDict.at(index)!=self.currentTab:
            self.currentTab.deactivate()
            if hasattr( self.currentTab, 'stateChanged' ):
                try:
                    self.currentTab.stateChanged.disconnect()
                except TypeError:
                    pass
            self.currentTab = self.tabDict.at(index)
            self.currentTab.activate()
            if hasattr( self.currentTab, 'stateChanged' ):
                self.currentTab.stateChanged.connect( self.todoList.onStateChanged )
            self.initMenu()
            self.actionResume.setEnabled(self.currentTab.stashSize())
        
    def initMenu(self):
        """setup print and view menus"""
        #view menu
        self.menuView.clear()
        if hasattr(self.currentTab,'viewActions'):
            self.menuView.addActions(self.currentTab.viewActions())
        dockList = self.findChildren(QtWidgets.QDockWidget)
        for dock in dockList:
            self.menuView.addAction(dock.toggleViewAction())

        #print menu
        self.menuPrint.clear()
        if hasattr(self.currentTab,'printTargets'):
            for plot in self.currentTab.printTargets():
                action = self.menuPrint.addAction( plot )
                action.triggered.connect( partial(self.onPrint, plot ))
        self.menuPrint.addSeparator()
        action = self.menuPrint.addAction("Print Preferences")
        action.triggered.connect(self.preferencesUiDock.show)
        action.triggered.connect(self.preferencesUiDock.raise_)

    def onPulses(self):
        self.pulseProgramDialog.show()
        self.pulseProgramDialog.setWindowState(QtCore.Qt.WindowActive)
        self.pulseProgramDialog.raise_()
        if hasattr(self.currentTab,'experimentName'):
            self.pulseProgramDialog.setCurrentTab(self.currentTab.experimentName)
                  
    def onClose(self):
        self.parent.close()
        
    def onMessageWrite(self,message,level=logging.DEBUG):
        if self.consoleEnable and level>= self.loggingLevel:
            cursor = self.textEditConsole.textCursor()
            cursor.movePosition(QtGui.QTextCursor.End)
            if level < logging.ERROR:
                self.textEditConsole.setTextColor(QtCore.Qt.black)
            else:
                self.textEditConsole.setTextColor(QtCore.Qt.red)
            cursor.insertText(message)
            self.textEditConsole.setTextCursor(cursor)
            self.textEditConsole.ensureCursorVisible()
        
    def closeEvent(self,e):
        logger = logging.getLogger("")
        logger.debug( "Saving Configuration" )
        self.saveConfig()
        self.config.commitToDatabase()
        for tab in self.tabDict.values():
            tab.onClose()
        self.currentTab.deactivate()
        self.pulseProgramDialog.done(0)
        self.ExternalParametersSelectionUi.onClose()
        self.dedicatedCountersWindow.close()
        self.pulseProgramDialog.onClose()
        self.scriptingWindow.onClose()
        self.userFunctionsEditor.onClose()
        self.logicAnalyzerWindow.close()
        self.measurementLog.close()
        if self.voltagesEnabled:
            self.voltageControlWindow.close()
        for awgUi in self.AWGUiDict.values():
            awgUi.close()
        numTempAreas = len(self.scanExperiment.area.tempAreas)
        for i in range(numTempAreas):
            if len(self.scanExperiment.area.tempAreas) > 0:
                self.scanExperiment.area.tempAreas[0].win.close()
        # close auxiliary pulsers
        #map(lambda x: x.shutdown(), self.auxiliaryPulsers)
        for p in self.auxiliaryPulsers:
            p.shutdown()

    def saveConfig(self):
        self.config['MainWindow.State'] = self.parent.saveState()
        for tab in self.tabDict.values():
            tab.saveConfig()
        self.config['MainWindow.currentIndex'] = self.tabWidget.currentIndex()
        self.config['MainWindow.pos'] = self.pos()
        self.config['MainWindow.size'] = self.size()
        self.config['MainWindow.isMaximized'] = self.isMaximized()
        self.config['Settings.loggingLevel'] = self.loggingLevel
        self.config['Settings.consoleMaximumLinesNew'] = self.consoleMaximumLines
        self.config['Settings.ShutterNameDict'] = self.shutterNameDict 
        self.config['SettingsTriggerNameDict'] = self.triggerNameDict 
        self.config['Settings.consoleEnable'] = self.consoleEnable 
        self.pulseProgramDialog.saveConfig()
        self.scriptingWindow.saveConfig()
        self.userFunctionsEditor.saveConfig()
        self.shutterUi.saveConfig()
        self.triggerUi.saveConfig()
        self.dedicatedCountersWindow.saveConfig()
        self.logicAnalyzerWindow.saveConfig()
        if self.voltagesEnabled:
            if self.voltageControlWindow:
                self.voltageControlWindow.saveConfig()
        self.ExternalParametersSelectionUi.saveConfig()
        self.globalVariablesUi.saveConfig()
        self.loggerUi.saveConfig()
        self.todoList.saveConfig()
        self.preferencesUi.saveConfig()
        self.measurementLog.saveConfig()
        self.valueHistoryUi.saveConfig()
        self.ExternalParametersUi.saveConfig()
        list(map(lambda x: x.saveConfig(), self.objectListToSaveContext))  # call saveConfig() for each element in the list
        for awgUi in self.AWGUiDict.values():
            awgUi.saveConfig()
        
    def onProjectSelection(self):
        ui = ProjectInfoUi(self.project)
        ui.show()
        ui.exec_()
        
    def getCurrentTab(self):
        index = self.tabWidget.currentIndex()
        return self.tabDict.keyAt(index), self.tabDict.at(index)
    
    def setCurrentTab(self, name):
        self.onCurrentChanged(self.tabDict.index(name))

    def onPrint(self, target):
        """Print action is triggered on 'target', which is a plot name"""
        if hasattr( self.currentTab, 'onPrint' ):
            printer = QtPrintSupport.QPrinter(mode=QtPrintSupport.QPrinter.ScreenResolution)
            if self.preferencesUi.preferences().printPreferences.doPrint:
                dialog = QtPrintSupport.QPrintDialog(printer, self)
                dialog.setWindowTitle("Print Document")
                if dialog.exec_() != QtWidgets.QDialog.Accepted:
                    return
            printer.setResolution(self.preferencesUi.preferences().printPreferences.printResolution)
    
            pdfPrinter = QtPrintSupport.QPrinter()
            pdfPrinter.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat)
            pdfPrinter.setOutputFileName(DataDirectory.DataDirectory().sequencefile(target+".pdf")[0])
            self.currentTab.onPrint(target, printer, pdfPrinter, self.preferencesUi.preferences().printPreferences)

    def onShowDocumentation(self):
        url = "file://" + os.path.join(os.path.dirname(os.path.abspath(__file__)),"docs/_build/html/index.html")
        webbrowser.open(url, new=2)

    def show(self):
        """show ExperimentUi, and any of the other main windows which were previously visible"""
        super(ExperimentUi, self).show()

        # restore dock state of ScanExperiment. Because ScanExperiment is a child QMainWindow of ExperimentUi
        # (rather than an independent window), restoreState must be called after show() is called on the parent
        # widget in order to work properly.
        for tab in self.tabDict.values():
            tabStateName = tab.experimentName+'.MainWindow.State'
            if tabStateName in self.config:
                tab.restoreState(self.config[tabStateName])

        pulseProgramVisible = self.config.get(self.pulseProgramDialog.configname+'.isVisible', True) #pulse program defaults to visible
        if pulseProgramVisible: self.pulseProgramDialog.show()
        else: self.pulseProgramDialog.hide()

        scriptingWindowVisible = self.config.get(self.scriptingWindow.configname+'.isVisible', False)
        if scriptingWindowVisible: self.scriptingWindow.show()
        else: self.scriptingWindow.hide()

        userFunctionsEditorVisible = self.config.get(self.userFunctionsEditor.configname+'.isVisible', False)
        if userFunctionsEditorVisible: self.userFunctionsEditor.show()
        else: self.userFunctionsEditor.hide()

        if self.voltagesEnabled:
            voltageControlWindowVisible = getattr(self.voltageControlWindow.settings, 'isVisible', False)
            if voltageControlWindowVisible: self.voltageControlWindow.show()
            else: self.voltageControlWindow.hide()

        if self.AWGUiDict:
            for awgUi in self.AWGUiDict.values():
                awgUiVisible = self.config.get(awgUi.configname+'.isVisible', False)
                if awgUiVisible: awgUi.show()
                else: awgUi.hide()

        self.setFocus(True)

    def setupFPGAs(self):
        """Setup all Opal Kelly FPGAs"""
        self.dac = DACController() #100 channel DAC board

        #determine name of FPGA used for Pulser, if any
        pulserName=None
        pulserSoftwareEnabled = self.project.isEnabled('software', 'Pulser')
        if pulserSoftwareEnabled:
            pulserHardware = next(iter(pulserSoftwareEnabled.values()))['hardware']
            hardwareObjName, hardwareName = project.fromFullName(pulserHardware)
            if hardwareObjName=='Opal Kelly FPGA':
                pulserName=hardwareName
        self.settings = FPGASettings() #settings for pulser specifically

        #determine name of FPGA used for DAC, if any
        dacName=None
        voltageSoftwareEnabled = self.project.isEnabled('software', 'Voltages')
        if voltageSoftwareEnabled:
            voltageHardware = next(iter(voltageSoftwareEnabled.values()))['hardware']
            hardwareObjName, hardwareName = project.fromFullName(voltageHardware)
            if hardwareObjName=='Opal Kelly FPGA':
                dacName=hardwareName

        self.OK_FPGA_Dict = self.pulser.listBoards() #list all connected Opal Kelly FPGA boards
        logger.info( "Opal Kelly Devices found: {0}".format({k:v.modelName for k,v in self.OK_FPGA_Dict.items()}) )

        enabledFPGAs = self.project.isEnabled('hardware', 'Opal Kelly FPGA') #Dict of enabled FPGAs
        for FPGAName, FPGAConfig in enabledFPGAs.items():
            FPGA = self.pulser if FPGAName==pulserName else (self.dac if FPGAName==dacName else OKBase())
            deviceName=FPGAConfig.get('device') #The 'device' field of an FPGA should be the identifier of the FPGA.
            if not deviceName:
                logger.error("No FPGA specified: 'device' field missing in Opal Kelly FPGA: '{0}' config".format(FPGAName))
            elif deviceName not in self.OK_FPGA_Dict:
                logger.error("FPGA device {0} specified in Opal Kelly FPGA: '{1}' config cannot be found".format(deviceName, FPGAName))
            else:
                device=self.OK_FPGA_Dict[deviceName]
                FPGA.openBySerial(device.serial)
                bitFile=FPGAConfig.get('bitFile')
                checkFileValid(bitFile, 'bitfile', FPGAName)
                if FPGAName==pulserName:
                    configFile = os.path.splitext(bitFile)[0] + '.xml'
                    checkFileValid(configFile, 'config file', FPGAName)
                if FPGAConfig.get('uploadOnStartup'):
                    FPGA.uploadBitfile(bitFile)
                    logger.info("Uploaded file '{0}' to {1} (model {2}) in Opal Kelly FPGA: '{3}' config".format(bitFile, deviceName, device.modelName, FPGAName))
                if FPGAName==pulserName:   # check and make sure correct hardware is loaded
                    try:
                        FPGA.pulserConfiguration(configFile)
                    except PulserHardwareException:
                        logger.exception('PulserHardwareException occurred, likely because firmware must be uploaded. Please restart the program and upload the firmware.')
                        if not self.project.exptConfig['showGui']: #force the GUI to be shown next time
                            self.project.exptConfig['showGui'] = True
                            with open(self.project.exptConfigFilename, 'w') as f:
                                yaml.dump(self.project.exptConfig, f, default_flow_style=False)
                        sys.exit("Please restart program and upload firmware (config GUI will be shown)")
                if FPGA==self.pulser:
                    self.settings.deviceSerial = device.serial
                    self.settings.deviceDescription = device.identifier
                    self.settings.deviceInfo = device
        pulserHardwareId = self.pulser.hardwareConfigurationId()
        if pulserHardwareId:
            logger.info("Pulser Configuration {0:x}".format(pulserHardwareId))
        else:
            logger.error("No pulser available")

    def instantiateAuxiliaryPulsers(self):
        self.auxiliaryPulsers = list()
        for FPGAName, FPGAConfig in self.project.isEnabled('hardware', 'Auxiliary Pulser').items():
            FPGA = PulserHardware()
            deviceName=FPGAConfig.get('device') #The 'device' field of an FPGA should be the identifier of the FPGA.
            if not deviceName:
                logger.error("No FPGA specified: 'device' field missing in Auxiliary Opal Kelly FPGA: '{0}' config".format(FPGAName))
            elif deviceName not in self.OK_FPGA_Dict:
                logger.error("FPGA device {0} specified in Auxiliary Opal Kelly FPGA: '{1}' config cannot be found".format(deviceName, FPGAName))
            else:
                device=self.OK_FPGA_Dict[deviceName]
                FPGA.openBySerial(device.serial)
                bitFile=FPGAConfig.get('bitFile')
                checkFileValid(bitFile, 'bitfile', FPGAName)
                configFile = os.path.splitext(bitFile)[0] + '.xml'
                checkFileValid(configFile, 'config file', FPGAName)
                if FPGAConfig.get('uploadOnStartup'):
                    FPGA.uploadBitfile(bitFile)
                    logger.info("Uploaded file '{0}' to {1} (model {2}) in Opal Kelly FPGA: '{3}' config".format(bitFile, deviceName, device.modelName, FPGAName))
                FPGA.pulserConfiguration(configFile)
                pulserHardwareId = self.pulser.hardwareConfigurationId()
                if pulserHardwareId:
                    logger.info("Auxiliary Pulser {1} Configuration {0:x}".format(pulserHardwareId, FPGAName))
                else:
                    logger.error("No pulser available")
                if FPGAConfig.get('PulserParameters'):
                    ui, _ = self.instantiateParametersUi(FPGA, "{0} Pulser Config".format(FPGAName),
                                                 "{0}.PulserParameterUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict)
                    self.objectListToSaveContext.append(ui)
                if FPGAConfig.get('DDS'):
                    ui, _ = self.instantiateDDSUi(FPGA, "{0} DDS".format(FPGAName), "{0}.DDSUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict)
                    self.objectListToSaveContext.append(ui)
                if FPGAConfig.get('DAC'):
                    ui, _ = self.instantiateDACUi(FPGA, "{0} DAC".format(FPGAName), "{0}.dacUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict)
                    self.objectListToSaveContext.append(ui)
                if FPGAConfig.get('Shutters'):
                    ui, _ = self.instantiateShutterUi(FPGA, "{0} Shutter".format(FPGAName), "{0}.ShutterUi".format(FPGAName), self.config, self.globalVariablesUi.globalDict, None, None)
                    self.objectListToSaveContext.append(ui)
                self.auxiliaryPulsers.append(FPGA)
Esempio n. 7
0
class SelectionUi(SelectionForm, SelectionBase):
    outputChannelsChanged = QtCore.pyqtSignal(object)
    inputChannelsChanged = QtCore.pyqtSignal(object)
    
    def __init__(self, config, globalDict, classdict, instancename="ExternalParameterSelection.ParametersSequence", parent=None):
        SelectionBase.__init__(self, parent)
        SelectionForm.__init__(self)
        self.config = config
        self.instancename = instancename
        self.parameters = self.config.get(self.instancename, SequenceDict())
        self.enabledParametersObjects = SequenceDict()
        self.classdict = classdict
        self.globalDict = globalDict
    
    def setupUi(self, MainWindow):
        logger = logging.getLogger(__name__)
        SelectionForm.setupUi(self, MainWindow)
        self.parameterTableModel = ExternalParameterTableModel( self.parameters, self.classdict )
        self.parameterTableModel.enableChanged.connect( self.onEnableChanged )
        self.tableView.setModel( self.parameterTableModel )
        self.tableView.resizeColumnsToContents()
        self.comboBoxDelegate = ComboBoxDelegate()
        self.tableView.setItemDelegateForColumn(3, self.comboBoxDelegate)
        self.tableView.setItemDelegateForColumn(2, self.comboBoxDelegate)
        self.tableView.horizontalHeader().setStretchLastSection(True)   
        self.filter = KeyListFilter( [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown] )
        self.filter.keyPressed.connect( self.onReorder )
        self.tableView.installEventFilter(self.filter)
        self.classComboBox.addItems(sorted(self.classdict.keys()))
        self.classComboBox.currentIndexChanged[str].connect(self.getInstrumentSuggestions)
        self.addParameterButton.clicked.connect( self.onAddParameter )
        self.removeParameterButton.clicked.connect( self.onRemoveParameter )
        self.refreshInstrumentComboBox.clicked.connect(self.getInstrumentSuggestions)
        for parameter in list(self.parameters.values()):
            if parameter.enabled:
                try:
                    self.enableInstrument(parameter)
                except Exception as e:
                    logger.warning("{0} while enabling instrument {1}".format(e, parameter.name))
                    parameter.enabled = False     
        self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) ) 
        self.emitSelectionChanged()
        self.tableView.selectionModel().currentChanged.connect( self.onActiveInstrumentChanged )

    def outputChannels(self):
        self._outputChannels =  dict(itertools.chain(*[p.outputChannelList() for p in self.enabledParametersObjects.values()]))        
        return self._outputChannels
        
    def inputChannels(self):
        self._inputChannels =  dict(itertools.chain(*[p.inputChannelList() for p in self.enabledParametersObjects.values()]))        
        return self._inputChannels
        
    def emitSelectionChanged(self):
        self.outputChannelsChanged.emit( self.outputChannels() )
        self.inputChannelsChanged.emit( self.inputChannels() )

    def getInstrumentSuggestions(self, className=None):
        className = str(className) if className else self.classComboBox.currentText()
        myclass = self.classdict[className]
        if hasattr(myclass, 'connectedInstruments'):
            updateComboBoxItems(self.instrumentComboBox, sorted(myclass.connectedInstruments()))
            self.refreshInstrumentComboBox.setEnabled(True)
        else:
            self.instrumentComboBox.clear()
            self.refreshInstrumentComboBox.setEnabled(False)

    def onReorder(self, key):
        if key in [QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]:
            indexes = self.tableView.selectedIndexes()
            up = key==QtCore.Qt.Key_PageUp
            delta = -1 if up else 1
            rows = sorted(unique([ i.row() for i in indexes ]), reverse=not up)
            if self.parameterTableModel.moveRow( rows, up=up ):
                selectionModel = self.tableView.selectionModel()
                selectionModel.clearSelection()
                for index in indexes:
                    selectionModel.select( self.parameterTableModel.createIndex(index.row()+delta, index.column()), QtCore.QItemSelectionModel.Select )
            self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) )               
            self.emitSelectionChanged()

    def onEnableChanged(self, name):
        logger = logging.getLogger(__name__)
        parameter = self.parameters[name]
        if parameter.enabled:
            try:
                self.enableInstrument(parameter)
            except Exception as e:
                logger.exception( "{0} while enabling instrument {1}".format(e, name))
                parameter.enabled = False                    
                self.parameterTableModel.setParameterDict( self.parameters )
        else:
            self.disableInstrument(name)
                      
    def onAddParameter(self):
        parameter = Parameter()
        parameter.instrument = str(self.instrumentComboBox.currentText())
        parameter.className = str(self.classComboBox.currentText())
        parameter.name = str(self.nameEdit.currentText())
        if parameter.name not in self.parameters:
            self.parameters[parameter.name] = parameter
            self.parameterTableModel.setParameterDict( self.parameters )
            self.tableView.resizeColumnsToContents()
            self.tableView.horizontalHeader().setStretchLastSection(True)        
        
    def onRemoveParameter(self):
        for index in sorted(unique([ i.row() for i in self.tableView.selectedIndexes() ]), reverse=True):
            parameter = self.parameters.at(index)
            parameter.enabled=False
            self.disableInstrument(parameter.name)
            self.parameters.pop( parameter.name )
        self.parameterTableModel.setParameterDict( self.parameters )
            
    def enableInstrument(self, parameter):
        if parameter.name not in self.enabledParametersObjects:
            logger = logging.getLogger(__name__)
            instance = self.classdict[parameter.className](parameter.name, parameter.settings, self.globalDict, parameter.instrument)
            self.enabledParametersObjects[parameter.name] = instance
            self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) )               
            self.emitSelectionChanged()
            self.parameterTableModel.setParameterDict( self.parameters )
            logger.info("Enabled Instrument {0} as {1}".format(parameter.className, parameter.name))
            
    def disableInstrument(self, name):
        if name in self.enabledParametersObjects:
            logger = logging.getLogger(__name__)
            instance = self.enabledParametersObjects.pop( name )
            instance.close()
            self.enabledParametersObjects.sortToMatch( list(self.parameters.keys()) )               
            self.emitSelectionChanged()
            parameter = self.parameters[name]
            logger.info("Disabled Instrument {0} as {1}".format(parameter.className, parameter.name))
        
    def onActiveInstrumentChanged(self, modelIndex, modelIndex2 ):
        logger = logging.getLogger(__name__)
        logger.debug( "activeInstrumentChanged {0}".format( modelIndex.row() ) )
        if self.parameters.at(modelIndex.row()).enabled:
            self.treeWidget.setParameters( self.enabledParametersObjects[self.parameters.at(modelIndex.row()).name].parameter )
        
    def saveConfig(self):
        self.config[self.instancename] = self.parameters
        
    def onClose(self):
        for inst in list(self.enabledParametersObjects.values()):
            inst.close()
Esempio n. 8
0
class FitFunctionBase(object, metaclass=FitFunctionMeta):
    expression = Expression()
    name = 'None'
    parameterNames = list()

    def __init__(self):
        numParameters = len(self.parameterNames)
        self.epsfcn = 0.0
        self.parameters = [0] * numParameters
        self.startParameters = [1] * numParameters
        self.startParameterExpressions = None  # will be initialized by FitUiTableModel if values are available
        self.parameterEnabled = [True] * numParameters
        self.parametersConfidence = [None] * numParameters
        self.units = None
        self.results = SequenceDict({'RMSres': ResultRecord(name='RMSres')})
        self.useSmartStartValues = False
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative')
        self.parametersUpdated = Observable()
        self.parameterBounds = [[None, None] for _ in range(numParameters)]
        self.parameterBoundsExpressions = None
        self.useErrorBars = True

    def __setstate__(self, state):
        state.pop('parameterNames', None)
        state.pop('cov_x', None)
        state.pop('infodict', None)
        state.pop('laguerreCacheEta', None)
        state.pop('laguerreTable', None)
        state.pop('pnCacheBeta', None)
        state.pop('pnTable', None)
        self.__dict__ = state
        self.__dict__.setdefault('useSmartStartValues', False)
        self.__dict__.setdefault('startParameterExpressions', None)
        self.__dict__.setdefault('parameterBounds',
                                 [[None, None]
                                  for _ in range(len(self.parameterNames))])
        self.__dict__.setdefault('parameterBoundsExpressions', None)
        self.__dict__.setdefault('useErrorBars', True)
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative')

    def allFitParameters(self, p):
        """return a list where the disabled parameters are added to the enabled parameters given in p"""
        pindex = 0
        params = list()
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                params.append(p[pindex])
                pindex += 1
            else:
                params.append(float(self.startParameters[index]))
        return params

    @staticmethod
    def coercedValue(val, bounds):
        if bounds[1] is not None and val >= bounds[1]:
            val = float(0.95 * bounds[1] + 0.05 *
                        bounds[0] if bounds[0] is not None else bounds[1] -
                        0.01)
        if bounds[0] is not None and val <= bounds[0]:
            val = float(0.95 * bounds[0] + 0.05 *
                        bounds[1] if bounds[1] is not None else bounds[0] +
                        0.01)
        return val

    def enabledStartParameters(self, parameters=None, bounded=False):
        """return a list of only the enabled start parameters"""
        if parameters is None:
            parameters = self.startParameters
        params = list()
        if bounded:
            for enabled, param, bounds in zip(self.parameterEnabled,
                                              parameters,
                                              self.parameterBounds):
                if enabled:
                    params.append(self.coercedValue(float(param), bounds))
        else:
            for enabled, param in zip(self.parameterEnabled, parameters):
                if enabled:
                    params.append(float(param))
        return params

    def enabledFitParameters(self, parameters=None):
        """return a list of only the enabled fit parameters"""
        if parameters is None:
            parameters = self.parameters
        params = list()
        for enabled, param in zip(self.parameterEnabled, parameters):
            if enabled:
                params.append(float(param))
        return params

    def enabledParameterNames(self):
        """return a list of only the enabled fit parameters"""
        params = list()
        for enabled, param in zip(self.parameterEnabled, self.parameterNames):
            if enabled:
                params.append(param)
        return params

    def setEnabledFitParameters(self, parameters):
        """set the fitted parameters if enabled"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parameters[index] = parameters[pindex]
                pindex += 1
            else:
                self.parameters[index] = float(self.startParameters[index])

    def setEnabledConfidenceParameters(self, confidence):
        """set the parameter confidence values for the enabled parameters"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parametersConfidence[index] = confidence[pindex]
                pindex += 1
            else:
                self.parametersConfidence[index] = None

    @native
    def smartStartValues(self, x, y, parameters, enabled):
        return None

    def enabledSmartStartValues(self, x, y, parameters):
        smartParameters = self.smartStartValues(x, y, parameters,
                                                self.parameterEnabled)
        return [
            smartparam if enabled else param for enabled, param, smartparam in
            zip(self.parameterEnabled, parameters, smartParameters)
        ] if smartParameters is not None else None

    def evaluate(self, globalDict):
        myReplacementDict = self.replacementDict()
        if globalDict is not None:
            myReplacementDict.update(globalDict)
        if self.startParameterExpressions is not None:
            self.startParameters = [
                param if expr is None else self.expression.evaluateAsMagnitude(
                    expr, myReplacementDict) for param, expr in zip(
                        self.startParameters, self.startParameterExpressions)
            ]
        if self.parameterBoundsExpressions is not None:
            self.parameterBounds = [[
                bound[0]
                if expr[0] is None else self.expression.evaluateAsMagnitude(
                    expr[0], myReplacementDict),
                bound[1] if expr[1] is None else
                self.expression.evaluateAsMagnitude(expr[0], myReplacementDict)
            ] for bound, expr in zip(self.parameterBounds,
                                     self.parameterBoundsExpressions)]

    def enabledBounds(self):
        result = [[
            float(bounds[0]) if bounds[0] is not None else None,
            float(bounds[1]) if bounds[1] is not None else None
        ] for enabled, bounds in zip(self.parameterEnabled,
                                     self.parameterBounds) if enabled]
        enabled = any((any(bounds) for bounds in result))
        return result if enabled else None

    def leastsq(self, x, y, parameters=None, sigma=None):
        logger = logging.getLogger(__name__)
        # Ensure all values of sigma or non zero by replacing with the minimum nonzero value
        if sigma is not None and self.useErrorBars:
            nonzerosigma = sigma[sigma > 0]
            sigma[sigma == 0] = numpy.min(
                nonzerosigma) if len(nonzerosigma) > 0 else 1.0
        else:
            sigma = None
        if parameters is None:
            parameters = [float(param) for param in self.startParameters]
        if self.useSmartStartValues:
            smartParameters = self.smartStartValues(x, y, parameters,
                                                    self.parameterEnabled)
            if smartParameters is not None:
                parameters = [
                    smartparam if enabled else param
                    for enabled, param, smartparam in zip(
                        self.parameterEnabled, parameters, smartParameters)
                ]

        myEnabledBounds = self.enabledBounds()
        if myEnabledBounds:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsqbound(
                self.residuals,
                self.enabledStartParameters(parameters, bounded=True),
                args=(y, x, sigma),
                epsfcn=self.epsfcn,
                full_output=True,
                bounds=myEnabledBounds)
        else:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsq(
                self.residuals,
                self.enabledStartParameters(parameters),
                args=(y, x, sigma),
                epsfcn=self.epsfcn,
                full_output=True)
        self.setEnabledFitParameters(enabledOnlyParameters)
        self.update(self.parameters)
        logger.info("chisq {0}".format(sum(infodict["fvec"] *
                                           infodict["fvec"])))

        # calculate final chi square
        self.chisq = sum(infodict["fvec"] * infodict["fvec"])

        self.dof = max(len(x) - len(parameters), 1)
        RMSres = Q(sqrt(self.chisq / self.dof))
        RMSres.significantDigits = 3
        self.results['RMSres'].value = RMSres
        # chisq, sqrt(chisq/dof) agrees with gnuplot
        logger.info("success {0} {1}".format(self.ier, self.mesg))
        logger.info("Converged with chi squared {0}".format(self.chisq))
        logger.info("degrees of freedom, dof {0}".format(self.dof))
        logger.info(
            "RMS of residuals (i.e. sqrt(chisq/dof)) {0}".format(RMSres))
        logger.info("Reduced chisq (i.e. variance of residuals) {0}".format(
            self.chisq / self.dof))

        # uncertainties are calculated as per gnuplot, "fixing" the result
        # for non unit values of the reduced chisq.
        # values at min match gnuplot
        enabledParameterNames = self.enabledParameterNames()
        if cov_x is not None:
            enabledOnlyParametersConfidence = numpy.sqrt(
                numpy.diagonal(cov_x)) * sqrt(self.chisq / self.dof)
            self.setEnabledConfidenceParameters(
                enabledOnlyParametersConfidence)
            logger.info("Fitted parameters at minimum, with 68% C.I.:")
            for i, pmin in enumerate(enabledOnlyParameters):
                logger.info(
                    "%2i %-10s %12f +/- %10f" %
                    (i, enabledParameterNames[i], pmin,
                     sqrt(max(cov_x[i, i], 0)) * sqrt(self.chisq / self.dof)))

            logger.info("Correlation matrix")
            # correlation matrix close to gnuplot
            messagelist = ["               "]
            for i in range(len(enabledOnlyParameters)):
                messagelist.append("%-10s" % (enabledParameterNames[i], ))
            logger.info(" ".join(messagelist))
            messagelist = []
            for i in range(len(enabledOnlyParameters)):
                messagelist.append("%10s" % enabledParameterNames[i])
                for j in range(i + 1):
                    messagelist.append(
                        "%10f" %
                        (cov_x[i, j] / sqrt(abs(cov_x[i, i] * cov_x[j, j])), ))
                logger.info(" ".join(messagelist))

                #-----------------------------------------------
        else:
            self.parametersConfidence = [None] * len(self.parametersConfidence)

        return self.parameters

    def __str__(self):
        return "; ".join([
            ", ".join([self.name, self.functionString] + [
                "{0}={1}".format(name, value)
                for name, value in zip(self.parameterNames, self.parameters)
            ])
        ])

    def setConstant(self, name, value):
        setattr(self, name, value)

    def update(self, parameters=None):
        self.parametersUpdated.fire(values=self.replacementDict())

    def toXmlElement(self, parent):
        myroot = ElementTree.SubElement(parent, 'FitFunction', {
            'name': self.name,
            'functionString': self.functionString
        })
        for name, value, confidence, enabled, startExpression, bounds, boundsexpression in zip_longest(
                self.parameterNames, self.parameters,
                self.parametersConfidence, self.parameterEnabled,
                self.startParameterExpressions, self.parameterBounds,
                self.parameterBoundsExpressions):
            e = ElementTree.SubElement(
                myroot, 'Parameter', {
                    'name': name,
                    'confidence': repr(confidence),
                    'enabled': str(enabled),
                    'startExpression': str(startExpression),
                    'bounds': ",".join(map(str, bounds)),
                    'boundsExpression': ",".join(map(str, boundsexpression))
                })
            e.text = str(value)
        for result in list(self.results.values()):
            e = ElementTree.SubElement(myroot, 'Result', {
                'name': result.name,
                'definition': str(result.definition)
            })
            e.text = str(result.value)
        return myroot

    def toHdf5(self, group):
        fitfunction_group = group.require_group('fitfunction')
        fitfunction_group.attrs['name'] = self.name
        fitfunction_group.attrs['functionString'] = self.functionString
        parameter_group = fitfunction_group.require_group('parameters')
        for index, (name, value, confidence, enabled, startExpression, bounds,
                    boundsexpression) in enumerate(
                        zip_longest(self.parameterNames, self.parameters,
                                    self.parametersConfidence,
                                    self.parameterEnabled,
                                    self.startParameterExpressions,
                                    self.parameterBounds,
                                    self.parameterBoundsExpressions)):
            g = parameter_group.require_group(name)
            g.attrs['confidence'] = confidence
            g.attrs['enabled'] = enabled
            g.attrs['startExpression'] = str(startExpression)
            g.attrs['bounds'] = bounds
            g.attrs['boundsExpression'] = ",".join(map(str, boundsexpression))
            g.attrs['value'] = value
            g.attrs['index'] = index
        results_group = fitfunction_group.require_group('results')
        for result in list(self.results.values()):
            g = results_group.requie_group(result.name)
            g.attrs['definition'] = str(result.definition)
            g.attrs['value'] = repr(result.value)

    def residuals(self, p, y, x, sigma):
        p = self.allFitParameters(p)
        if sigma is not None:
            return (y - self.functionEval(x, *p)) / sigma
        else:
            return y - self.functionEval(x, *p)

    def value(self, x, p=None):
        p = self.parameters if p is None else p
        return self.functionEval(x, *p)

    def replacementDict(self):
        replacement = dict(list(zip(self.parameterNames, self.parameters)))
        replacement.update(
            dict(((v.name, v.value) for v in list(self.results.values()))))
        return replacement
class VoltageGlobalAdjust(VoltageGlobalAdjustForm, VoltageGlobalAdjustBase ):
    updateOutput = QtCore.pyqtSignal(object, object)
    outputChannelsChanged = QtCore.pyqtSignal(object)
    _channelParams = {}
    
    def __init__(self, config, globalDict, parent=None):
        VoltageGlobalAdjustForm.__init__(self)
        VoltageGlobalAdjustBase.__init__(self, parent)
        self.config = config
        self.configname = 'VoltageGlobalAdjust.Settings'
        self.settings = self.config.get(self.configname, Settings())
        self.globalAdjustDict = SequenceDict()
        self.myLabelList = list()
        self.myBoxList = list()
        self.historyCategory = 'VoltageGlobalAdjust'
        self.adjustHistoryName = None
        self.globalDict = globalDict
        self.adjustCache = self.config.get(self.configname+".cache", dict()) 
        self.savedValue = defaultdict( lambda: None )
        self.displayValueObservable = defaultdict( lambda: Observable() )

    def setupUi(self, parent):
        VoltageGlobalAdjustForm.setupUi(self, parent)
        self.gainBox.setValue(self.settings.gain)
        self.gainBox.valueChanged.connect( self.onGainChanged )
        self.tableModel = VoltageGlobalAdjustTableModel( self.globalAdjustDict, self.globalDict )
        self.tableView.setModel( self.tableModel )
        self.tableView.setSortingEnabled(True)   # triggers sorting
        self.delegate =  MagnitudeSpinBoxDelegate(self.globalDict)
        self.tableView.setItemDelegateForColumn(1, self.delegate)
        
    def onGainChanged(self, gain):
        self.settings.gain = gain
        self.updateOutput.emit(self.globalAdjustDict, self.settings.gain)        
        
    def setupGlobalAdjust(self, name, adjustDict):
        if name!=self.adjustHistoryName:
            self.adjustCache[self.adjustHistoryName] = [v.data for v in list(self.globalAdjustDict.values())]
            self.settings.gainCache[self.adjustHistoryName] = self.settings.gain
            self.settings.gain = self.settings.gainCache.get( name, self.settings.gain )
            if name in self.adjustCache:
                for data in self.adjustCache[name]:
                    if data[0] in adjustDict:
                        adjustDict[data[0]].data = data
            self.adjustHistoryName = name
        self.globalAdjustDict = adjustDict
        for name, adjust in self.globalAdjustDict.items():
            try:
                adjust.valueChanged.connect(self.onValueChanged, QtCore.Qt.UniqueConnection)
            except:
                pass
        self.tableModel.setGlobalAdjust( adjustDict )
        self.outputChannelsChanged.emit( self.outputChannels() )
        self.gainBox.setValue(self.settings.gain)
        #self.updateOutput.emit(self.globalAdjustDict, self.settings.gain)        
        
    def onValueChanged(self, name, value, string, origin):
        if origin=='recalculate':
            self.tableModel.valueRecalcualted(name)
        self.globalAdjustDict[name]._value = float(self.globalAdjustDict[name]._value)
        self.updateOutput.emit(self.globalAdjustDict, self.settings.gain)
    
    def saveConfig(self):
        self.config[self.configname] = self.settings
        self.adjustCache[self.adjustHistoryName] = [v.data for v in list(self.globalAdjustDict.values())]
        self.config[self.configname+".cache"] = self.adjustCache
        
    def setValue(self, channel, value):
        self.globalAdjustDict[channel].value = value 
        return value

    def getValue(self, channel):
        return self.globalAdjustDict[channel].value
    
    def currentValue(self, channel):
        return self.globalAdjustDict[channel].value
    
    def saveValue(self, channel):
        self.savedValue[channel] = self.globalAdjustDict[channel].value
    
    def restoreValue(self, channel):
        if self.savedValue[channel] is not None:
            self.globalAdjustDict[channel].value = self.savedValue[channel]
        return True
    
    def strValue(self, channel):
        adjust = self.globalAdjustDict[channel]
        return adjust.string if adjust.hasDependency else None
    
    def setStrValue(self, channel, value):
        pass
    
    def outputChannels(self):
        self._outputChannels = dict(( (channelName, VoltageOutputChannel(self, None, channelName, self.globalDict, )) for channelName in self.globalAdjustDict.keys() ))
        return self._outputChannels
Esempio n. 10
0
class FitFunctionBase(object, metaclass=FitFunctionMeta):
    expression = Expression()
    name = 'None'
    parameterNames = list()
    def __init__(self):
        numParameters = len(self.parameterNames)
        self.epsfcn=0.0
        self.parameters = [0] * numParameters
        self.startParameters = [1] * numParameters 
        self.startParameterExpressions = None   # will be initialized by FitUiTableModel if values are available
        self.parameterEnabled = [True] * numParameters
        self.parametersConfidence = [None] * numParameters
        self.units = None
        self.results = SequenceDict({'RMSres': ResultRecord(name='RMSres')})
        self.useSmartStartValues = False
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative' )
        self.parametersUpdated = Observable()
        self.parameterBounds = [[None, None] for _ in range(numParameters) ]
        self.parameterBoundsExpressions = None
        self.useErrorBars = True
        
    def __setstate__(self, state):
        state.pop('parameterNames', None )
        state.pop('cov_x', None)
        state.pop('infodict', None)
        state.pop('laguerreCacheEta', None )
        state.pop('laguerreTable', None)
        state.pop('pnCacheBeta', None )
        state.pop('pnTable', None)
        self.__dict__ = state
        self.__dict__.setdefault( 'useSmartStartValues', False )
        self.__dict__.setdefault( 'startParameterExpressions', None )
        self.__dict__.setdefault( 'parameterBounds', [[None, None] for _ in range(len(self.parameterNames)) ]  )
        self.__dict__.setdefault( 'parameterBoundsExpressions', None)
        self.__dict__.setdefault( 'useErrorBars', True)
        self.hasSmartStart = not hasattr(self.smartStartValues, 'isNative' )
 
    def allFitParameters(self, p):
        """return a list where the disabled parameters are added to the enabled parameters given in p"""
        pindex = 0
        params = list()
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                params.append(p[pindex])
                pindex += 1
            else:
                params.append(float(self.startParameters[index]))
        return params
    
    @staticmethod
    def coercedValue( val, bounds ):
        if bounds[1] is not None and val>=bounds[1]:
            val = float(0.95*bounds[1]+0.05*bounds[0] if bounds[0] is not None else bounds[1]-0.01)
        if bounds[0] is not None and val<=bounds[0]:
            val = float(0.95*bounds[0]+0.05*bounds[1] if bounds[1] is not None else bounds[0]+0.01)
        return val
    
    def enabledStartParameters(self, parameters=None, bounded=False):
        """return a list of only the enabled start parameters"""
        if parameters is None:
            parameters = self.startParameters
        params = list()
        if bounded:
            for enabled, param, bounds in zip(self.parameterEnabled, parameters, self.parameterBounds):
                if enabled:
                    params.append(self.coercedValue(float(param), bounds))
        else:
            for enabled, param in zip(self.parameterEnabled, parameters):
                if enabled:
                    params.append(float(param))
        return params

    def enabledFitParameters(self, parameters=None):
        """return a list of only the enabled fit parameters"""
        if parameters is None:
            parameters = self.parameters
        params = list()
        for enabled, param in zip(self.parameterEnabled, parameters):
            if enabled:
                params.append(float(param))
        return params

    def enabledParameterNames(self):
        """return a list of only the enabled fit parameters"""
        params = list()
        for enabled, param in zip(self.parameterEnabled, self.parameterNames):
            if enabled:
                params.append(param)
        return params
    
    def setEnabledFitParameters(self, parameters):
        """set the fitted parameters if enabled"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parameters[index] = parameters[pindex]
                pindex += 1
            else:
                self.parameters[index] = float(self.startParameters[index])
    
    def setEnabledConfidenceParameters(self, confidence):
        """set the parameter confidence values for the enabled parameters"""
        pindex = 0
        for index, enabled in enumerate(self.parameterEnabled):
            if enabled:
                self.parametersConfidence[index] = confidence[pindex]
                pindex += 1
            else:
                self.parametersConfidence[index] = None        

    @native
    def smartStartValues(self, x, y, parameters, enabled):
        return None
    
    def enabledSmartStartValues(self, x, y, parameters):
        smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled)
        return [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters)] if smartParameters is not None else None

    def evaluate(self, globalDict ):
        myReplacementDict = self.replacementDict()
        if globalDict is not None:
            myReplacementDict.update( globalDict )
        if self.startParameterExpressions is not None:
            self.startParameters = [param if expr is None else self.expression.evaluateAsMagnitude(expr, myReplacementDict ) for param, expr in zip(self.startParameters, self.startParameterExpressions)]
        if self.parameterBoundsExpressions is not None:
            self.parameterBounds = [[bound[0] if expr[0] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict),
                                     bound[1] if expr[1] is None else self.expression.evaluateAsMagnitude(expr[0], myReplacementDict)]
                                     for bound, expr in zip(self.parameterBounds, self.parameterBoundsExpressions)]

    def enabledBounds(self):
        result = [[float(bounds[0]) if bounds[0] is not None else None,
                   float(bounds[1]) if bounds[1] is not None else None] for enabled, bounds in zip(self.parameterEnabled, self.parameterBounds) if enabled]
        enabled = any( (any(bounds) for bounds in result) )
        return result if enabled else None

    def leastsq(self, x, y, parameters=None, sigma=None):
        logger = logging.getLogger(__name__)
        # Ensure all values of sigma or non zero by replacing with the minimum nonzero value
        if sigma is not None and self.useErrorBars:
            nonzerosigma = sigma[sigma>0]
            sigma[sigma==0] = numpy.min(nonzerosigma) if len(nonzerosigma)>0 else 1.0
        else:
            sigma = None 
        if parameters is None:
            parameters = [float(param) for param in self.startParameters]
        if self.useSmartStartValues:
            smartParameters = self.smartStartValues(x, y, parameters, self.parameterEnabled)
            if smartParameters is not None:
                parameters = [ smartparam if enabled else param for enabled, param, smartparam in zip(self.parameterEnabled, parameters, smartParameters)]
        
        myEnabledBounds = self.enabledBounds()
        if myEnabledBounds:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsqbound(self.residuals, self.enabledStartParameters(parameters, bounded=True),
                                                                                                 args=(y, x, sigma), epsfcn=self.epsfcn, full_output=True, bounds=myEnabledBounds)
        else:
            enabledOnlyParameters, cov_x, infodict, self.mesg, self.ier = leastsq(self.residuals, self.enabledStartParameters(parameters), args=(y, x, sigma),
                                                                                            epsfcn=self.epsfcn, full_output=True)
        self.setEnabledFitParameters(enabledOnlyParameters)
        self.update(self.parameters)
        logger.info( "chisq {0}".format( sum(infodict["fvec"]*infodict["fvec"]) ) )        
        
        # calculate final chi square
        self.chisq=sum(infodict["fvec"]*infodict["fvec"])
        
        self.dof = max( len(x)-len(parameters), 1)
        RMSres = Q(sqrt(self.chisq/self.dof))
        RMSres.significantDigits = 3
        self.results['RMSres'].value = RMSres
        # chisq, sqrt(chisq/dof) agrees with gnuplot
        logger.info(  "success {0} {1}".format( self.ier, self.mesg ) )
        logger.info(  "Converged with chi squared {0}".format(self.chisq) )
        logger.info(  "degrees of freedom, dof {0}".format( self.dof ) )
        logger.info(  "RMS of residuals (i.e. sqrt(chisq/dof)) {0}".format( RMSres ) )
        logger.info(  "Reduced chisq (i.e. variance of residuals) {0}".format( self.chisq/self.dof ) )
        
        # uncertainties are calculated as per gnuplot, "fixing" the result
        # for non unit values of the reduced chisq.
        # values at min match gnuplot
        enabledParameterNames = self.enabledParameterNames()
        if cov_x is not None:
            enabledOnlyParametersConfidence = numpy.sqrt(numpy.diagonal(cov_x))*sqrt(self.chisq/self.dof)
            self.setEnabledConfidenceParameters(enabledOnlyParametersConfidence)
            logger.info(  "Fitted parameters at minimum, with 68% C.I.:" )
            for i, pmin in enumerate(enabledOnlyParameters):
                logger.info(  "%2i %-10s %12f +/- %10f"%(i, enabledParameterNames[i], pmin, sqrt(max(cov_x[i, i], 0))*sqrt(self.chisq/self.dof)) )
        
            logger.info(  "Correlation matrix" )
            # correlation matrix close to gnuplot
            messagelist = ["               "]
            for i in range(len(enabledOnlyParameters)): messagelist.append( "%-10s"%(enabledParameterNames[i],) )
            logger.info( " ".join(messagelist))
            messagelist = []
            for i in range(len(enabledOnlyParameters)):
                messagelist.append( "%10s"%enabledParameterNames[i] )
                for j in range(i+1):
                    messagelist.append(  "%10f"%(cov_x[i, j]/sqrt(abs(cov_x[i, i]*cov_x[j, j])),) )
                logger.info( " ".join(messagelist))
    
                #-----------------------------------------------
        else:
            self.parametersConfidence = [None]*len(self.parametersConfidence)
 
        return self.parameters
                
    def __str__(self):
        return "; ".join([", ".join([self.name, self.functionString] + [ "{0}={1}".format(name, value) for name, value in zip(self.parameterNames, self.parameters)])])

    def setConstant(self, name, value):
        setattr(self, name, value)
        
    def update(self,parameters=None):
        self.parametersUpdated.fire( values=self.replacementDict() )
    
    def toXmlElement(self, parent):
        myroot  = ElementTree.SubElement(parent, 'FitFunction', {'name': self.name, 'functionString': self.functionString})
        for name, value, confidence, enabled, startExpression, bounds, boundsexpression in zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions):
            e = ElementTree.SubElement( myroot, 'Parameter', {'name':name, 'confidence':repr(confidence), 'enabled': str(enabled), 'startExpression': str(startExpression), 'bounds': ",".join(map(str, bounds)),
                                                              'boundsExpression': ",".join(map(str, boundsexpression))})
            e.text = str(value)
        for result in list(self.results.values()):
            e = ElementTree.SubElement( myroot, 'Result', {'name':result.name, 'definition':str(result.definition)})
            e.text = str(result.value)
        return myroot

    def toHdf5(self, group):
        fitfunction_group = group.require_group('fitfunction')
        fitfunction_group.attrs['name'] = self.name
        fitfunction_group.attrs['functionString'] = self.functionString
        parameter_group = fitfunction_group.require_group('parameters')
        for index, (name, value, confidence, enabled, startExpression, bounds, boundsexpression) in enumerate(zip_longest(self.parameterNames, self.parameters, self.parametersConfidence, self.parameterEnabled, self.startParameterExpressions, self.parameterBounds, self.parameterBoundsExpressions)):
            g = parameter_group.require_group(name)
            g.attrs['confidence'] = confidence
            g.attrs['enabled'] = enabled
            g.attrs['startExpression'] = str(startExpression)
            g.attrs['bounds'] = bounds
            g.attrs['boundsExpression'] = ",".join(map(str, boundsexpression))
            g.attrs['value'] = value
            g.attrs['index'] = index
        results_group =  fitfunction_group.require_group('results')
        for result in list(self.results.values()):
            g = results_group.requie_group(result.name)
            g.attrs['definition'] = str(result.definition)
            g.attrs['value'] = repr(result.value)

    def residuals(self, p, y, x, sigma):
        p = self.allFitParameters(p)
        if sigma is not None:
            return (y-self.functionEval(x, *p))/sigma
        else:
            return y-self.functionEval(x, *p)
        
    def value(self,x,p=None):
        p = self.parameters if p is None else p
        return self.functionEval(x, *p )

    def replacementDict(self):
        replacement = dict(list(zip(self.parameterNames, self.parameters)))
        replacement.update( dict( ( (v.name, v.value) for v in list(self.results.values()) ) ) )
        return replacement