Exemplo n.º 1
0
class MDevice(QThread):
    '''
  MView uses the MDevice class to give all sources of data a common 
  interface with which to interact in the context of MView. These 
  sources of data can be anything including but not limited to LabRad 
  servers, RS232 devices, GPIB Devices, they can even represent the 
  contents of .hdf5 files. Devices in MView are created by instantiating
  their device drivers. For example, if there are two RS232 devices, 
  we create two instances of the RS232 device driver. This means that 
  only one generic device driver needs to be created for one interface 
  (RS232, LabRad Servers, HDF5 files, etc.) and it can then be applied 
  to all devices that use the same interface.
  '''
    updateSignal = pyqtSignal()
    addParameterSignal = pyqtSignal(str)
    lock = threading.Lock()

    begin_signal = pyqtSignal(name="begin_signal")

    def __init__(self, name, *args, **kwargs):
        '''Initializes the device:

    1. Sets the frame title. 1.
    2. Sets the refresh rate. 2.

    Function arguments:

    :param name: The name of the device

   '''
        super(MDevice, self).__init__()
        self.lockLoggingSettings = kwargs.get("lock_logging_settings", False)
        self.defaultLogLocation = kwargs.get("default_log_location", None)
        self.dataType = kwargs.get("data_type", "float32")
        # Create a new MFrame
        web.devices.append(self)
        self.frame = MFrame()
        # print "Setting title to:", name, args
        self.frame.setTitle(name)
        self.name = name
        self.refreshRate = 1
        self.container = None
        self.datachest = None
        self.keepGoing = True
        self.settingResultIndices = []
        self.notifier_mailing_lists = []
        self.doneLoading = False

        #self.memory_tracker = tracker.SummaryTracker()
    def log(self, log):
        """ Tell the device whether to log data or not

        :param log: Boolean

    """
        if log == False:
            for p in self.getParameters():
                self.disableDataLogging(p)
        else:
            if (self.frame.getDataChestWrapper() == None):
                self.configureDataLogging()

        self.frame.masterEnableDataLogging(log)

    def isLogging(self):
        '''Getter for whether or not datalogging is enabled for this device.

      :rtype: boolean

   '''
        return self.frame.isDataLogging()

    def setContainer(self, container):
        # traceback.print_stack()
        self.container = container
        self.frame.setContainer(container)

    def getContainer(self):
        return self.container

    def updateContainer(self):
        '''Refresh the devices container (Tile) on the GUI
      by emitting an update signal
   '''
        if self.container != None:
            self.updateSignal.emit()

    def addButton(self, *args):
        pass

    def setTitle(self, title):
        self.frame.setTitle(title)

    def query(self, *args):
        pass

    def setYLabel(self, *args):
        pass

    def setRefreshRate(self, *args):
        pass

    def setPlotRefreshRate(self, *args):
        pass

    def addButtonToGui(self, button):
        self.frame.appendButton(button)

    def addReadout(self, name, units):
        self.nicknames.append(name)
        self.units.append(units)

    def addPlot(self, length=None, *args):

        if (self.isLogging()):
            self.frame.addPlot(length)
            # Datalogging must be enabled if we want to plot data.

            return self.frame.getPlot()
        else:
            raise Exception("Cannot add plot before enabling data logging.")
            return None

    def getFrame(self):
        """Return the device's frame."""
        return self.frame

    def stop(self):
        # print "stopping device thread..."
        self.keepGoing = False
        # print "device thread stopped."
        #self.device_stop_signal.emit()
        if self.frame.DataLoggingInfo()['chest']:
            self.frame.DataLoggingInfo()['chest'].close()
        self.close()

    def __threadSafeClose(self):
        if self.frame.DataLoggingInfo()['chest']:
            self.frame.DataLoggingInfo()['chest'].close()

    def plot(self, plot):
        self.frame.setHasPlot(plot)

    def begin(self, **kwargs):
        '''Start the device. 
   '''
        # Automatically refresh node data in callQuery
        self.refreshNodeDataInCallQuery = kwargs.get('auto_refresh_node', True)
        # if not self.refreshNodeDataInCallQuery:
        # print self, "will not automatically refresh node data"
        # traceback.print_stack()
        self.onBegin()
        # self.frame.setReadingIndex(self.settingResultIndices)
        #self.configureDataLogging()
        # Each device NEEDS to run on a different thread
        # than the main thread (which ALWAYS runs the GUI).
        # This thread is responsible for querying the devices.
        #        self.deviceThread = threading.Thread(target=self.callQuery, args=[])
        #        # If the main thread stops, stop the child thread.
        #        self.deviceThread.daemon = True
        #        # Start the thread.

        self.start()
        #self.callQuery()
    def __threadSafeBegin(self):
        self.configureDataLogging()

    def configureDataLogging(self):

        # print self, "is datalogging"
        self.frame.DataLoggingInfo()['name'] = self.name

        self.frame.DataLoggingInfo(
        )['lock_logging_settings'] = self.lockLoggingSettings
        if self.defaultLogLocation != None:
            # If the current directory is a subdirectory of the default,
            # then that is ok and the current directory should not be
            # changed.
            print "current location:", self.frame.DataLoggingInfo()['location']
            print "default:", self.defaultLogLocation
            if not (self.defaultLogLocation
                    in self.frame.DataLoggingInfo()['location']):
                print "Paths not ok"

                self.frame.DataLoggingInfo(
                )['location'] = self.defaultLogLocation

        self.frame.DataLoggingInfo()['chest'] = MDataBaseWrapper(self)

        self.datachest = self.frame.DataLoggingInfo()['chest']

    def onBegin(self):
        '''Called at the end of MDevice.begin(). This is called before 
        MView starts. This allows us to configure settings that 
        MView might use while starting. This might include datalog 
        locations or device-specific information.'''
        pass

    def loaded(self):

        print self, "loaded."
        self.onLoad()
        self.doneLoading = True

    def isLoaded(self):
        return self.doneLoading

    def onLoad(self):
        '''Called at the end of MGui.startGui(), when the main 
        MView GUI has finished loading. This allows the 
        MDevice to configure pieces of MView only available
        once the program has fully loaded.'''
        pass

    def onAddParameter(self, *args, **kwargs):
        '''Called when when a new parameter is added. 
    It is passed whatever MDevice.addParameter() is passed. 
    (Note: MDevice.onAddParameter() and MDevice.addParameter() 
    are different). This function must return a tuple in 
    the form ((str) Parameter Name, (int)Precision, (str) units)
   '''
        return

    def setPrecisions(self, precisions):
        self.frame.setPrecisions(precisions)

    def setSigFigs(self, parameter, sigfigs):
        self.frame.setSigFigs(parameter, sigfigs)

    def _setReadings(self, readings, update=True):
        '''Tell the frame what the readings are so that they can be logged.

        :param readings: Type: list

   '''

        # readings = self.frame.getReadings()
        # print "set readings called"
        # traceback.print_stack()
        # if readings != None:
        # node = self.frame.getNode()
        # anchorData = []
        # # # print "HERE A"
        # if node is not None:

        # for input in node.getAnchors():
        # if input.getType() == 'input':
        # print "INPUT ANCHOR", input
        # data = input.getData()
        # if data != None and type(data) is list:
        # anchorData.append(data[-1])
        # elif data != None:
        # anchorData.append(None)
        # print "readigns:", readings
        # print "anchordata:", anchorData
        # readings.extend(anchorData)
        # print "comb readigns:", readings

        # else:
    def isOutOfRange(self, key):
        return self.frame.getOutOfRangeStatus(key)

    def setOutOfRange(self, key):
        # print self, key, "is out of range"
        self.frame.setOutOfRange(key)

    def setInRange(self, key):
        self.frame.setInRange(key)

    def disableRange(self):
        self.frame.disableRange()

    def setReading(self, parameter, reading):
        self.frame.setReading(parameter, reading)

    def setMailingLists(self, lists):
        self.notifier_mailing_lists = lists

    def getMailingLists(self):
        return self.notifier_mailing_lists

    def getUnit(self, parameter):
        return self.frame.getUnit(parameter)

    def setUnit(self, parameter, unit):
        self.frame.setUnit(parameter, unit)

    def setPrecision(self, parameter, precision):
        self.frame.setPrecision(parameter, precision)

    def getPrecision(self, parameter):
        return self.frame.getPrecision(parameter)

    def getSigFigs(self, parameter):
        return self.frame.getSigFigs(parameter)

    def getReading(self, parameter):
        return self.frame.getReading(parameter)

    def setReadingIndex(self, parameter, index):
        self.frame.setReadingIndex(parameter, index)

    def getReadingIndex(self, parameter):
        return self.frame.getReadingIndex(parameter)

    def setCommand(self, parameter, command):
        # print "Setting command for", parameter, "is", command
        self.frame.setCommand(parameter, command)

    def getCommand(self, parameter):
        # print "Getting Parameter:",self.frame.getCommand(parameter)
        return self.frame.getCommand(parameter)

    def getParameters(self):
        return self.frame.getParameters()

    def getNicknames(self):
        return self.frame.getNicknames()

    def setParamVisibility(self, parameter, visible):
        self.frame.setParamVisibility(parameter, True)

    def getParamVisibility(self, paramteter):
        return self.frame.getParamVisibility(parameter)

    def getReadingIndex(self, parameter):
        return self.frame.getReadingIndex(parameter)

    def enableDataLogging(self, parameter):
        self.frame.DataLoggingInfo()['channels'][parameter] = True

    def disableDataLogging(self, parameter):
        self.frame.DataLoggingInfo()['channels'][parameter] = False

    def isDataLoggingEnabled(self, parameter):
        return self.frame.DataLoggingInfo()['channels'][parameter]

    def getParameterType(self, parameter):
        if type(parameter) is dict:
            parameter = parameter.keys()[0]
        return self.frame.getNode().getAnchorByName(parameter).getType()

    def disableAllDataLogging(self):
        self.frame.masterEnableDataLogging(False)
        for p in self.getParameters():
            self.disableDataLogging(p)

    def enableAllDataLogging(self):
        self.frame.masterEnableDataLogging(True)

    def run(self):
        '''Automatically called periodically, 
        determined by MDevice.Mframe.getRefreshRate(). 
        There is also a MDevice.Mframe.setRefreshRate()
        function with which the refresh rate can be configured.
        '''

        while True:
            #t1 = time.time()
            self.query()
            node = self.frame.getNode()
            if node is not None and self.refreshNodeDataInCallQuery:
                self.frame.getNode().refreshData()
            if self.datachest is not None and self.doneLoading:
                try:

                    if self.frame.isDataLogging():

                        #print "MDevice:", str(self),"thread id:",int(QThread.currentThreadId())
                        self.datachest.save()
                        pass

                except:
                    traceback.print_exc()

            if web.gui != None and web.gui.MAlert != None:
                web.gui.MAlert.monitorReadings(self)

            self.updateContainer()
            #t2 = time.time()
            #print self, "time to run:", t2 - t1
            if self.keepGoing:
                self.msleep(int(self.frame.getRefreshRate() * 1000))
            else:
                return

                #threading.Timer(self.frame.getRefreshRate(),
                #               self.callQuery).start()

    def prompt(self, button):
        '''Called when 
    a device's button is pushed. Button is an array which 
    is associated with the button. The array is constructed 
    in the device driver code, and the PyQT button is then appended
    to the end by MView. The array associated with the button is passed 
    to prompt() in the device driver. The device driver then determines 
    what to do based on the button pushed. 
   '''
        pass

    def close(self):
        return

    def addParameter(self, *args, **kwargs):
        """Adds a parameter to the GUI. The first argument is the name,
        the other arguments are specific to the device driver.
        """
        try:
            name = args[0]
        except:
            raise AttributError(
                "The first argument of addParameter() must be a name")

        show = kwargs.get("show", True)
        units = kwargs.get('units', None)
        sigfigs = kwargs.get('significant_figures', None)
        precision = kwargs.get('precision', None)
        if sigfigs is None and precision is None:
            precision = 2
        index = kwargs.get('index', None)
        log = kwargs.get("log", self.isLogging())
        self.frame.addParameter((name, units, precision))
        self.setReadingIndex(name, index)
        self.setPrecision(name, precision)
        self.setSigFigs(name, sigfigs)
        self.setUnit(name, units)
        self.onAddParameter(*args, **kwargs)
        self.frame.setParamVisibility(name, show)
        self.frame.DataLoggingInfo()['channels'][name] = log
        self.addParameterSignal.emit(name)

    # def logData(self, b):
    #     """Enable or disable datalogging for the device."""
    #     # if channels!= None:
    #     #    self.frame.DataLoggingInfo['channels'] = channels
    #     self.frame.enableDataLogging(b)

    def __str__(self):
        if self.frame.getTitle() is None:
            return "Unnamed Device"
        return self.frame.getTitle()