def addSettings(self, objWithVA, conf=None): """ Adds settings as one widget on a line for each VigilantAttribute (VA) in the given object. Each setting entry created is added to .entries. objWithVA (object): an object with VAs. conf (None or dict of str->config): allows to override the automatic selection of the VA widget. See odemis.gui.conf.data for documentation. raise: LookupError: if no VA is found on the objWithVA """ vas = getVAs(objWithVA) if not vas: raise LookupError("No VAs found!") if not conf: conf = {} vas_names = util.sorted_according_to(vas.keys(), conf.keys()) for name in vas_names: va = vas[name] self.setting_controller.add_setting_entry(name, va, None, conf=conf.get( name, None))
def add_hw_component(self, hw_comp, setting_controller, hidden=None): """ Add setting entries for the given hardware component hidden (None or set of str): name of VAs to not show """ hidden = HIDDEN_VAS | (hidden or set()) self.setting_controllers.append(setting_controller) vas_comp = getVAs(hw_comp) vas_config = get_hw_config( hw_comp, self._hw_settings_config) # OrderedDict or dict # Re-order the VAs of the component in the same order as in the config vas_names = util.sorted_according_to(list(vas_comp.keys()), list(vas_config.keys())) for name in vas_names: try: if name in hidden: continue elif name in vas_config: va_conf = vas_config[name] else: logging.debug("No config found for %s: %s", hw_comp.role, name) va_conf = None va = vas_comp[name] setting_controller.add_setting_entry(name, va, hw_comp, va_conf) except TypeError: msg = "Error adding %s setting for: %s" logging.exception(msg, hw_comp.name, name)
def get_local_vas(hw_comp, hw_settings): """ Find all the VAs of a component which are worthy to become local VAs. hw_comp (HwComponent): The component to look at hw_settings (dict): the hardware settings, as received from get_hw_settings_config() return (set of str): all the names for the given comp """ comp_vas = getVAs(hw_comp) config_vas = get_hw_config(hw_comp, hw_settings) settings = set() for name, va in comp_vas.items(): # Take all VAs that would be displayed on the stream panel if name in HIDDEN_VAS or va.readonly: continue try: ctyp = config_vas[name]["control_type"] if ctyp == odemis.gui.CONTROL_NONE: continue except KeyError: # not in config => it'll be displayed pass settings.add(name) return settings
def get_local_vas(hw_comp, hidden=None): """ Find all the VAs of a component which are worthy to become local VAs. :param hw_comp: (HwComponent) :param hidden: (set) Name of VAs to ignore return (set of str): all the names for the given comp """ config = get_hw_settings_config() hidden_vas = HIDDEN_VAS | (hidden or set()) comp_vas = getVAs(hw_comp) config_vas = config.get(hw_comp.role, {}) # OrderedDict or dict settings = set() for name, va in comp_vas.items(): # Take all VAs that would be displayed on the stream panel if name in hidden_vas or va.readonly: continue try: ctyp = config_vas[name]["control_type"] if ctyp == odemis.gui.CONTROL_NONE: continue except KeyError: # not in config => it'll be displayed pass settings.add(name) return settings
def __init__(self, components): """ components (set of HwComponents): component which should be observed """ self._all_settings = {} self._components = components # keep a reference to the components, so they are not garbage collected self._va_updaters = [ ] # keep a reference to the subscribers so they are not garbage collected for comp in components: self._all_settings[comp.name] = {} vas = model.getVAs(comp).items() prepare_to_listen_to_more_vas(len(vas)) for va_name, va in vas: if va_name in HIDDEN_VAS: continue # Store current value of VA (calling .value might take some time) self._all_settings[comp.name][va_name] = [va.value, va.unit] # Subscribe to VA, update dictionary on callback def update_settings(value, comp_name=comp.name, va_name=va_name): self._all_settings[comp_name][va_name][0] = value self._va_updaters.append(update_settings) va.subscribe(update_settings)
def __init__(self, btn_ctrl, va, main_data): """ :type btn_ctrl: odemis.gui.comp.buttons.ImageTextToggleButton """ super(ChamberButtonController, self).__init__(btn_ctrl, va, main_data) self.main_data = main_data # If pressure information, assume it is a complete SEM chamber, # otherwise assume it uses a sample loader like the Phenom or Delphi. if 'pressure' in getVAs(main_data.chamber): main_data.chamber.pressure.subscribe(self._update_label, init=True) self._btn_faces = { 'normal': { 'normal': imgdata.btn_press.Bitmap, 'hover': imgdata.btn_press_h.Bitmap, 'active': imgdata.btn_press_a.Bitmap, }, 'working': { 'normal': imgdata.btn_press_orange.Bitmap, 'hover': imgdata.btn_press_orange_h.Bitmap, 'active': imgdata.btn_press_orange_a.Bitmap, }, 'vacuum': { 'normal': imgdata.btn_press_green.Bitmap, 'hover': imgdata.btn_press_green_h.Bitmap, 'active': imgdata.btn_press_green_a.Bitmap, } } self._tooltips = { CHAMBER_PUMPING: "Pumping...", CHAMBER_VENTING: "Venting...", CHAMBER_VENTED: "Pump the chamber", CHAMBER_VACUUM: "Vent the chamber", } else: self.btn.SetLabel("CHAMBER") self._btn_faces = { 'normal': { 'normal': imgdata.btn_eject.Bitmap, 'hover': imgdata.btn_eject_h.Bitmap, 'active': imgdata.btn_eject_a.Bitmap, }, 'working': { 'normal': imgdata.btn_eject_orange.Bitmap, 'hover': imgdata.btn_eject_orange_h.Bitmap, 'active': imgdata.btn_eject_orange_a.Bitmap, }, 'vacuum': { 'normal': imgdata.btn_eject_green.Bitmap, 'hover': imgdata.btn_eject_green_h.Bitmap, 'active': imgdata.btn_eject_green_a.Bitmap, } } self._tooltips = { CHAMBER_PUMPING: "Loading...", CHAMBER_VENTING: "Ejecting...", CHAMBER_VENTED: "Load the sample", CHAMBER_VACUUM: "Eject the sample", }
def add_hw_component(self, hw_comp, setting_controller, hidden=None): """ Add setting entries for the given hardware component hidden (None or set of str): name of VAs to not show """ hidden = HIDDEN_VAS | (hidden or set()) self.setting_controllers.append(setting_controller) vas_comp = getVAs(hw_comp) vas_config = get_hw_config(hw_comp, self._hw_settings_config) # OrderedDict or dict # Re-order the VAs of the component in the same order as in the config vas_names = util.sorted_according_to(vas_comp.keys(), vas_config.keys()) for name in vas_names: try: if name in hidden: continue elif name in vas_config: va_conf = vas_config[name] else: logging.debug("No config found for %s: %s", hw_comp.role, name) va_conf = None va = vas_comp[name] setting_controller.add_setting_entry(name, va, hw_comp, va_conf) except TypeError: msg = "Error adding %s setting for: %s" logging.exception(msg, hw_comp.name, name)
def addSettings(self, objWithVA, conf=None): """ Adds settings as one widget on a line for each VigilantAttribute (VA) in the given object. Each setting entry created is added to .entries. objWithVA (object): an object with VAs. conf (None or dict of str->config): allows to override the automatic selection of the VA widget. See odemis.gui.conf.data for documentation. raise: LookupError: if no VA is found on the objWithVA """ vas = getVAs(objWithVA) if not vas: raise LookupError("No VAs found!") if not conf: conf = {} vas_names = util.sorted_according_to(vas.keys(), conf.keys()) for name in vas_names: va = vas[name] self.setting_controller.add_setting_entry(name, va, None, conf=conf.get(name, None))
def _get_settings_vas(self, stream): """ Find all the VAs of a stream which can potentially affect the acquisition time return (set of VAs) """ nvas = model.getVAs(stream) # name -> va vas = set() # remove some VAs known to not affect the acquisition time for n, va in nvas.items(): if n not in self.VAS_NO_ACQUSITION_EFFECT: vas.add(va) return vas
def _speedUpPyroVAConnect(comp): """ Ensures that all the VAs of the component will be quick to access comp (Component) """ # Force the creation of the connection # If the connection already exists it's very fast, otherwise, we wait # for the connection to be created in a separate thread for name, va in model.getVAs(comp).items(): t = threading.Thread(name="Connection to VA %s.%s" % (comp.name, name), target=va._pyroBind) t.daemon = True t.start()
def _set_fan(self, enable): """ Turn on/off the fan of the CCD enable (boolean): True to turn on/restore the fan, and False to turn if off """ main_data = self._tab_data_model.main if "fanSpeed" not in model.getVAs(main_data.ccd): return fs = main_data.ccd.fanSpeed if enable: if self._orig_fan_speed is not None: fs.value = max(fs.value, self._orig_fan_speed) else: self._orig_fan_speed = fs.value fs.value = 0
def __init__(self, name, role, children, settle_time=0, **kwargs): ''' children (dict string->model.HwComponent): the children There must be exactly two children "pmt-control" and "detector". settle_time (0 < float): time to wait after turning on the gain to have it fully working. Raise an ValueError exception if the children are not compatible ''' # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, **kwargs) if settle_time < 0: raise ValueError("Settle time of %g s for '%s' is negative" % (settle_time, name)) elif settle_time > 10: # a large value is a sign that the user mistook in units raise ValueError("Settle time of %g s for '%s' is too long" % (settle_time, name)) self._settle_time = settle_time # Check the children pmt = children["detector"] if not isinstance(pmt, ComponentBase): raise ValueError("Child detector is not a component.") if not hasattr(pmt, "data") or not isinstance(pmt.data, DataFlowBase): raise ValueError("Child detector is not a Detector component.") self._pmt = pmt self.children.value.add(pmt) self._shape = pmt.shape # copy all the VAs and Events from the PMT to here (but .state and .children). pmtVAs = model.getVAs(pmt) for key, value in pmtVAs.items(): setattr(self, key, value) pmtEvents = model.getEvents(pmt) for key, value in pmtEvents.items(): setattr(self, key, value) ctrl = children["pmt-control"] if not isinstance(ctrl, ComponentBase): raise ValueError("Child pmt-control is not a component.") self._control = ctrl self.children.value.add(ctrl) self.data = PMTDataFlow(self, self._pmt, self._control) # Duplicate control unit VAs # In case of counting PMT these VAs are not available since a # spectrograph is given instead of the control unit. try: if (hasattr(ctrl, "gain") and isinstance(ctrl.gain, model.VigilantAttributeBase)): self._gain = ctrl.gain.range[0] self.gain = model.FloatContinuous(self._gain, ctrl.gain.range, unit="V", setter=self._setGain) self._last_gain = self._gain self._setGain(self._gain) # Just start with no gain if (hasattr(ctrl, "powerSupply") and isinstance( ctrl.powerSupply, model.VigilantAttributeBase)): self.powerSupply = ctrl.powerSupply # Turn on the controller self.powerSupply.value = True except IOError: # FIXME: needs to be handled directly by PMTControl (at least automatic reconnect) raise HwError("PMT Control Unit connection timeout. " "Please turn off and on the power to the box and " "then restart Odemis.") # Protection VA should be available anyway if not (hasattr(ctrl, "protection") and isinstance(ctrl.protection, model.VigilantAttributeBase)): raise IOError( "Given component appears to be neither a PMT control " "unit or a spectrograph since protection VA is not " "available.")
def print_vattributes(component, pretty): for name, value in model.getVAs(component).items(): print_vattribute(name, value, pretty)
def __init__(self, name, role, children, **kwargs): ''' children (dict string->model.HwComponent): the children There must be exactly two children "spectrograph" and "detector". The first dimension of the CCD is supposed to be along the wavelength, with the first pixels representing the lowest wavelengths. Raise an ValueError exception if the children are not compatible ''' # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, **kwargs) # Check the children dt = children["detector"] if not isinstance(dt, ComponentBase): raise ValueError("Child detector is not a component.") if not hasattr(dt, "shape") or not isinstance(dt.shape, tuple): raise ValueError("Child detector is not a Detector component.") if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase): raise ValueError("Child detector is not a Detector component.") self._detector = dt self.children.add(dt) sp = children["spectrograph"] if not isinstance(sp, ComponentBase): raise ValueError("Child spectrograph is not a component.") try: if not "wavelength" in sp.axes: raise ValueError("Child spectrograph has no 'wavelength' axis.") except Exception: raise ValueError("Child spectrograph is not an Actuator.") self._spectrograph = sp self.children.add(sp) # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError("Child detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning("Child detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. # TODO: give a init parameter or VA to specify a smaller window height # than the entire CCD (some spectrometers have only noise on the top and # bottom) if dt.binning.range[1][1] < dt.resolution.range[1][1]: # without software binning, we are stuck to the max binning logging.info("Spectrometer %s will only use a %d px band of the %d " "px of the sensor", name, dt.binning.range[1][1], dt.resolution.range[1][1]) resolution = [dt.resolution.range[1][0], 1] # max,1 binning = [1, 1] # horizontally: as fine as possible, with a maximum around 256px, over # this, use binning if possible binning[0] = min(max(resolution[0] // 256, dt.binning.range[0][0]), dt.binning.range[1][0]) resolution[0] //= binning[0] # vertically: 1, with binning as big as possible binning[1] = min(dt.binning.range[1][1], dt.resolution.range[1][1]) min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, [min_res, max_res], setter=self._setResolution) # 2D binning is like a "small resolution" self._binning = binning self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning) self._setBinning(binning) # will also update the resolution # TODO: support software binning by rolling up our own dataflow that # does data merging assert dt.resolution.range[0][1] == 1 self.data = dt.data # duplicate every other VA and Event from the detector # that includes required VAs like .pixelSize and .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug("skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "pixelSize") assert hasattr(self, "exposureTime") # Update metadata of detector with wavelength conversion # whenever the wavelength/grating axes moves. try: self._pn_phys = sp.getPolyToWavelength() except AttributeError: raise ValueError("Child spectrograph has no getPolyToWavelength() method") sp.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinningUpdate) self.binning.subscribe(self._onResBinningUpdate, init=True)
def print_vattributes(component, pretty): for name, value in model.getVAs(component).items(): if name in special_va_names: continue print_vattribute(name, value, pretty)
def __init__(self, name, role, children, settle_time=0, **kwargs): ''' children (dict string->model.HwComponent): the children There must be exactly two children "pmt-control" and "detector". settle_time (0 < float): time to wait after turning on the gain to have it fully working. Raise an ValueError exception if the children are not compatible ''' # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, **kwargs) if settle_time < 0: raise ValueError("Settle time of %g s for '%s' is negative" % (settle_time, name)) elif settle_time > 10: # a large value is a sign that the user mistook in units raise ValueError("Settle time of %g s for '%s' is too long" % (settle_time, name)) self._settle_time = settle_time # Check the children pmt = children["detector"] if not isinstance(pmt, ComponentBase): raise ValueError("Child detector is not a component.") if not hasattr(pmt, "data") or not isinstance(pmt.data, DataFlowBase): raise ValueError("Child detector is not a Detector component.") self._pmt = pmt self.children.value.add(pmt) self._shape = pmt.shape # copy all the VAs and Events from the PMT to here (but .state and .children). pmtVAs = model.getVAs(pmt) for key, value in pmtVAs.items(): setattr(self, key, value) pmtEvents = model.getEvents(pmt) for key, value in pmtEvents.items(): setattr(self, key, value) ctrl = children["pmt-control"] if not isinstance(ctrl, ComponentBase): raise ValueError("Child pmt-control is not a component.") self._control = ctrl self.children.value.add(ctrl) self.data = PMTDataFlow(self, self._pmt, self._control) # Duplicate control unit VAs # In case of counting PMT these VAs are not available since a # spectrograph is given instead of the control unit. try: if (hasattr(ctrl, "gain") and isinstance(ctrl.gain, model.VigilantAttributeBase)): self._gain = ctrl.gain.range[0] self.gain = model.FloatContinuous(self._gain, ctrl.gain.range, unit="V", setter=self._setGain) self._last_gain = self._gain self._setGain(self._gain) # Just start with no gain if (hasattr(ctrl, "powerSupply") and isinstance(ctrl.powerSupply, model.VigilantAttributeBase)): self.powerSupply = ctrl.powerSupply # Turn on the controller self.powerSupply.value = True except IOError: # FIXME: needs to be handled directly by PMTControl (at least automatic reconnect) raise HwError("PMT Control Unit connection timeout. " "Please turn off and on the power to the box and " "then restart Odemis.") # Protection VA should be available anyway if not (hasattr(ctrl, "protection") and isinstance(ctrl.protection, model.VigilantAttributeBase)): raise IOError("Given component appears to be neither a PMT control " "unit or a spectrograph since protection VA is not " "available.")
def __init__(self, name, role, children=None, daemon=None, **kwargs): """ All the arguments are identical to AndorCam2, expected: children (dict string->kwargs): name of child must be "shamrock" and the kwargs contains the arguments passed to instantiate the Shamrock component """ # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, daemon=daemon, **kwargs) # Create the detector (ccd) child try: dt_kwargs = children["andorcam2"] except Exception: raise ValueError("AndorSpec excepts one child named 'andorcam2'") # We could inherit from it, but difficult to not mix up .binning, .shape # .resolution... self._detector = andorcam2.AndorCam2(daemon=daemon, **dt_kwargs) self._children.add(self._detector) dt = self._detector # Copy and adapt the VAs and roattributes from the detector # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError( "Child detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning( "Child detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. if dt.binning.range[1][1] < dt.resolution.range[1][1]: # without software binning, we are stuck to the max binning logging.info( "Spectrometer %s will only use a %d px band of the %d " "px of the sensor", name, dt.binning.range[1][1], dt.resolution.range[1][1]) resolution = (dt.resolution.range[1][0], 1) # max,1 # vertically: 1, with binning as big as possible binning = (dt.binning.value[0], min(dt.binning.range[1][1], dt.resolution.range[1][1])) min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, [min_res, max_res], setter=self._setResolution) # 2D binning is like a "small resolution" self._binning = binning self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning) self._setBinning(binning) # will also update the resolution # TODO: update also the metadata MD_SENSOR_PIXEL_SIZE pxs = dt.pixelSize.value[ 0], dt.pixelSize.value[1] * dt.binning.value[1] self.pixelSize = model.VigilantAttribute(pxs, unit="m", readonly=True) # Note: the metadata has no MD_PIXEL_SIZE, but a MD_WL_LIST # TODO: support software binning by rolling up our own dataflow that # does data merging assert dt.resolution.range[0][1] == 1 self.data = dt.data # duplicate every other VA and Event from the detector # that includes required VAs like .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents( dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug( "skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "exposureTime") # Create the spectrograph (actuator) child try: sp_kwargs = children["shamrock"] except Exception: raise ValueError("AndorSpec excepts one child named 'shamrock'") self._spectrograph = Shamrock(parent=self, path=self._detector._initpath, daemon=daemon, **sp_kwargs) self._children.add(self._spectrograph) self._spectrograph.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinningUpdate) self.binning.subscribe(self._onResBinningUpdate, init=True)
def __init__(self, name, role, children, **kwargs): """ children (dict string->model.HwComponent): the children There must be exactly two children "spectrograph" and "detector". The first dimension of the CCD is supposed to be along the wavelength, with the first pixels representing the lowest wavelengths. Raise: ValueError: if the children are not compatible """ # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, **kwargs) # Check the children dt = children["detector"] if not isinstance(dt, ComponentBase): raise ValueError("Child detector is not a component.") if (not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or not model.hasVA(dt, "pixelSize"): raise ValueError("Child detector is not a Detector component.") if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase): raise ValueError("Child detector has not .data DataFlow.") self._detector = dt self.children.value.add(dt) sp = children["spectrograph"] if not isinstance(sp, ComponentBase): raise ValueError("Child spectrograph is not a component.") try: if "wavelength" not in sp.axes: raise ValueError("Child spectrograph has no 'wavelength' axis.") except Exception: raise ValueError("Child spectrograph is not an Actuator.") self._spectrograph = sp self.children.value.add(sp) # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError("Child detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning( "Child detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1], ) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # Wrapper for the dataflow self.data = SpecDataFlow(self, dt.data) # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. # TODO: give a init parameter or VA to specify a smaller window height # than the entire CCD (some spectrometers have only noise on the top and # bottom) if dt.binning.range[1][1] < dt.resolution.range[1][1]: # without software binning, we are stuck to the max binning # TODO: support software binning by rolling up our own dataflow that # does data merging logging.info( "Spectrometer %s will only use a %d px band of the %d " "px of the sensor", name, dt.binning.range[1][1], dt.resolution.range[1][1], ) assert dt.resolution.range[0][1] == 1 resolution = (dt.resolution.range[1][0], 1) # max,1 min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, (min_res, max_res), setter=self._setResolution) # 2D binning is like a "small resolution" # Initial binning is minimum binning horizontally, and maximum vertically self._binning = (1, min(dt.binning.range[1][1], dt.resolution.range[1][1])) self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning) self._setBinning(self._binning) # will also update the resolution # TODO: also wrap translation, if it exists? # duplicate every other VA and Event from the detector # that includes required VAs like .pixelSize and .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug("skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "pixelSize") if not model.hasVA(self, "exposureTime"): logging.warning("Spectrometer %s has no exposureTime VA", name) sp.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinning) self.binning.subscribe(self._onResBinning) self._updateWavelengthList()
def __init__(self, name, role, dependencies, settle_time=0, **kwargs): ''' dependencies (dict string->model.HwComponent): the dependencies There must a dependency "detector" and at least one of the dependencies "pmt-control" and "pmt-signal". "pmt-control" takes a PMTControl or spectrograph object, "pmt-signal" an extra detector to be activated during the acquisition (eg, for shutter control) settle_time (0 < float): time to wait after turning on the gain to have it fully working. Raise an ValueError exception if the dependencies are not compatible ''' # we will fill the set of dependencies with Components later in ._dependencies model.Detector.__init__(self, name, role, dependencies=dependencies, **kwargs) if settle_time < 0: raise ValueError("Settle time of %g s for '%s' is negative" % (settle_time, name)) elif settle_time > 10: # a large value is a sign that the user mistook in units raise ValueError("Settle time of %g s for '%s' is too long" % (settle_time, name)) self._settle_time = settle_time # Check the dependencies pmt = dependencies["detector"] if not isinstance(pmt, ComponentBase): raise ValueError("Dependency detector is not a component.") if not hasattr(pmt, "data") or not isinstance(pmt.data, DataFlowBase): raise ValueError( "Dependency detector is not a Detector component.") self._pmt = pmt self._shape = pmt.shape # copy all the VAs and Events from the PMT to here (but .state and .dependencies). pmtVAs = model.getVAs(pmt) for key, value in pmtVAs.items(): if not hasattr(self, key): setattr(self, key, value) pmtEvents = model.getEvents(pmt) for key, value in pmtEvents.items(): setattr(self, key, value) if "pmt-control" in dependencies: ctrl = dependencies["pmt-control"] self._control = ctrl if not isinstance(ctrl, ComponentBase): raise ValueError("Dependency pmt-control is not a component.") # Duplicate control unit VAs # In case of counting PMT these VAs are not available since a # spectrograph is given instead of the control unit. try: if model.hasVA(ctrl, "gain"): gain = ctrl.gain.range[0] self.gain = model.FloatContinuous(gain, ctrl.gain.range, unit="V", setter=self._setGain) self._last_gain = gain self._setGain(gain) # Just start with no gain if model.hasVA(ctrl, "powerSupply"): self.powerSupply = ctrl.powerSupply # Turn on the controller self.powerSupply.value = True except IOError: # FIXME: needs to be handled directly by PMTControl (at least automatic reconnect) raise HwError( "PMT Control Unit connection timeout. " "Please turn off and on the power to the box and " "then restart Odemis.") # Protection VA should be available anyway if not model.hasVA(ctrl, "protection"): raise ValueError( "Given component appears to be neither a PMT control " "unit or a spectrograph since protection VA is not " "available.") else: self._control = None if "pmt-signal" in dependencies: self._signal = dependencies["pmt-signal"] if not isinstance(self._signal, ComponentBase): raise ValueError("Dependency pmt-signal is not a component.") if not hasattr(self._signal, "data") or not isinstance( self._signal.data, model.DataFlowBase): raise ValueError( "Dependency pmt-signal doesn't have an attribute .data of type DataFlow." ) else: self._signal = None self.data = PMTDataFlow(self, self._pmt, self._control, self._signal)
def __init__(self, name, role, children, **kwargs): ''' children (dict string->model.HwComponent): the children There must be exactly two children "spectrograph" and "detector". The first dimension of the CCD is supposed to be along the wavelength, with the first pixels representing the lowest wavelengths. Raise an ValueError exception if the children are not compatible ''' # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, **kwargs) # Check the children dt = children["detector"] if not isinstance(dt, ComponentBase): raise ValueError("Child detector is not a component.") if not hasattr(dt, "shape") or not isinstance(dt.shape, tuple): raise ValueError("Child detector is not a Detector component.") if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase): raise ValueError("Child detector is not a Detector component.") self._detector = dt self.children.add(dt) sp = children["spectrograph"] if not isinstance(sp, ComponentBase): raise ValueError("Child spectrograph is not a component.") try: if not "wavelength" in sp.axes: raise ValueError( "Child spectrograph has no 'wavelength' axis.") except Exception: raise ValueError("Child spectrograph is not an Actuator.") self._spectrograph = sp self.children.add(sp) # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError( "Child detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning( "Child detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. # TODO: give a init parameter or VA to specify a smaller window height # than the entire CCD (some spectrometers have only noise on the top and # bottom) if dt.binning.range[1][1] < dt.resolution.range[1][1]: # without software binning, we are stuck to the max binning logging.info( "Spectrometer %s will only use a %d px band of the %d " "px of the sensor", name, dt.binning.range[1][1], dt.resolution.range[1][1]) resolution = [dt.resolution.range[1][0], 1] # max,1 binning = [1, 1] # horizontally: as fine as possible, with a maximum around 256px, over # this, use binning if possible binning[0] = min(max(resolution[0] // 256, dt.binning.range[0][0]), dt.binning.range[1][0]) resolution[0] //= binning[0] # vertically: 1, with binning as big as possible binning[1] = min(dt.binning.range[1][1], dt.resolution.range[1][1]) min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, [min_res, max_res], setter=self._setResolution) # 2D binning is like a "small resolution" self._binning = binning self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning) self._setBinning(binning) # will also update the resolution # TODO: support software binning by rolling up our own dataflow that # does data merging assert dt.resolution.range[0][1] == 1 self.data = dt.data # duplicate every other VA and Event from the detector # that includes required VAs like .pixelSize and .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents( dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug( "skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "pixelSize") assert hasattr(self, "exposureTime") # Update metadata of detector with wavelength conversion # whenever the wavelength/grating axes moves. try: self._pn_phys = sp.getPolyToWavelength() except AttributeError: raise ValueError( "Child spectrograph has no getPolyToWavelength() method") sp.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinningUpdate) self.binning.subscribe(self._onResBinningUpdate, init=True)
def print_vattributes(component, pretty): for name, value in model.getVAs(component).items(): if name in VAS_HIDDEN: continue print_vattribute(name, value, pretty)
def __init__(self, name, role, dependencies, **kwargs): ''' dependencies (dict string->model.HwComponent): the dependencies There must be exactly two dependencies "spectrograph" and "detector". The first dimension of the CCD is supposed to be along the wavelength, with the first pixels representing the lowest wavelengths. Raise: ValueError: if the dependencies are not compatible ''' # we will fill the set of dependencies with Components later in ._dependencies model.Detector.__init__(self, name, role, dependencies=dependencies, **kwargs) # Check the dependencies dt = dependencies["detector"] if not isinstance(dt, ComponentBase): raise ValueError("Dependency detector is not a component.") if ((not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or not model.hasVA(dt, "pixelSize")): raise ValueError( "Dependency detector is not a Detector component.") if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase): raise ValueError("Dependency detector has not .data DataFlow.") self._detector = dt self.dependencies.value.add(dt) sp = dependencies["spectrograph"] if not isinstance(sp, ComponentBase): raise ValueError("Dependency spectrograph is not a component.") try: if "wavelength" not in sp.axes: raise ValueError( "Dependency spectrograph has no 'wavelength' axis.") except Exception: raise ValueError("Dependency spectrograph is not an Actuator.") self._spectrograph = sp self.dependencies.value.add(sp) # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError( "Dependency detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning( "Dependency detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # Wrapper for the dataflow self.data = SpecDataFlow(self, dt.data) # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. # So max vertical resolution is always 1. assert dt.resolution.range[0][1] == 1 resolution = (dt.resolution.range[1][0], 1) # max,1 min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, (min_res, max_res), setter=self._setResolution) # The vertical binning is linked to the detector resolution, as it # represents how many pixels are used (and binned). So the maximum # binning is the maximum *resolution* (and it's converted either to # detector binning, or binned later in software). min_bin = (dt.binning.range[0][0], dt.binning.range[0][1]) max_bin = (dt.binning.range[1][0], dt.resolution.range[1][1]) # Initial binning is minimum binning horizontally, and maximum vertically self._binning = (1, dt.resolution.range[1][1]) self._max_det_vbin = dt.binning.range[1][1] self.binning = model.ResolutionVA(self._binning, (min_bin, max_bin), setter=self._setBinning) self._setBinning(self._binning) # will also update the resolution # TODO: also wrap translation, if it exists? # duplicate every other VA and Event from the detector # that includes required VAs like .pixelSize and .exposureTime for aname, value in chain( model.getVAs(dt).items(), model.getEvents(dt).items()): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug( "skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "pixelSize") if not model.hasVA(self, "exposureTime"): logging.warning("Spectrometer %s has no exposureTime VA", name) sp.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinning) self.binning.subscribe(self._onResBinning) self._updateWavelengthList()
def __init__(self, name, role, children=None, daemon=None, **kwargs): """ All the arguments are identical to AndorCam2, expected: children (dict string->kwargs): name of child must be "shamrock" and the kwargs contains the arguments passed to instantiate the Shamrock component """ # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, daemon=daemon, **kwargs) # Create the detector (ccd) child try: dt_kwargs = children["andorcam2"] except Exception: raise ValueError("AndorSpec excepts one child named 'andorcam2'") # We could inherit from it, but difficult to not mix up .binning, .shape # .resolution... self._detector = andorcam2.AndorCam2(parent=self, daemon=daemon, **dt_kwargs) self.children.value.add(self._detector) dt = self._detector # Copy and adapt the VAs and roattributes from the detector # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError("Child detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning("Child detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. if dt.binning.range[1][1] < dt.resolution.range[1][1]: # without software binning, we are stuck to the max binning logging.info("Spectrometer %s will only use a %d px band of the %d " "px of the sensor", name, dt.binning.range[1][1], dt.resolution.range[1][1]) resolution = (dt.resolution.range[1][0], 1) # max,1 # vertically: 1, with binning as big as possible binning = (dt.binning.value[0], min(dt.binning.range[1][1], dt.resolution.range[1][1])) min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, [min_res, max_res], setter=self._setResolution) # 2D binning is like a "small resolution" self._binning = binning self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning) self._setBinning(binning) # will also update the resolution # TODO: update also the metadata MD_SENSOR_PIXEL_SIZE pxs = dt.pixelSize.value[0], dt.pixelSize.value[1] * dt.binning.value[1] self.pixelSize = model.VigilantAttribute(pxs, unit="m", readonly=True) # Note: the metadata has no MD_PIXEL_SIZE, but a MD_WL_LIST # TODO: support software binning by rolling up our own dataflow that # does data merging assert dt.resolution.range[0][1] == 1 self.data = dt.data # duplicate every other VA and Event from the detector # that includes required VAs like .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug("skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "exposureTime") # Create the spectrograph (actuator) child try: sp_kwargs = children["shamrock"] except Exception: raise ValueError("AndorSpec excepts one child named 'shamrock'") self._spectrograph = Shamrock(parent=self, camera=self._detector, path=self._detector._initpath, daemon=daemon, **sp_kwargs) self.children.value.add(self._spectrograph) self._spectrograph.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinningUpdate) self.binning.subscribe(self._onResBinningUpdate, init=True)
def __init__(self, name, role, dependencies, **kwargs): ''' dependencies (dict string->model.HwComponent): the dependencies There must be exactly two dependencies "spectrograph" and "detector". The first dimension of the CCD is supposed to be along the wavelength, with the first pixels representing the lowest wavelengths. Raise: ValueError: if the dependencies are not compatible ''' # we will fill the set of dependencies with Components later in ._dependencies model.Detector.__init__(self, name, role, dependencies=dependencies, **kwargs) # Check the dependencies dt = dependencies["detector"] if not isinstance(dt, ComponentBase): raise ValueError("Dependency detector is not a component.") if ((not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or not model.hasVA(dt, "pixelSize")): raise ValueError("Dependency detector is not a Detector component.") if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase): raise ValueError("Dependency detector has not .data DataFlow.") self._detector = dt self.dependencies.value.add(dt) sp = dependencies["spectrograph"] if not isinstance(sp, ComponentBase): raise ValueError("Dependency spectrograph is not a component.") try: if "wavelength" not in sp.axes: raise ValueError("Dependency spectrograph has no 'wavelength' axis.") except Exception: raise ValueError("Dependency spectrograph is not an Actuator.") self._spectrograph = sp self.dependencies.value.add(sp) # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError("Dependency detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning("Dependency detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # Wrapper for the dataflow self.data = SpecDataFlow(self, dt.data) # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. # So max vertical resolution is always 1. assert dt.resolution.range[0][1] == 1 resolution = (dt.resolution.range[1][0], 1) # max,1 min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, (min_res, max_res), setter=self._setResolution) # The vertical binning is linked to the detector resolution, as it # represents how many pixels are used (and binned). So the maximum # binning is the maximum *resolution* (and it's converted either to # detector binning, or binned later in software). min_bin = (dt.binning.range[0][0], dt.binning.range[0][1]) max_bin = (dt.binning.range[1][0], dt.resolution.range[1][1]) # Initial binning is minimum binning horizontally, and maximum vertically self._binning = (1, dt.resolution.range[1][1]) self._max_det_vbin = dt.binning.range[1][1] self.binning = model.ResolutionVA(self._binning, (min_bin, max_bin), setter=self._setBinning) self._setBinning(self._binning) # will also update the resolution # TODO: also wrap translation, if it exists? # duplicate every other VA and Event from the detector # that includes required VAs like .pixelSize and .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug("skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "pixelSize") if not model.hasVA(self, "exposureTime"): logging.warning("Spectrometer %s has no exposureTime VA", name) sp.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinning) self.binning.subscribe(self._onResBinning) self._updateWavelengthList()
def __init__(self, name, role, children, **kwargs): ''' children (dict string->model.HwComponent): the children There must be exactly two children "spectrograph" and "detector". The first dimension of the CCD is supposed to be along the wavelength, with the first pixels representing the lowest wavelengths. Raise: ValueError: if the children are not compatible ''' # we will fill the set of children with Components later in ._children model.Detector.__init__(self, name, role, **kwargs) # Check the children dt = children["detector"] if not isinstance(dt, ComponentBase): raise ValueError("Child detector is not a component.") if ((not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or not model.hasVA(dt, "pixelSize")): raise ValueError("Child detector is not a Detector component.") if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase): raise ValueError("Child detector has not .data DataFlow.") self._detector = dt self.children.value.add(dt) sp = children["spectrograph"] if not isinstance(sp, ComponentBase): raise ValueError("Child spectrograph is not a component.") try: if "wavelength" not in sp.axes: raise ValueError("Child spectrograph has no 'wavelength' axis.") except Exception: raise ValueError("Child spectrograph is not an Actuator.") self._spectrograph = sp self.children.value.add(sp) # set up the detector part # check that the shape is "horizontal" if dt.shape[0] <= 1: raise ValueError("Child detector must have at least 2 pixels horizontally") if dt.shape[0] < dt.shape[1]: logging.warning("Child detector is shaped vertically (%dx%d), " "this is probably incorrect, as wavelengths are " "expected to be along the horizontal axis", dt.shape[0], dt.shape[1]) # shape is same as detector (raw sensor), but the max resolution is always flat self._shape = tuple(dt.shape) # duplicate # Wrapper for the dataflow self.data = SpecDataFlow(self, dt.data) # The resolution and binning are derived from the detector, but with # settings set so that there is only one horizontal line. # TODO: give a init parameter or VA to specify a smaller window height # than the entire CCD (some spectrometers have only noise on the top and # bottom) if dt.binning.range[1][1] < dt.resolution.range[1][1]: # without software binning, we are stuck to the max binning # TODO: support software binning by rolling up our own dataflow that # does data merging logging.info("Spectrometer %s will only use a %d px band of the %d " "px of the sensor", name, dt.binning.range[1][1], dt.resolution.range[1][1]) assert dt.resolution.range[0][1] == 1 resolution = (dt.resolution.range[1][0], 1) # max,1 min_res = (dt.resolution.range[0][0], 1) max_res = (dt.resolution.range[1][0], 1) self.resolution = model.ResolutionVA(resolution, (min_res, max_res), setter=self._setResolution) # 2D binning is like a "small resolution" # Initial binning is minimum binning horizontally, and maximum vertically self._binning = (1, min(dt.binning.range[1][1], dt.resolution.range[1][1])) self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning) self._setBinning(self._binning) # will also update the resolution # TODO: also wrap translation, if it exists? # duplicate every other VA and Event from the detector # that includes required VAs like .pixelSize and .exposureTime for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items(): if not hasattr(self, aname): setattr(self, aname, value) else: logging.debug("skipping duplication of already existing VA '%s'", aname) assert hasattr(self, "pixelSize") if not model.hasVA(self, "exposureTime"): logging.warning("Spectrometer %s has no exposureTime VA", name) sp.position.subscribe(self._onPositionUpdate) self.resolution.subscribe(self._onResBinning) self.binning.subscribe(self._onResBinning) self._updateWavelengthList()