class ForAttrTabata(Device): """ Demonstrate the use of the forwarded attribute """ # PROTECTED REGION ID(ForAttrTabata.class_variable) ENABLED START # # PROTECTED REGION END # // ForAttrTabata.class_variable prepare = attribute(name="prepare", label="prepare", forwarded=True) work = attribute(name="work", label="work", forwarded=True) rest = attribute(name="rest", label="rest", forwarded=True) cycle = attribute(name="cycle", label="cycle", forwarded=True) tabata = attribute(name="tabata", label="tabata", forwarded=True) # --------------- # General methods # --------------- def init_device(self): """Initialises the attributes and properties of the ForAttrTabata.""" Device.init_device(self) # PROTECTED REGION ID(ForAttrTabata.init_device) ENABLED START # # PROTECTED REGION END # // ForAttrTabata.init_device def always_executed_hook(self): """Method always executed before any TANGO command is executed.""" # PROTECTED REGION ID(ForAttrTabata.always_executed_hook) ENABLED START # noqa E501 # PROTECTED REGION END # // ForAttrTabata.always_executed_hook def delete_device(self): """Hook to delete resources allocated in init_device.
class TempHumid(Device): temperature = attribute(name='Temperature', access=AttrWriteType.READ, dtype=float, fget='get_temperature', format='.2f', min_value=-273.15, doc='the measured temperature', unit='C') humidity = attribute(name='Humidity', access=AttrWriteType.READ, dtype=float, fget='get_humidity', format='.2f', doc='the measured humidity', unit='%') def init_device(self): self.info_stream('Trying to connect device to server.') try: Device.init_device(self) self.am2315 = am_driver.AM2315() self.set_state(DevState.ON) self.temp = 0 self.humid = 0 self.info_stream("Connection established.") except: self.error_stream('Connection could not be established.') @DebugIt() @command() def get_data(self): try: #_read_data measures both humidity and temperature self.am2315._read_data() self.temp = self.am2315.temperature self.humid = self.am2315.humidity except: self.error_stream('Data could not be read') @InfoIt(show_ret=True) def get_temperature(self): return self.temp @InfoIt(show_ret=True) def get_humidity(self): return self.humid @ErrorIt() @command() def error_func(self): print('You have made an error.') return None def read_state(self): return self.state()
def attr(**kwargs): name = kwargs['name'].lower() func = ATTR_MAP[name] dtype = kwargs.setdefault('dtype', float) def get(self): value = self.last_values[name] if isinstance(value, Exception): raise value value = self.last_values[name] if value is None: value = float('nan') if dtype == float else '' return value, time.time(), AttrQuality.ATTR_INVALID return value attr = attribute(get, **kwargs) sig = inspect.signature(func) if len(sig.parameters) > 1: @attr.setter def fset(self, value): func(self.cryocon, value) fset.__name__ = 'write_' + name kwargs['fset'] = fset return attr
class DeviceMemLeak(Device): attr1 = attribute(dtype=tango.CmdArgType.DevEncoded) def init_device(self): self.info_stream('In Python init_device method') self.set_state(tango.DevState.ON) self.set_change_event("attr1", True, False) self._stopped = False self._value = pickle.dumps(dict(index=0, value=1.23)) self._fmt = "pickle" def read_attr1(self): return self._fmt, self._value @command def Start(self): self.info_stream('Starting...') self._stopped = False self._job_thread = JobThread(self) self._job_thread.start() @command def Stop(self): self.info_stream('Stopping...') self._stopped = True self._job_thread.join()
def initialize_dynamic_attributes(self): # TODO: setup polling and event filter for d in self.DYN_ATTRS: new_attr = attribute(fget=self.read_general, fset=self.write_general, **d) self.add_attribute(new_attr)
class Clock(Device): @attribute(dtype=float) def time(self): return time.time() gmtime = attribute(dtype=(int, ), max_dim_x=9) def read_gmtime(self): return time.gmtime() @attribute(dtype=Noon) def noon(self): time_struct = time.gmtime(time.time()) return Noon.AM if time_struct.tm_hour < 12 else Noon.PM @command(dtype_in=float, dtype_out=str) def ctime(self, seconds): """ Convert a time in seconds since the Epoch to a string in local time. This is equivalent to asctime(localtime(seconds)). When the time tuple is not present, current time as returned by localtime() is used. """ return time.ctime(seconds) @command(dtype_in=(int, ), dtype_out=float) def mktime(self, tupl): return time.mktime(tupl)
def add_dyn_attr_new(self): attr = attribute( name="dyn_attr", dtype=GoodEnum, access=AttrWriteType.READ_WRITE, fget=self.read, fset=self.write) self.add_attribute(attr)
class Device2(Device): attr1 = attribute() attr2 = attribute() def init_device(self): Device.init_device(self) self.set_change_event("attr1", True, False) self.set_change_event("attr2", True, False) self._attr1 = 0 self._attr2 = 0 def read_attr1(self): return self._attr1 def read_attr2(self): return self._attr2
def add_dyn_attr(self): attr = attribute( name="dyn_attr", dtype=dtype, max_dim_x=10, access=AttrWriteType.READ_WRITE, fget=self.read, fset=self.write) self.add_attribute(attr)
class Worker(Device): def init_device(self): super(Worker, self).init_device() self._is_on = False is_on = attribute( dtype=tango.DevBoolean, access=tango.AttrWriteType.READ_WRITE, ) def read_is_on(self): return self._is_on def write_is_on(self, value): self._is_on = value
class FwdServer(Device): """ Start this server: python FwdServer.py myFwdServer Start the server containing the root attribute. Then using jive select the Attribute properties from myFwdServer Select you forwarded attribute and add the value to __root_att e.g. __root_att -> x/y/z/root_attribute_name Now restart the FwdServer """ def init_device(self): Device.init_device(self) self._current = 0.0 self.set_state(tango.DevState.ON) voltage = attribute(name="voltage", label='Voltage', forwarded=True) @attribute(label='Current', dtype='float') def current(self): return self._current
class Device2(Device): attr2 = attribute() def init_device(self): self.set_state(tango.DevState.ON) def read_attr2(self): return create_device_proxy() def is_cmd2_allowed(self): return self.get_state() == tango.DevState.ON @command() def cmd2(self): self.set_state(tango.DevState.MOVING) def job(device): with tango.EnsureOmniThread(): create_device_proxy() device.set_state(tango.DevState.ON) threading.Thread(target=job, args=(self, )).start()
class Device1(Device): attr1 = attribute() def init_device(self): self.set_state(tango.DevState.ON) def read_attr1(self): return read_double_scalar() def is_cmd1_allowed(self): return self.get_state() == tango.DevState.ON @command() def cmd1(self): self.set_state(tango.DevState.MOVING) def job(device): with tango.EnsureOmniThread(): read_double_scalar() device.set_state(tango.DevState.ON) threading.Thread(target=job, args=(self, )).start()
class DeviceEventGenerator(Device): attr_with_events = attribute() def init_device(self): self.set_change_event("attr_with_events", True, False) self.set_state(tango.DevState.ON) self.emit_events = False self.job_thread = None self.info_stream("DeviceEventGenerator.init_device") def read_attr_with_events(self): return 0.0 def is_Start_allowed(self): return self.get_state() == tango.DevState.ON @command() def Start(self): self.emit_events = True self.set_state(tango.DevState.MOVING) def job(device): while device.emit_events: self.info_stream("Running job") device.push_change_event("attr_with_events", 0.0) time.sleep(0.01) self.job_thread = threading.Thread(target=job, args=(self, )) self.job_thread.start() @command() def Stop(self): self.emit_events = False self.job_thread.join() self.set_state(tango.DevState.ON)
class CspMasterLeafNode(SKABaseDevice): """ The primary responsibility of the CSP Master Leaf node is to monitor the CSP Master and issue control actions during an observation. :Device Properties: CspMasterFQDN: Property to provide FQDN of CSP Master Device :Device Attributes: cspHealthState: Forwarded attribute to provide CSP Master Health State activityMessage: Attribute to provide activity message """ # ----------------- # Device Properties # ----------------- CspMasterFQDN = device_property(dtype="str") # ---------- # Attributes # ---------- activityMessage = attribute( dtype="str", access=AttrWriteType.READ_WRITE, doc="Activity Message", ) cspHealthState = attribute(name="cspHealthState", label="cspHealthState", forwarded=True) # --------------- # General methods # --------------- class InitCommand(SKABaseDevice.InitCommand): """ A class for the TMC CSP Master Leaf Node's init_device() method. """ def do(self): """ Initializes the attributes and properties of the CspMasterLeafNode. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) :raises: DevFailed if error occurs while creating the device proxy for CSP Master or subscribing the evennts. """ super().do() device = self.target device.attr_map = {} this_device = TangoServerHelper.get_instance() this_device.set_tango_class(device) this_device.write_attr("activityMessage", const.STR_CSP_INIT_LEAF_NODE, False) ApiUtil.instance().set_asynch_cb_sub_model( tango.cb_sub_model.PUSH_CALLBACK) log_msg = f"{const.STR_SETTING_CB_MODEL}{ApiUtil.instance().get_asynch_cb_sub_model()}" self.logger.debug(log_msg) this_device.write_attr("activityMessage", const.STR_INIT_SUCCESS, False) self.logger.info(const.STR_INIT_SUCCESS) return (ResultCode.OK, const.STR_INIT_SUCCESS) def always_executed_hook(self): # PROTECTED REGION ID(CspMasterLeafNode.always_executed_hook) ENABLED START # """ Internal construct of TANGO. """ # PROTECTED REGION END # // CspMasterLeafNode.always_executed_hook def delete_device(self): # PROTECTED REGION ID(CspMasterLeafNode.delete_device) ENABLED START # """ Internal construct of TANGO. """ # PROTECTED REGION END # // CspMasterLeafNode.delete_device # ------------------ # Attributes methods # ------------------ def read_activityMessage(self): # PROTECTED REGION ID(CspMasterLeafNode.activityMessage_read) ENABLED START # """ Internal construct of TANGO. Returns the activityMessage. """ return self.attr_map["activityMessage"] # PROTECTED REGION END # // CspMasterLeafNode.activityMessage_read def write_activityMessage(self, value): # PROTECTED REGION ID(CspMasterLeafNode.activityMessage_write) ENABLED START # """Internal construct of TANGO. Sets the activityMessage. """ self.attr_map["activityMessage"] = value # PROTECTED REGION END # // CspMasterLeafNode.activityMessage_write def is_Standby_allowed(self): """ Checks whether this command is allowed to be run in current device state :return: True if this command is allowed to be run in current device state :rtype: boolean :raises: DevFailed if this command is not allowed to be run in current device state """ handler = self.get_command_object("Standby") return handler.check_allowed() @command( dtype_in=("str", ), doc_in= "If the array length is 0, the command applies to the whole\nCSP Element.\nIf the array " "length is > 1, each array element specifies the FQDN of the\nCSP SubElement to put in " "STANDBY mode.", ) @DebugIt() def Standby(self, argin): """ Sets Standby Mode on the CSP Element. """ handler = self.get_command_object("Standby") handler(argin) def init_command_objects(self): """ Initialises the command handlers for commands supported by this device. """ device_data = DeviceData.get_instance() super().init_command_objects() args = (device_data, self.state_model, self.logger) self.register_command_object("Off", Off(*args)) self.register_command_object("On", On(*args)) self.register_command_object("Standby", Standby(*args))
class SKACapability(with_metaclass(DeviceMeta, SKAObsDevice)): """ A Subarray handling device. It exposes the instances of configured capabilities. """ # PROTECTED REGION ID(SKACapability.class_variable) ENABLED START # # PROTECTED REGION END # // SKACapability.class_variable # ----------------- # Device Properties # ----------------- CapType = device_property(dtype='str', ) CapID = device_property(dtype='str', ) subID = device_property(dtype='str', ) # ---------- # Attributes # ---------- activationTime = attribute( dtype='double', unit="s", standard_unit="s", display_unit="s", doc="Time of activation in seconds since Unix epoch.", ) configuredInstances = attribute( dtype='uint16', doc= "Number of instances of this Capability Type currently in use on this subarray.", ) usedComponents = attribute( dtype=('str', ), max_dim_x=100, doc= "A list of components with no. of instances in use on this Capability.", ) # --------------- # General methods # --------------- def init_device(self): SKAObsDevice.init_device(self) self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # PROTECTED REGION ID(SKACapability.init_device) ENABLED START # self._activation_time = 0.0 self._configured_instances = 0 self._used_components = [""] # PROTECTED REGION END # // SKACapability.init_device def always_executed_hook(self): # PROTECTED REGION ID(SKACapability.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKACapability.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKACapability.delete_device) ENABLED START # pass # PROTECTED REGION END # // SKACapability.delete_device # ------------------ # Attributes methods # ------------------ def read_activationTime(self): # PROTECTED REGION ID(SKACapability.activationTime_read) ENABLED START # """ Reads time of activation since Unix epoch. :return: Activation time in seconds """ return self._activation_time # PROTECTED REGION END # // SKACapability.activationTime_read def read_configuredInstances(self): # PROTECTED REGION ID(SKACapability.configuredInstances_read) ENABLED START # """ Reads the number of instances of a capability in the subarray :return: The number of configured instances of a capability in a subarray """ return self._configured_instances # PROTECTED REGION END # // SKACapability.configuredInstances_read def read_usedComponents(self): # PROTECTED REGION ID(SKACapability.usedComponents_read) ENABLED START # """ Reads the list of components with no. of instances in use on this Capability :return: The number of components currently in use. """ return self._used_components # PROTECTED REGION END # // SKACapability.usedComponents_read # -------- # Commands # -------- @command( dtype_in='uint16', doc_in="The number of instances to configure for this Capability.", ) @DebugIt() def ConfigureInstances(self, argin): # PROTECTED REGION ID(SKACapability.ConfigureInstances) ENABLED START # """ This function indicates how many number of instances of the current capacity should to be configured. :param argin: Number of instances to configure :return: None. """ self._configured_instances = argin
class SKAMaster(with_metaclass(DeviceMeta, SKABaseDevice)): """ A master test """ # PROTECTED REGION ID(SKAMaster.class_variable) ENABLED START # # PROTECTED REGION END # // SKAMaster.class_variable # ----------------- # Device Properties # ----------------- # List of maximum number of instances per capability type provided by this Element; # CORRELATOR=512, PSS-BEAMS=4, PST-BEAMS=6, VLBI-BEAMS=4 or for DSH it can be: # BAND-1=1, BAND-2=1, BAND3=0, BAND-4=0, BAND-5=0 (if only bands 1&2 is installed) MaxCapabilities = device_property(dtype=('str', ), ) # ---------- # Attributes # ---------- elementLoggerAddress = attribute( dtype='str', doc="FQDN of Element Logger", ) elementAlarmAddress = attribute( dtype='str', doc="FQDN of Element Alarm Handlers", ) elementTelStateAddress = attribute( dtype='str', doc="FQDN of Element TelState device", ) elementDatabaseAddress = attribute( dtype='str', doc="FQDN of Element Database device", ) maxCapabilities = attribute( dtype=('str', ), max_dim_x=20, doc= "Maximum number of instances of each capability type, e.g. 'CORRELATOR:512', 'PSS-BEAMS:4'.", ) availableCapabilities = attribute( dtype=('str', ), max_dim_x=20, doc="A list of available number of instances of each capability type, " "e.g. 'CORRELATOR:512', 'PSS-BEAMS:4'.", ) # --------------- # General methods # --------------- def init_device(self): SKABaseDevice.init_device(self) # PROTECTED REGION ID(SKAMaster.init_device) ENABLED START # self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # Initialize attribute values. self._element_logger_address = "" self._element_alarm_address = "" self._element_tel_state_address = "" self._element_database_address = "" self._element_alarm_device = "" self._element_tel_state_device = "" self._element_database_device = "" self._max_capabilities = {} if self.MaxCapabilities: for max_capability in self.MaxCapabilities: capability_type, max_capability_instances = max_capability.split( ":") self._max_capabilities[capability_type] = int( max_capability_instances) self._available_capabilities = self._max_capabilities.copy() # PROTECTED REGION END # // SKAMaster.init_device def always_executed_hook(self): # PROTECTED REGION ID(SKAMaster.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKAMaster.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKAMaster.delete_device) ENABLED START # pass # PROTECTED REGION END # // SKAMaster.delete_device # ------------------ # Attributes methods # ------------------ def read_elementLoggerAddress(self): # PROTECTED REGION ID(SKAMaster.elementLoggerAddress_read) ENABLED START # """Reads FQDN of Element Logger device""" return self._element_logger_address # PROTECTED REGION END # // SKAMaster.elementLoggerAddress_read def read_elementAlarmAddress(self): # PROTECTED REGION ID(SKAMaster.elementAlarmAddress_read) ENABLED START # """Reads FQDN of Element Alarm device""" return self._element_alarm_address # PROTECTED REGION END # // SKAMaster.elementAlarmAddress_read def read_elementTelStateAddress(self): # PROTECTED REGION ID(SKAMaster.elementTelStateAddress_read) ENABLED START # """Reads FQDN of Element TelState device""" return self._element_tel_state_address # PROTECTED REGION END # // SKAMaster.elementTelStateAddress_read def read_elementDatabaseAddress(self): # PROTECTED REGION ID(SKAMaster.elementDatabaseAddress_read) ENABLED START # """Reads FQDN of Element Database device""" return self._element_database_address # PROTECTED REGION END # // SKAMaster.elementDatabaseAddress_read def read_maxCapabilities(self): # PROTECTED REGION ID(SKAMaster.maxCapabilities_read) ENABLED START # """Reads maximum number of instances of each capability type""" return convert_dict_to_list(self._max_capabilities) # PROTECTED REGION END # // SKAMaster.maxCapabilities_read def read_availableCapabilities(self): # PROTECTED REGION ID(SKAMaster.availableCapabilities_read) ENABLED START # """Reads list of available number of instances of each capability type""" return convert_dict_to_list(self._available_capabilities) # PROTECTED REGION END # // SKAMaster.availableCapabilities_read # -------- # Commands # -------- @command( dtype_in='DevVarLongStringArray', doc_in="[nrInstances][Capability types]", dtype_out='bool', ) @DebugIt() def isCapabilityAchievable(self, argin): # PROTECTED REGION ID(SKAMaster.isCapabilityAchievable) ENABLED START # """ Checks of provided capabilities can be achieved by the resource(s). :param argin: DevVarLongStringArray. An array consisting pair of [nrInstances]: DevLong. Number of instances of the capability. [Capability types]: DevString. Type of capability. :return: DevBoolean True if capability can be achieved. False if cannot. """ command_name = 'isCapabilityAchievable' capabilities_instances, capability_types = argin validate_input_sizes(command_name, argin) validate_capability_types(command_name, capability_types, list(self._max_capabilities.keys())) for capability_type, capability_instances in zip( capability_types, capabilities_instances): if not self._available_capabilities[ capability_type] >= capability_instances: return False return True
class FspPssSubarray(CspSubElementObsDevice): """ FspPssSubarray TANGO device class for the FspPssSubarray prototype """ # PROTECTED REGION ID(FspPssSubarray.class_variable) ENABLED START # # PROTECTED REGION END # // FspPssSubarray.class_variable # ----------------- # Device Properties # ----------------- SubID = device_property(dtype='uint16') FspID = device_property(dtype='uint16') CbfControllerAddress = device_property( dtype='str', doc="FQDN of CBF Controller", default_value="mid_csp_cbf/controller/main") # TODO: CbfSubarrayAddress prop not being used CbfSubarrayAddress = device_property(dtype='str', doc="FQDN of CBF Subarray") VCC = device_property(dtype=('str', )) # ---------- # Attributes # ---------- receptors = attribute( dtype=('uint16', ), access=AttrWriteType.READ, max_dim_x=197, label="Receptors", doc="List of receptors assigned to subarray", ) searchBeams = attribute( dtype=('str', ), access=AttrWriteType.READ, max_dim_x=192, label="SearchBeams", doc="List of searchBeams assigned to fspsubarray", ) searchWindowID = attribute( dtype='uint16', access=AttrWriteType.READ, max_dim_x=2, label="ID for 300MHz Search Window", doc= "Identifier of the Search Window to be used as input for beamforming on this FSP.", ) searchBeamID = attribute( dtype=('uint16', ), access=AttrWriteType.READ, max_dim_x=192, label="ID for 300MHz Search Window", doc= "Identifier of the Search Window to be used as input for beamforming on this FSP.", ) outputEnable = attribute( dtype='bool', access=AttrWriteType.READ, label="Enable Output", doc="Enable/disable transmission of output products.", ) # --------------- # General methods # --------------- def init_command_objects(self): """ Sets up the command objects """ super().init_command_objects() device_args = (self, self.state_model, self.logger) self.register_command_object("ConfigureScan", self.ConfigureScanCommand(*device_args)) self.register_command_object("GoToIdle", self.GoToIdleCommand(*device_args)) class InitCommand(CspSubElementObsDevice.InitCommand): """ A class for the Vcc's init_device() "command". """ def do(self): """ Stateless hook for device initialisation. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ self.logger.debug("Entering InitCommand()") device = self.target # Make a private copy of the device properties: device._subarray_id = device.SubID device._fsp_id = device.FspID # initialize attribute values device._receptors = [] device._search_beams = [] device._search_window_id = 0 device._search_beam_id = [] device._output_enable = 0 device._scan_id = 0 device._config_id = "" # device proxy for easy reference to CBF Controller device._proxy_cbf_controller = tango.DeviceProxy( device.CbfControllerAddress) device._controller_max_capabilities = dict( pair.split(":") for pair in device._proxy_cbf_controller.get_property( "MaxCapabilities")["MaxCapabilities"]) device._count_vcc = int(device._controller_max_capabilities["VCC"]) device._fqdn_vcc = list(device.VCC)[:device._count_vcc] device._proxies_vcc = [*map(tango.DeviceProxy, device._fqdn_vcc)] message = "FspPssSubarry Init command completed OK" self.logger.info(message) return (ResultCode.OK, message) # PROTECTED REGION END # // FspPssSubarray.init_device def always_executed_hook(self): # PROTECTED REGION ID(FspPssSubarray.always_executed_hook) ENABLED START # """hook before any commands""" pass # PROTECTED REGION END # // FspPssSubarray.always_executed_hook def delete_device(self): # PROTECTED REGION ID(FspPssSubarray.delete_device) ENABLED START # """Set Idle, remove all receptors, turn device OFF""" pass # PROTECTED REGION END # // FspPssSubarray.delete_device # ------------------ # Attributes methods # ------------------ def read_receptors(self): # PROTECTED REGION ID(FspPssSubarray.receptors_read) ENABLED START # """return receptros attribute.(array of int)""" return self._receptors # PROTECTED REGION END # // FspPssSubarray.receptors_read def read_searchBeams(self): # PROTECTED REGION ID(FspPssSubarray.searchBeams_read) ENABLED START # """Return searchBeams attribute (JSON)""" return self._search_beams # PROTECTED REGION END # // FspPssSubarray.searchBeams_read def read_searchBeamID(self): # PROTECTED REGION ID(FspPssSubarray.read_searchBeamID ENABLED START # """REturn list of SearchBeam IDs(array of int). (From searchBeams JSON)""" return self._search_beam_id # PROTECTED REGION END # // FspPssSubarray.read_searchBeamID def read_searchWindowID(self): # PROTECTED REGION ID(CbfSubarrayPssConfig.read_searchWindowID) ENABLED START # """Return searchWindowID attribtue(array of int)""" return self._search_window_id # PROTECTED REGION END # // CbfSubarrayPssConfig.read_searchWindowID def read_outputEnable(self): # PROTECTED REGION ID(CbfSubarrayPssConfig.read_outputEnable) ENABLED START # """Enable/Disable transmission of the output products""" return self._output_enable # PROTECTED REGION END # // CbfSubarrayPssConfig.read_outputEnable # -------- # Commands # -------- def _add_receptors(self, receptorIDs): """add specified receptors to the FSP subarray. Input is array of int.""" self.logger.debug("_AddReceptors") errs = [] # list of error messages receptor_to_vcc = dict( [*map(int, pair.split(":"))] for pair in self._proxy_cbf_controller.receptorToVcc) for receptorID in receptorIDs: try: vccID = receptor_to_vcc[receptorID] subarrayID = self._proxies_vcc[vccID - 1].subarrayMembership # only add receptor if it belongs to the CBF subarray if subarrayID != self._subarray_id: errs.append( "Receptor {} does not belong to subarray {}.".format( str(receptorID), str(self._subarray_id))) else: if receptorID not in self._receptors: self._receptors.append(receptorID) else: # TODO: this is not true if more receptors can be # specified for the same search beam log_msg = "Receptor {} already assigned to current FSP subarray.".format( str(receptorID)) self.logger.warn(log_msg) except KeyError: # invalid receptor ID errs.append("Invalid receptor ID: {}".format(receptorID)) if errs: msg = "\n".join(errs) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "AddReceptors execution", tango.ErrSeverity.ERR) # PROTECTED REGION END # // FspPssSubarray.AddReceptors def _remove_receptors(self, argin): """Remove Receptors. Input is array of int""" self.logger.debug("_remove_receptors") for receptorID in argin: if receptorID in self._receptors: self._receptors.remove(receptorID) else: log_msg = "Receptor {} not assigned to FSP subarray. "\ "Skipping.".format(str(receptorID)) self.logger.warn(log_msg) def _remove_all_receptors(self): self._remove_receptors(self._receptors[:]) # -------- # Commands # -------- class ConfigureScanCommand(CspSubElementObsDevice.ConfigureScanCommand): """ A class for the FspPssSubarray's ConfigureScan() command. """ """Input a serilized JSON object. """ def do(self, argin): """ Stateless hook for ConfigureScan() command functionality. :param argin: The configuration as JSON formatted string :type argin: str :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) :raises: ``CommandError`` if the configuration data validation fails. """ device = self.target argin = json.loads(argin) # Configure receptors. self.logger.debug("_receptors = {}".format(device._receptors)) device._fsp_id = argin["fsp_id"] device._search_window_id = int(argin["search_window_id"]) self.logger.debug("_search_window_id = {}".format( device._search_window_id)) for searchBeam in argin["search_beam"]: if len(searchBeam["receptor_ids"]) != 1: # TODO - to add support for multiple receptors msg = "Currently only 1 receptor per searchBeam is supported" self.logger.error(msg) return (ResultCode.FAILED, msg) device._add_receptors(map(int, searchBeam["receptor_ids"])) self.logger.debug("device._receptors = {}".format( device._receptors)) device._search_beams.append(json.dumps(searchBeam)) device._search_beam_id.append(int( searchBeam["search_beam_id"])) # TODO: _output_enable is not currently set # TODO - possibly move validation of params to # validate_input() # (result_code, msg) = self.validate_input(argin) # TODO result_code = ResultCode.OK # TODO - temp - remove msg = "Configure command completed OK" # TODO temp, remove if result_code == ResultCode.OK: # store the configuration on command success device._last_scan_configuration = argin msg = "Configure command completed OK" return (result_code, msg) def validate_input(self, argin): """ Validate the configuration parameters against allowed values, as needed. :param argin: The JSON formatted string with configuration for the device. :type argin: 'DevString' :return: A tuple containing a return code and a string message. :rtype: (ResultCode, str) """ device = self.target return (ResultCode.OK, "ConfigureScan arguments validation successfull") @command( dtype_in='DevString', doc_in="JSON formatted string with the scan configuration.", dtype_out='DevVarLongStringArray', doc_out= "A tuple containing a return code and a string message indicating status. " "The message is for information purpose only.", ) @DebugIt() def ConfigureScan(self, argin): # PROTECTED REGION ID(Vcc.ConfigureScan) ENABLED START # """ Configure the observing device parameters for the current scan. :param argin: JSON formatted string with the scan configuration. :type argin: 'DevString' :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ command = self.get_command_object("ConfigureScan") (return_code, message) = command(argin) return [[return_code], [message]] class GoToIdleCommand(CspSubElementObsDevice.GoToIdleCommand): """ A class for the FspPssSubarray's GoToIdle command. """ def do(self): """ Stateless hook for GoToIdle() command functionality. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ self.logger.debug("Entering GoToIdleCommand()") device = self.target # initialize attribute values device._search_beams = [] device._search_window_id = 0 device._search_beam_id = [] device._output_enable = 0 device._scan_id = 0 device._config_id = "" device._remove_all_receptors() if device.state_model.obs_state == ObsState.IDLE: return (ResultCode.OK, "GoToIdle command completed OK. Device already IDLE") return (ResultCode.OK, "GoToIdle command completed OK")
class AgilisAGAP(Device): Address = device_property( dtype='int16', ) Port = device_property( dtype='str', ) # some Constants __AXIS_X = 'U' __AXIS_Y = 'V' # Errors from page 64 of the manual __ERROR_NEG_END_OF_RUN = 1 __ERROR_POS_END_OF_RUN = 2 __ERROR_OUT_OF_RANGE = ('G', 'C') # States from page 65 of the manual __STATE_READY = ('32', '33', '34', '35', '36') __STATE_MOVING = ('28', '29') position_x = attribute( label='Position X', dtype='float', access=AttrWriteType.READ_WRITE, format="%4.3f", doc = 'absolute position X' ) position_y = attribute( label='Position Y', dtype='float', access=AttrWriteType.READ_WRITE, format="%4.3f", doc = 'absolute position Y' ) def init_device(self): Device.init_device(self) self.set_state(DevState.INIT) try: self.info_stream("Connecting to AgilisAGAP on port: {:s} ...".format(self.Port)) self.serial = serial.Serial( port = self.Port, baudrate = 921600, bytesize = 8, stopbits = 1, parity = 'N', xonxoff = True, timeout = 0.05) if self.serial.isOpen(): self.serial.close() self.serial.open() self.info_stream("Success!") self.info_stream('Connection established:\n{:s}\n{:s}'.format(self.query('ID?'), self.query('VE?'))) except: self.error_stream("Cannot connect!") self.set_state(DevState.OFF) self.set_state(DevState.ON) def delete_device(self): if self.serial.isOpen(): self.serial.close() self.info_stream('Connection closed for port {:s}'.format(self.Port)) def always_executed_hook(self): res = self.query('TS?') if (res != ''): err = int(res[:4],16) state = res[4:] if (state in self.__STATE_MOVING): self.set_status('Device is MOVING') self.set_state(DevState.MOVING) elif (state in self.__STATE_READY): self.set_status('Device is ON') self.set_state(DevState.ON) else: self.set_status('Device is UNKOWN') self.set_state(DevState.UNKNOWN) def read_position_x(self): return float(self.query('TP' + self.__AXIS_X + '?')) def write_position_x(self, value): self.query('PA' + self.__AXIS_X + str(value)) err = self.get_cmd_error_string() if err in self.__ERROR_OUT_OF_RANGE: Except.throw_exception('x position out of range', 'x position out of range', 'write_position_x') else: self.set_state(DevState.MOVING) def read_position_y(self): return float(self.query('TP' + self.__AXIS_Y + '?')) def write_position_y(self, value): self.query('PA' + self.__AXIS_Y + str(value)) err = self.get_cmd_error_string() if err in self.__ERROR_OUT_OF_RANGE: Except.throw_exception('y position out of range', 'y position out of range', 'write_position_y') else: self.set_state(DevState.MOVING) @command def Stop(self): self.query('ST') @command() def Reset(self): self.query('RS') def query(self, cmd): prefix = str(self.Address) + cmd[:-1] self.send_cmd(cmd) answer = self.serial.readline().decode('utf-8') if answer.startswith(prefix): answer = answer[len(prefix):].strip() else: answer = '' return answer def send_cmd(self, cmd): cmd = str(self.Address) + cmd + '\r\n' self.serial.flushInput() self.serial.flushOutput() self.serial.write(cmd.encode('utf-8')) self.serial.flush() def get_cmd_error_string(self): error = self.query('TE?') return error.strip()
class NIUSB6501(Device): port3 = attribute(fget='read_port3', fset='write_port3', name='Port_1_3', access=AttrWriteType.READ_WRITE, dtype=bool) port4 = attribute(fget='read_port4', fset='write_port4', name='Port_1_4', access=AttrWriteType.READ_WRITE, dtype=bool) port5 = attribute(fget='read_port5', fset='write_port5', name='Port_1_5', access=AttrWriteType.READ_WRITE, dtype=bool) port6 = attribute(fget='read_port6', fset='write_port6', name='Port_1_6', access=AttrWriteType.READ_WRITE, dtype=bool) port7 = attribute(fget='read_port7', fset='write_port7', name='Port_1_7', access=AttrWriteType.READ_WRITE, dtype=bool) gatetime = attribute(fget='read_gatetime', fset='write_gatetime', name='Gatetime', access=AttrWriteType.READ_WRITE, dtype=float, min_value=0) act_port = attribute(fget='read_act_port', fset='write_act_port', name='ActivePort', access=AttrWriteType.READ_WRITE, dtype=int, min_warning=2, min_value=0, max_value=7) frequency = attribute(fget='read_frequency', fset='write_frequency', name='Frequency', access=AttrWriteType.READ_WRITE, dtype=float, min_value=1) npulses = attribute(name='npulses', access=AttrWriteType.READ_WRITE, dtype=int, min_value=1) def init_device(self): self.info_stream('Trying to establish connection') try: Device.init_device(self) self.dev = ni.get_adapter() self.set_state(DevState.ON) self.dev.set_io_mode(0b00000000, 0b01111111, 0b00000000) #only ports 1.0-1.7 are writeable self.__ports = {3: False, 4: False, 5: False, 6: False, 7: False} self.__active = '0b00000000' self.dev.write_port(1, int(self.__active, 2)) self.info_stream('Connection established.') self.__gatetime = 1 self.__act_port = 3 self.__freq = 1 self.__npulses = 1 self.__busy = False except: if not self.dev: self.error_stream('Connection could not be established') self.set_state(DevState.FAULT) else: #Connection already running (info_stream gets called in case #the init method is called more than once) self.info_stream( 'Connection has already been established in a prior run.') def read_port3(self): return self.__ports[3] def write_port3(self, state): self.change_port(3, state) def read_port4(self): return self.__ports[4] def write_port4(self, state): self.change_port(4, state) def read_port5(self): return self.__ports[5] def write_port5(self, state): self.change_port(5, state) def read_port6(self): return self.__ports[6] def write_port6(self, state): self.change_port(6, state) def read_port7(self): return self.__ports[7] def write_port7(self, state): self.change_port(7, state) def read_npulses(self): return self.__npulses def write_npulses(self, val): self.__npulses = val def read_gatetime(self): return self.__gatetime def write_gatetime(self, value): self.__gatetime = value def read_act_port(self): return self.__act_port def write_act_port(self, value): self.__act_port = value def read_frequency(self): return self.__freq def write_frequency(self, value): self.__freq = value @DebugIt(show_args=True, show_ret=True) def change_active(self, port, state): new = -1 - port self.__active = list(self.__active) if state: self.__active[new] = '1' if not state: self.__active[new] = '0' self.__active = ''.join(self.__active) bitmap = self.__active return bitmap @DebugIt() def change_port(self, port, state): self.change_active(port, state) self.dev.write_port(1, int(self.__active, 2)) self.__ports[port] = state self.debug_stream('changed port' + str(port) + ' to ' + str(state)) #using the gate_timer for longer than 3s command will create a timeout error #the command will still be executed as desired, but a warning will be sent @DebugIt() @command() def gate_timer(self): start = time.time() self.__busy = True self.change_port(self.__act_port, True) self.info_stream('Port{} active'.format(self.__act_port)) time.sleep(self.__gatetime) self.change_port(self.__act_port, False) self.__busy = False self.info_stream('Port{} inactive'.format(self.__act_port)) self.debug_stream('Actual duration of gate: ' + str(time.time() - start)) @command() def train_async(self): if not self.__busy: t = Thread(target=self.pulsetrain) t.daemon = True t.start() def pulsetrain(self): bitmap_on = int(self.change_active(self.__act_port, True), 2) bitmap_off = int(self.change_active(self.__act_port, False), 2) self.debug_stream(str(bitmap_on)) self.debug_stream(str(bitmap_off)) # start = time.time() # while time.time() <= start+self.__gatetime: # hits += 1 # self.dev.write_port(1,bitmap_on) # time.sleep(1/(self.__freq)) # self.dev.write_port(1,bitmap_off) # time.sleep(1/(self.__freq)) # act_dur = time.time()-start self.set_state(DevState.RUNNING) self.__busy = True t0 = time.time() for i in range(self.__npulses): self.dev.write_port(1, bitmap_on) time.sleep(1 / (2 * self.__freq)) self.dev.write_port(1, bitmap_off) time.sleep(1 / (2 * self.__freq)) duration = time.time() - t0 print(f'elapsed: {duration:.2f}', file=self.log_debug) self.set_state(DevState.ON) self.__busy = False
class TemplateDeviceServer(Device): ''' This docstring should describe your Tango Class and optionally what it depends on (drivers etc). ''' # ------ Attributes ------ # humidity = attribute(label='Humidity', dtype=float, access=AttrWriteType.READ, doc='Example for an attribute that can only be read.') # optionally use fget/fset to point to read and write functions. # Default is "read_temperature"/"write_temperature". # Added some optional attribute properties. temperature = attribute(label='Temperature', fget='get_temperature', dtype=float, access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, min_value=-273.15, min_alarm=-100, max_alarm=100, min_warning=-50, max_warning=50, unit='C', format="8.4f", doc='Attribute that can be read/written.') # ------ Device Properties ------ # # device_properties will be set once per family-member and usually - # contain serial numbers or a certain port-ID that needs to be set once - # and will not change while the server is running. port = device_property(dtype=int, default_value=10000) # ------ default functions that are inherited from parent "Device" ------ # def init_device(self): Device.init_device(self) self.info_stream('Connection established') # prints this line while - # in logging mode "info" or lower self.set_state(DevState.ON) # here you could initiate first contact to the hardware (driver) self.__temp = 0 # declaring values for the attributes if needed self.__humid = 0 def delete_device(self): self.set_state(DevState.OFF) self.error_stream('A device was deleted!') # prints this line while - # in logging mode "error" or lower. # define what is executed when Tango checks for the state. # Here you could inquire the state of the hardware and not just - # (as it is in default) of the TDS. # Default returns state but also sets state from ON to ALARM if - # some attribute alarm limits are surpassed. def dev_state(self): # possible pseudo code: # if hardware-state and TDS-state is ON: # return DevState.ON # else: # return DevState.FAULT return DevState def always_executed_hook(self): # a method that is executed continuously and by default does nothing. # if you want smth done polled/continuously, put it in this method. # check connection to hardware or whether status is acceptable etc. pass # ------ Read/Write functions ------ # def read_humidity(self): # this is default to read humidity return self.__humid # returns the value of the "humidity" attr. def get_temperature(self): # this was set by fget in attribute declaration return self.__temp def write_temperature(self, value): # possibly execute some function here to talk to the hardware - # (e.g. set temperature with a thermostat) self.__temp = value # update the declared server value of the attr. # ------ Internal Methods ------ # # method that works with multiple input parameters only "inside" this code def internal_method(self, param1, param2): # do something with param1, param2 pass # ------ COMMANDS ------ # @DebugIt() # let the execution of this command be logged in debugging mode @command() # make method executable through the client - # (as opposed to just callable inside this code) def external_method(self, param): # this kind of method only allows one input parameter pass # more examples of externally executable methods @command() def turn_off(self): self.set_state(DevState.OFF) @command() def turn_on(self): self.set_state(DevState.ON)
class Timer(Device): """ A Timer countdown device composed by minutes and seconds. **Properties:** - Device Property minutesCounter - device name for the minutes counter - Type:'DevString' secondsCounter - Device name for the seconds counter - Type:'DevString' sleep_time - Sleep time - Type:'DevFloat' """ # PROTECTED REGION ID(Timer.class_variable) ENABLED START # def event_subscription(self): self._dev_factory.get_device(self.secondsCounter).subscribe_event( "value", tango.EventType.CHANGE_EVENT, self.handle_event, stateless=True, ) self._dev_factory.get_device(self.minutesCounter).subscribe_event( "value", tango.EventType.CHANGE_EVENT, self.handle_event, stateless=True, ) def internal_reset_counters(self): with self._lock: self._dev_factory.get_device(self.minutesCounter).CounterReset( self._start_minutes) self._dev_factory.get_device(self.secondsCounter).CounterReset( self._start_seconds) def step_loop(self): with tango.EnsureOmniThread(): while not self.get_state() == tango.DevState.OFF: # import debugpy; debugpy.debug_this_thread() with self._lock: device = self._dev_factory.get_device(self.secondsCounter) # self.logger.debug("SECONDS %s", device.value) device.decrement() time.sleep(self.sleep_time) def handle_event(self, evt): if evt.err: error = evt.errors[0] self.logger.error("%s %s", error.reason, error.desc) return if evt.device.value <= 0 and (not self.get_state() == tango.DevState.OFF): self.logger.debug("HANDLE EVENT %s %s", evt.device.dev_name(), evt.device.value) if evt.device.dev_name() == self.secondsCounter: if self.get_state() == DevState.ALARM: with self._lock: self.set_state(DevState.OFF) else: device = self._dev_factory.get_device(self.minutesCounter) with self._lock: device.decrement() self._dev_factory.get_device( self.secondsCounter).CounterReset(59) # self.logger.debug("MINUTES %s", device.value) else: with self._lock: self.set_state(DevState.ALARM) def is_Start_allowed(self): return self.get_state() == tango.DevState.OFF def is_Stop_allowed(self): return not self.get_state() == tango.DevState.OFF def is_ResetCounters_allowed(self): return self.get_state() == tango.DevState.OFF # PROTECTED REGION END # // Timer.class_variable # ----------------- # Device Properties # ----------------- minutesCounter = device_property(dtype="DevString", default_value="test/counter/minutes") secondsCounter = device_property(dtype="DevString", default_value="test/counter/seconds") sleep_time = device_property(dtype="DevFloat", default_value=1) # ---------- # Attributes # ---------- start_minutes = attribute( dtype="DevShort", access=AttrWriteType.READ_WRITE, ) start_seconds = attribute( dtype="DevShort", access=AttrWriteType.READ_WRITE, ) # --------------- # General methods # --------------- def init_device(self): """Initialises the attributes and properties of the Timer.""" Device.init_device(self) # PROTECTED REGION ID(Timer.init_device) ENABLED START # self.logger = logging.getLogger(__name__) self._lock = threading.Lock() self._dev_factory = DevFactory() self._start_minutes = 0 self._start_seconds = 0 self.subscribed = False self.set_state(DevState.OFF) self.worker_thread = None # PROTECTED REGION END # // Timer.init_device def always_executed_hook(self): """Method always executed before any TANGO command is executed.""" # PROTECTED REGION ID(Timer.always_executed_hook) ENABLED START # if not self.subscribed: self.event_subscription() self.subscribed = True self.internal_reset_counters() # PROTECTED REGION END # // Timer.always_executed_hook def delete_device(self): """Hook to delete resources allocated in init_device. This method allows for any memory or other resources allocated in the init_device method to be released. This method is called by the device destructor and by the device Init command. """ # PROTECTED REGION ID(Timer.delete_device) ENABLED START # # PROTECTED REGION END # // Timer.delete_device # ------------------ # Attributes methods # ------------------ def read_start_minutes(self): # PROTECTED REGION ID(Timer.start_minutes_read) ENABLED START # """Return the start_minutes attribute.""" return self._start_minutes # PROTECTED REGION END # // Timer.start_minutes_read def write_start_minutes(self, value): # PROTECTED REGION ID(Timer.start_minutes_write) ENABLED START # """Set the start_minutes attribute.""" if value < 1: raise Exception("only positive values!") self._start_minutes = value # PROTECTED REGION END # // Timer.start_minutes_write def read_start_seconds(self): # PROTECTED REGION ID(Timer.start_seconds_read) ENABLED START # """Return the start_seconds attribute.""" return self._start_seconds # PROTECTED REGION END # // Timer.start_seconds_read def write_start_seconds(self, value): # PROTECTED REGION ID(Timer.start_seconds_write) ENABLED START # """Set the start_seconds attribute.""" if value < 1: raise Exception("only positive values!") self._start_seconds = value # PROTECTED REGION END # // Timer.start_seconds_write # -------- # Commands # -------- @command() @DebugIt() def ResetCounters(self): # PROTECTED REGION ID(Timer.ResetCounters) ENABLED START # """ Reset the counters minutes and seconds :return:None """ self.internal_reset_counters() # PROTECTED REGION END # // Timer.ResetCounters @command() @DebugIt() def Start(self): # PROTECTED REGION ID(Timer.Start) ENABLED START # """ Set the device state to ON and start execution :return:None """ self.set_state(tango.DevState.RUNNING) self.worker_thread = threading.Thread(target=self.step_loop) self.worker_thread.start() # PROTECTED REGION END # // Timer.Start @command() @DebugIt() def Stop(self): # PROTECTED REGION ID(Timer.Stop) ENABLED START # """ set the device state to OFF and stop the execution :return:None """ self.set_state(DevState.OFF) self.worker_thread.join()
class RaspberryPiIO(Device): #attributes image = attribute(dtype=((int, ), ), max_dim_x=2000, max_dim_y=2000) pin3_voltage = attribute(label="PIN_3 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_3 voltage", fget="get_pin3_voltage", fset="set_pin3_voltage", fisallowed="is_voltage_allowed") pin3_output = attribute(label="PIN_3 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_3 output", fget="get_pin3_output", fset="set_pin3_output", fisallowed="is_output_allowed") pin5_voltage = attribute(label="PIN_5 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_5 voltage", fget="get_pin5_voltage", fset="set_pin5_voltage", fisallowed="is_voltage_allowed") pin5_output = attribute(label="PIN_5 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_5 output", fget="get_pin5_output", fset="set_pin5_output", fisallowed="is_output_allowed") pin7_voltage = attribute(label="PIN_7 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_7 voltage", fget="get_pin7_voltage", fset="set_pin7_voltage", fisallowed="is_voltage_allowed") pin7_output = attribute(label="PIN_7 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_7 output", fget="get_pin7_output", fset="set_pin7_output", fisallowed="is_output_allowed") pin8_voltage = attribute(label="PIN_8 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_8 voltage", fget="get_pin8_voltage", fset="set_pin8_voltage", fisallowed="is_voltage_allowed") pin8_output = attribute(label="PIN_8 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_8 output", fget="get_pin8_output", fset="set_pin8_output", fisallowed="is_output_allowed") pin10_voltage = attribute(label="PIN_10 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_10 voltage", fget="get_pin10_voltage", fset="set_pin10_voltage", fisallowed="is_voltage_allowed") pin10_output = attribute(label="PIN_10 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_10 output", fget="get_pin10_output", fset="set_pin10_output", fisallowed="is_output_allowed") pin11_voltage = attribute(label="PIN_11 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_11 voltage", fget="get_pin11_voltage", fset="set_pin11_voltage", fisallowed="is_voltage_allowed") pin11_output = attribute(label="PIN_11 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_11 output", fget="get_pin11_output", fset="set_pin11_output", fisallowed="is_output_allowed") pin12_voltage = attribute(label="PIN_12 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_12 voltage", fget="get_pin12_voltage", fset="set_pin12_voltage", fisallowed="is_voltage_allowed") pin12_output = attribute(label="PIN_12 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_12 output", fget="get_pin12_output", fset="set_pin12_output", fisallowed="is_output_allowed") pin13_voltage = attribute(label="PIN_13 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_13 voltage", fget="get_pin13_voltage", fset="set_pin13_voltage", fisallowed="is_voltage_allowed") pin13_output = attribute(label="PIN_13 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_13 output", fget="get_pin13_output", fset="set_pin13_output", fisallowed="is_output_allowed") pin15_voltage = attribute(label="PIN_15 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_15 voltage", fget="get_pin15_voltage", fset="set_pin15_voltage", fisallowed="is_voltage_allowed") pin15_output = attribute(label="PIN_15 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_15 output", fget="get_pin15_output", fset="set_pin15_output", fisallowed="is_output_allowed") pin16_voltage = attribute(label="PIN_16 voltage", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_16 voltage", fget="get_pin16_voltage", fset="set_pin16_voltage", fisallowed="is_voltage_allowed") pin16_output = attribute(label="PIN_16 output", dtype=bool, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ_WRITE, doc="PIN_16 output", fget="get_pin16_output", fset="set_pin16_output", fisallowed="is_output_allowed") Host = device_property(dtype=str) Port = device_property(dtype=int, default_value=9788) def init_device(self): Device.init_device(self) self.raspberry = Raspberry(self.Host) #Event flags #self.set_change_event('pin3_voltage', True, True) #self.set_change_event('pin5_voltage', True, True) #self.set_change_event('pin7_voltage', True, True) #self.set_change_event('pin8_voltage', True, True) #self.set_change_event('pin10_voltage', True, True) #self.set_change_event('pin11_voltage', True, True) #self.set_change_event('pin12_voltage', True, True) #self.set_change_event('pin13_voltage', True, True) #self.set_change_event('pin15_voltage', True, True) #self.set_change_event('pin16_voltage', True, True) #self.set_change_event('pin3_output', True, True) #self.set_change_event('pin5_output', True, True) #self.set_change_event('pin7_output', True, True) #self.set_change_event('pin8_output', True, True) #self.set_change_event('pin10_output', True, True) #self.set_change_event('pin11_output', True, True) #self.set_change_event('pin12_output', True, True) #self.set_change_event('pin13_output', True, True) #self.set_change_event('pin15_output', True, True) #self.set_change_event('pin16_output', True, True) #No error decorator for the init function try: self.raspberry.connect_to_pi() url = 'http://' + self.Host + ':5000/stream' response = requests.get(url, stream=True) self.reader = Reader(response) self.frame = self.get_frame() self.set_state(DevState.ON) except (BrokenPipeError, ConnectionRefusedError, ConnectionError, socket.timeout) as connectionerror: self.set_state(DevState.FAULT) self.debug_stream('Unable to connect to Raspberry Pi TCP/IP' + ' server.') def get_frame(self): for event in self.reader.events(): try: event = event.strip(b'\r\n\r\n') decoded = np.asarray(bytearray(event), dtype=np.uint8) decoded = decoded.reshape(480, 640) yield decoded except Exception as e: # print("Missing frame") # print(e) pass def read_image(self): l = next(self.frame) return l def delete_device(self): self.raspberry.disconnect_from_pi() self.raspberry = None #Read and write states currently have the same condition def is_voltage_allowed(self, request): if request == AttReqType.READ_REQ: return (self.get_state() == DevState.ON) if request == AttReqType.WRITE_REQ: return (self.get_state() == DevState.ON) def is_output_allowed(self, request): return self.get_state() == DevState.ON def set_voltage(self, value, pin, output): if not output: raise ValueError("Pin must be setup as an output first") else: request = self.raspberry.setvoltage(pin, value) if not request: raise ValueError("Pin must be setup as an output first") #gpio3 @catch_connection_error def get_pin3_voltage(self): self.__pin3_voltage = self.raspberry.readvoltage(3) return self.__pin3_voltage @catch_connection_error def set_pin3_voltage(self, value): self.set_voltage(value, 3, self.__pin3_output) @catch_connection_error def get_pin3_output(self): self.__pin3_output = self.raspberry.readoutput(3) return self.__pin3_output @catch_connection_error def set_pin3_output(self, value): self.raspberry.setoutput(3, value) #gpio5 @catch_connection_error def get_pin5_voltage(self): self.__pin5_voltage = self.raspberry.readvoltage(5) return self.__pin5_voltage @catch_connection_error def set_pin5_voltage(self, value): self.set_voltage(value, 5, self.__pin5_output) @catch_connection_error def get_pin5_output(self): self.__pin5_output = self.raspberry.readoutput(5) return self.__pin5_output @catch_connection_error def set_pin5_output(self, value): self.raspberry.setoutput(5, value) #gpio7 @catch_connection_error def get_pin7_voltage(self): self.__pin7_voltage = self.raspberry.readvoltage(7) return self.__pin7_voltage @catch_connection_error def set_pin7_voltage(self, value): self.set_voltage(value, 7, self.__pin7_output) @catch_connection_error def get_pin7_output(self): self.__pin7_output = self.raspberry.readoutput(7) return self.__pin7_output @catch_connection_error def set_pin7_output(self, value): self.raspberry.setoutput(7, value) #gpio8 @catch_connection_error def get_pin8_voltage(self): self.__pin8_voltage = self.raspberry.readvoltage(8) return self.__pin8_voltage @catch_connection_error def set_pin8_voltage(self, value): self.set_voltage(value, 8, self.__pin8_output) @catch_connection_error def get_pin8_output(self): self.__pin8_output = self.raspberry.readoutput(8) return self.__pin8_output @catch_connection_error def set_pin8_output(self, value): self.raspberry.setoutput(8, value) #gpio10 @catch_connection_error def get_pin10_voltage(self): self.__pin10_voltage = self.raspberry.readvoltage(10) return self.__pin10_voltage @catch_connection_error def set_pin10_voltage(self, value): self.set_voltage(value, 10, self.__pin10_output) @catch_connection_error def get_pin10_output(self): self.__pin10_output = self.raspberry.readoutput(10) return self.__pin10_output @catch_connection_error def set_pin10_output(self, value): self.raspberry.setoutput(10, value) #gpio11 @catch_connection_error def get_pin11_voltage(self): self.__pin11_voltage = self.raspberry.readvoltage(11) return self.__pin11_voltage @catch_connection_error def set_pin11_voltage(self, value): self.set_voltage(value, 11, self.__pin11_output) @catch_connection_error def get_pin11_output(self): self.__pin11_output = self.raspberry.readoutput(11) return self.__pin11_output @catch_connection_error def set_pin11_output(self, value): self.raspberry.setoutput(11, value) #gpio12 @catch_connection_error def get_pin12_voltage(self): self.__pin12_voltage = self.raspberry.readvoltage(12) return self.__pin12_voltage @catch_connection_error def set_pin12_voltage(self, value): self.set_voltage(value, 12, self.__pin12_output) @catch_connection_error def get_pin12_output(self): self.__pin12_output = self.raspberry.readoutput(12) return self.__pin12_output @catch_connection_error def set_pin12_output(self, value): self.raspberry.setoutput(12, value) #gpio13 @catch_connection_error def get_pin13_voltage(self): self.__pin13_voltage = self.raspberry.readvoltage(13) return self.__pin13_voltage @catch_connection_error def set_pin13_voltage(self, value): self.set_voltage(value, 13, self.__pin13_output) @catch_connection_error def get_pin13_output(self): self.__pin13_output = self.raspberry.readoutput(13) return self.__pin13_output @catch_connection_error def set_pin13_output(self, value): self.raspberry.setoutput(13, value) #gpio15 @catch_connection_error def get_pin15_voltage(self): self.__pin15_voltage = self.raspberry.readvoltage(15) return self.__pin15_voltage @catch_connection_error def set_pin15_voltage(self, value): self.set_voltage(value, 15, self.__pin15_output) @catch_connection_error def get_pin15_output(self): self.__pin15_output = self.raspberry.readoutput(15) return self.__pin15_output @catch_connection_error def set_pin15_output(self, value): self.raspberry.setoutput(15, value) #gpio16 @catch_connection_error def get_pin16_voltage(self): self.__pin16_voltage = self.raspberry.readvoltage(16) return self.__pin16_voltage @catch_connection_error def set_pin16_voltage(self, value): self.set_voltage(value, 16, self.__pin16_output) @catch_connection_error def get_pin16_output(self): self.__pin16_output = self.raspberry.readoutput(16) return self.__pin16_output @catch_connection_error def set_pin16_output(self, value): self.raspberry.setoutput(16, value) #End of gpio's def is_TurnOff_allowed(self): return self.get_state() == DevState.ON @command def TurnOff(self): self.raspberry.turnoff() self.set_state(DevState.OFF) def is_ResetAll_allowed(self): return self.get_state() == DevState.ON @command def ResetAll(self): self.raspberry.resetall()
class PowerSupply(Device): """ Example power supply device from the PyTango documentation. """ # green_mode = GreenMode.Asyncio voltage = attribute( label="Voltage", dtype=float, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ, unit="V", format="8.4f", doc="the power supply voltage", ) current = attribute( label="Current", dtype=float, display_level=DispLevel.EXPERT, access=AttrWriteType.READ_WRITE, unit="A", format="8.4f", min_value=0.0, max_value=8.5, min_alarm=0.1, max_alarm=8.4, min_warning=0.5, max_warning=8.0, fget="get_current", fset="set_current", doc="the power supply current", ) noise = attribute(label="Noise", dtype=((int, ), ), max_dim_x=1024, max_dim_y=1024) host = device_property(dtype=str) port = device_property(dtype=int, default_value=9788) def __init__(self, device_class, device_name): super().__init__(device_class, device_name) self.__current = 0.0 def init_device(self): """Initialise device""" Device.init_device(self) self.set_current(0.0) self.set_state(DevState.STANDBY) def read_voltage(self): """Read voltage""" self.info_stream("read_voltage(%s, %d)", self.host, self.port) return 240, time.time(), AttrQuality.ATTR_VALID def get_current(self): """Get the current""" return self.__current def set_current(self, current): """Set the current""" self.__current = current def read_info(self): """Get device information""" return "Information", dict(manufacturer="Tango", model="PS2000", version_number=123) @DebugIt() def read_noise(self): """Get a matrix of random noise""" return numpy.random.random_integers(1000, size=(100, 100)) @command def turn_on(self): """Turn the device on""" # turn on the actual power supply here self.set_state(DevState.ON) @command def turn_off(self): """Turn the device off""" # turn off the actual power supply here self.set_state(DevState.OFF) @command( dtype_in=float, doc_in="Ramp target current", dtype_out=bool, doc_out="True if ramping went well, " "False otherwise", ) def ramp(self, target_current): """Ramp voltage to the target current""" # should do the ramping. This doesn't. self.set_current(target_current) return True
class ADAM6224(Device): """ ADAM6224 It is a definition of a class used to control ADAM-6224 controller via Modbus TCP. Device contains 4-ch Isolated Analog Output and 4-ch Digital Input. For each AO there are associated attributes: * AnalogOutput(double) - contains present output value of the channel (V or mA), it is adjustable * SafetyValue(double) - contains present safety value of the channel (V or mA), it is adjustable * StartupValue(double) - contains present startup value of the channel (V or mA), it is adjustable * Status(string) - contains present status of the channel, available: Fail to provide AO Value, No Output Current, Zero/Span Calibration Error, DI triggered to Safety Value, DI triggered to Startup Value, AO triggered to Fail Safety Value * CodeType(string) - contains present type of output of the channel, available: 0-20mA, 4-20mA, 0-10V, 0-5V, +-10V, +-5V For each DI there are associated attributes: * DigitalInput(bool) - contains present input value of the channel * EventStatus(string) - contains present status of the channel, available: Unreliable DI value (UART Timeout), Safety Value triggered, Startup Value triggered """ __metaclass__ = DeviceMeta connected_ADAM = 0.0 digital_input_values = [False, False, False, False] digital_input_events = [0, 0, 0, 0] analog_output_values = [0, 0, 0, 0] analog_output_statuses = [0, 0, 0, 0, 0, 0, 0, 0] analog_output_types = [0, 0, 0, 0] analog_output_startup_values = [0, 0, 0, 0] analog_output_safety_values = [0, 0, 0, 0] type_to_code_dict = { '0-20mA': int("0182", 16), '4-20mA': int("0180", 16), '0-10V': int("0148", 16), '0-5V': int("0147", 16), '+-10V': int("0143", 16), '+-5V': int("0142", 16) } code_to_type_dict = {v: k for k, v in type_to_code_dict.iteritems()} event_status_dictionary = { int(1): 'Unreliable DI value (UART Timeout)', int(2): 'Safety Value triggered', int(4): 'Startup Value triggered', int(0): ' ' } status_dict_1 = { int(1): 'Fail to provide AO Value', int(8): 'No Output Current', int(512): 'Zero/Span Calibration Error', int(0): ' ' } status_dict_2 = { int(1): 'DI triggered to Safety Value', int(2): 'DI triggered to Startup Value', int(4): 'AO triggered to Fail Safety Value', int(0): ' ' } # ---------------- # Class Properties # ---------------- # ----------------- # Device Properties # ----------------- DeviceAddress = device_property(dtype='str', default_value="192.168.120.55", doc="An IP address of device") # ------------------ # Attributes methods # ------------------ # -------------------- # DigitalInput method # -------------------- def read_DigitalInput(self, channel): return self.digital_input_values[channel] # -------------------- # EventStatus method # -------------------- def read_EventStatus(self, channel): return self.event_status_dictionary[self.digital_input_events[channel]] # -------------------- # Status method # -------------------- def read_Status(self, channel): reg = 2 * channel return self.status_dict_1[self.analog_output_statuses[reg]] + \ ' ' + \ self.status_dict_2[self.analog_output_statuses[reg + 1]] # -------------------- # AnalogOutput method # -------------------- def read_AnalogOutput(self, channel): return self.encode_value(self.analog_output_values[channel], channel) def write_AnalogOutput(self, channel, value): self.connected_ADAM.write_register( channel, self.decode_value(value, channel, 0)) # -------------------- # SafetyValue method # -------------------- def read_SafetyValue(self, channel): return self.encode_value(self.analog_output_safety_values[channel], channel) def write_SafetyValue(self, value, channel): reg = 410 + channel self.connected_ADAM.write_register( reg, self.decode_value(value, channel, 1)) # -------------------- # StartupValue method # -------------------- def read_StartupValue(self, channel): return self.encode_value(self.analog_output_startup_values[channel], channel) def write_StartupValue(self, value, channel): reg = 400 + channel self.connected_ADAM.write_register( reg, self.decode_value(value, channel, 2)) # -------------------- # TypeCode method # -------------------- def read_TypeCode(self, channel): return self.decode_type_code(channel) def write_TypeCode(self, value, channel): reg = 200 + channel self.connected_ADAM.write_register(reg, self.encode_type_code(value)) # ---------- # Attributes # ---------- DigitalInput_0 = attribute(dtype='bool', access=AttrWriteType.READ, doc="Bool value at channel 0 of Digital Input") DigitalInput_1 = attribute(dtype='bool', access=AttrWriteType.READ, doc="Bool value at channel 1 of Digital Input") DigitalInput_2 = attribute(dtype='bool', access=AttrWriteType.READ, doc="Bool value at channel 2 of Digital Input") DigitalInput_3 = attribute(dtype='bool', access=AttrWriteType.READ, doc="Bool value at channel 3 of Digital Input") AnalogOutput_0 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Value at channel 0 of Analog Output") AnalogOutput_1 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Value at channel 1 of Analog Output") AnalogOutput_2 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Value at channel 2 of Analog Output") AnalogOutput_3 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Value at channel 3 of Analog Output") SafetyValue_0 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Safety Value of channel 0 of Analog Output") SafetyValue_1 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Safety Value of channel 1 of Analog Output") SafetyValue_2 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Safety Value of channel 2 of Analog Output") SafetyValue_3 = attribute(dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Safety Value of channel 3 of Analog Output") StartupValue_0 = attribute( dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Startup Value of channel 0 of Analog Output") StartupValue_1 = attribute( dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Startup Value of channel 1 of Analog Output") StartupValue_2 = attribute( dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Startup Value of channel 2 of Analog Output") StartupValue_3 = attribute( dtype=float, access=AttrWriteType.READ_WRITE, format='%2.4f', doc="Startup Value of channel 3 of Analog Output") TypeCode_0 = attribute(dtype='str', access=AttrWriteType.READ_WRITE, doc="Type of output at channel 0 of Analog Output") TypeCode_1 = attribute(dtype='str', access=AttrWriteType.READ_WRITE, doc="Type of output at channel 1 of Analog Output") TypeCode_2 = attribute(dtype='str', access=AttrWriteType.READ_WRITE, doc="Type of output at channel 2 of Analog Output") TypeCode_3 = attribute(dtype='str', access=AttrWriteType.READ_WRITE, doc="Type of output at channel 3 of Analog Output") Status_0 = attribute(dtype='str', access=AttrWriteType.READ, doc="Status of channel 0 of Analog Output") Status_1 = attribute(dtype='str', access=AttrWriteType.READ, doc="Status of channel 1 of Analog Output") Status_2 = attribute(dtype='str', access=AttrWriteType.READ, doc="Status of channel 2 of Analog Output") Status_3 = attribute(dtype='str', access=AttrWriteType.READ, doc="Status of channel 3 of Analog Output") EventStatus_0 = attribute(dtype='str', access=AttrWriteType.READ, doc="Event Status of channel 0 of Digital Input") EventStatus_1 = attribute(dtype='str', access=AttrWriteType.READ, doc="Event Status of channel 1 of Digital Input") EventStatus_2 = attribute(dtype='str', access=AttrWriteType.READ, doc="Event Status of channel 2 of Digital Input") EventStatus_3 = attribute(dtype='str', access=AttrWriteType.READ, doc="Event Status of channel 3 of Digital Input") # --------------- # General methods # --------------- def init_device(self): """Initialise device and sets its state to STANDBY""" Device.init_device(self) self.set_state(DevState.STANDBY) self.set_status("ADAM-6224 in state STANDBY, ready to connect to " "device") def delete_device(self): """Disconnect from physical device before deleting instance""" self.connected_ADAM.close() @command @DebugIt() def disconnect(self): """Disconnect from device and sets state to STANDBY """ self.connected_ADAM.close() self.set_state(DevState.STANDBY) self.set_status( "Device disconnected form ADAM-6224, set state to STANDBY, " "ready to connect to device again") # -------------------- # AnalogOutput methods # -------------------- def read_AnalogOutput_0(self): return self.read_AnalogOutput(0) def write_AnalogOutput_0(self, value): return self.write_AnalogOutput(0, value) def read_AnalogOutput_1(self): return self.read_AnalogOutput(1) def write_AnalogOutput_1(self, value): return self.write_AnalogOutput(1, value) def read_AnalogOutput_2(self): return self.read_AnalogOutput(2) def write_AnalogOutput_2(self, value): return self.write_AnalogOutput(2, value) def read_AnalogOutput_3(self): return self.read_AnalogOutput(3) def write_AnalogOutput_3(self, value): return self.write_AnalogOutput(3, value) def read_AnalogOutput_4(self): return self.read_AnalogOutput(4) def write_AnalogOutput_4(self, value): return self.write_AnalogOutput(4, value) def read_AnalogOutput_5(self): return self.read_AnalogOutput(5) def write_AnalogOutput_5(self, value): return self.write_AnalogOutput(5, value) def read_AnalogOutput_6(self): return self.read_AnalogOutput(6) def write_AnalogOutput_6(self, value): return self.write_AnalogOutput(6, value) def read_AnalogOutput_7(self): return self.read_AnalogOutput(7) def write_AnalogOutput_7(self, value): return self.write_AnalogOutput(7, value) # -------------------- # SafetyValue methods # -------------------- def read_SafetyValue_0(self): return self.read_SafetyValue(0) def write_SafetyValue_0(self, value): self.write_SafetyValue(0, value) def read_SafetyValue_1(self): return self.read_SafetyValue(1) def write_SafetyValue_1(self, value): self.write_SafetyValue(1, value) def read_SafetyValue_2(self): return self.read_SafetyValue(2) def write_SafetyValue_2(self, value): self.write_SafetyValue(2, value) def read_SafetyValue_3(self): return self.read_SafetyValue(3) def write_SafetyValue_3(self, value): self.write_SafetyValue(3, value) def read_SafetyValue_4(self): return self.read_SafetyValue(4) def write_SafetyValue_4(self, value): self.write_SafetyValue(4, value) def read_SafetyValue_5(self): return self.read_SafetyValue(5) def write_SafetyValue_5(self, value): self.write_SafetyValue(5, value) def read_SafetyValue_6(self): return self.read_SafetyValue(6) def write_SafetyValue_6(self, value): self.write_SafetyValue(6, value) def read_SafetyValue_7(self): return self.read_SafetyValue(7) def write_SafetyValue_7(self, value): self.write_SafetyValue(7, value) # -------------------- # StartupValue methods # -------------------- def read_StartupValue_0(self): return self.read_StartupValue(0) def write_StartupValue_0(self, value): self.write_StartupValue(0, value) def read_StartupValue_1(self): return self.read_StartupValue(1) def write_StartupValue_1(self, value): self.write_StartupValue(1, value) def read_StartupValue_2(self): return self.read_StartupValue(2) def write_StartupValue_2(self, value): self.write_StartupValue(2, value) def read_StartupValue_3(self): return self.read_StartupValue(3) def write_StartupValue_3(self, value): self.write_StartupValue(3, value) def read_StartupValue_4(self): return self.read_StartupValue(4) def write_StartupValue_4(self, value): self.write_StartupValue(4, value) def read_StartupValue_5(self): return self.read_StartupValue(5) def write_StartupValue_5(self, value): self.write_StartupValue(5, value) def read_StartupValue_6(self): return self.read_StartupValue(6) def write_StartupValue_6(self, value): self.write_StartupValue(6, value) def read_StartupValue_7(self): return self.read_StartupValue(7) def write_StartupValue_7(self, value): self.write_StartupValue(7, value) # -------------------- # TypeCode methods # -------------------- def read_TypeCode_0(self): return self.read_TypeCode(0) def write_TypeCode_0(self, value): self.write_TypeCode(0, value) def read_TypeCode_1(self): return self.read_TypeCode(1) def write_TypeCode_1(self, value): self.write_TypeCode(1, value) def read_TypeCode_2(self): return self.read_TypeCode(2) def write_TypeCode_2(self, value): self.write_TypeCode(2, value) def read_TypeCode_3(self): return self.read_TypeCode(3) def write_TypeCode_3(self, value): self.write_TypeCode(3, value) def read_TypeCode_4(self): return self.read_TypeCode(4) def write_TypeCode_4(self, value): self.write_TypeCode(4, value) def read_TypeCode_5(self): return self.read_TypeCode(5) def write_TypeCode_5(self, value): self.write_TypeCode(5, value) def read_TypeCode_6(self): return self.read_TypeCode(6) def write_TypeCode_6(self, value): self.write_TypeCode(6, value) def read_TypeCode_7(self): return self.read_TypeCode(7) def write_TypeCode_7(self, value): self.write_TypeCode(7, value) # -------------------- # DigitalInput methods # -------------------- def read_DigitalInput_0(self): return self.read_DigitalInput(0) def read_DigitalInput_1(self): return self.read_DigitalInput(1) def read_DigitalInput_2(self): return self.read_DigitalInput(2) def read_DigitalInput_3(self): return self.read_DigitalInput(3) # -------------------- # EventStatus methods # -------------------- def read_EventStatus_0(self): return self.read_EventStatus(0) def read_EventStatus_1(self): return self.read_EventStatus(1) def read_EventStatus_2(self): return self.read_EventStatus(2) def read_EventStatus_3(self): return self.read_EventStatus(3) # -------------------- # EventStatus methods # -------------------- def read_Status_0(self): return self.read_Status(0) def read_Status_1(self): return self.read_Status(1) def read_Status_2(self): return self.read_Status(2) def read_Status_3(self): return self.read_Status(3) # -------------------- # Additional methods # -------------------- def decode_value(self, value, channel, type): """Decode double to 16-bit value depending on Type Code of channel """ type_code = self.analog_output_types[channel] if ((type_code == int("0182", 16) and not (value >= 0 and value <= 0.02)) or (type_code == int("0180", 16) and not (value >= 0.004 and value <= 0.02)) or (type_code == int("0148", 16) and not (value >= 0 and value <= 10)) or (type_code == int("0147", 16) and not (value >= 0 and value <= 5)) or (type_code == int("0143", 16) and not (value >= -10 and value <= 10)) or (type_code == int("0142", 16) and not (value >= -5 and value <= 5))): if type == 0: tmp = self.analog_output_values[channel] elif type == 1: tmp = self.analog_output_safety_values[channel] else: tmp = self.analog_output_statup_values[channel] self.error_stream('Illegal value') raise ValueError else: if type_code == int("0182", 16): tmp = int(4095 * value / 0.02) elif type_code == int("0180", 16): tmp = int(4095 * (value - 0.004) / 0.016) elif type_code == int("0148", 16): tmp = int(4095 * value / 10) elif type_code == int("0147", 16): tmp = int(4095 * value / 5) elif type_code == int("0143", 16): tmp = int(4095 * (value + 10) / 20) elif type_code == int("0142", 16): tmp = int(4095 * (value + 5) / 10) else: if type == 0: tmp = self.analog_output_values[channel] elif type == 1: tmp = self.analog_output_safety_values[channel] else: tmp = self.analog_output_statup_values[channel] return tmp def encode_value(self, value, channel): """Encode 16-bit value to double depending on Type Code of channel """ type_code = self.analog_output_types[channel] if type_code == int("0182", 16): tmp = 0.02 * value / 4095.0 elif type_code == int("0180", 16): tmp = (0.016 * value / 4095.0) + 0.004 elif type_code == int("0148", 16): tmp = 10 * value / 4095.0 elif type_code == int("0147", 16): tmp = 5 * value / 4095.0 elif type_code == int("0143", 16): tmp = (20 * value / 4095.0) - 10 elif type_code == int("0142", 16): tmp = (10 * value / 4095.0) - 5 else: tmp = self.analog_output_values[channel] return tmp def decode_type_code(self, channel): """Decode 16-bit number to Type Code string""" return self.code_to_type_dict[self.analog_output_types[channel]] def encode_type_code(self, value="0-20mA"): """Encodes Type Code string to 16-bit number""" return self.type_to_code_dict[value] # -------- # Commands # -------- @command @DebugIt() def ConnectWithDevice(self): """ Connect with ADAM Module with IP address the same as DeviceAddress property and sets its state to ON """ try: self.connected_ADAM = ModbusTcpClient(self.DeviceAddress, port=int(502)) except ModbusException as e: self.set_state(DevState.FAULT) self.set_status("Modbus exception caught while" " connecting to device: \n%s" % e) except Exception as e: self.set_state(DevState.FAULT) self.set_status("Exception caught while connecting to device:" + "\n%s" % e) self.set_state(DevState.ON) self.set_status("Connected to device with IP: " + str(self.DeviceAddress)) @command(polling_period=500) def read_DataFromDevice(self): """ Synchronous reading data from ADAM Module registers """ if self.get_state() == tango.DevState.ON: # read Digital Inputs tmp = self.connected_ADAM.read_coils(0, 4) self.digital_input_values = tmp.bits # read Analog Outputs Value tmp = self.connected_ADAM.read_holding_registers(0, 4) self.analog_output_values = tmp.registers # read Analog Outputs Status tmp = self.connected_ADAM.read_holding_registers(100, 8) self.analog_output_statuses = tmp.registers # read Digital Inputs Status tmp = self.connected_ADAM.read_holding_registers(110, 4) self.digital_input_events = tmp.registers # read Type Codes tmp = self.connected_ADAM.read_holding_registers(200, 4) self.analog_output_types = tmp.registers # read Startup Values tmp = self.connected_ADAM.read_holding_registers(400, 4) self.analog_output_startup_values = tmp.registers # read Safety Values tmp = self.connected_ADAM.read_holding_registers(410, 4) self.analog_output_safety_values = tmp.registers
class P04_beamline(Device): DYN_ATTRS = [ dict(name='photonenergy', label='photon energy', dtype=tango.DevFloat, access=READ_WRITE, unit='eV', format='%6.2f', min_value=240, max_value=2000), dict(name='exitslit', label="exit slit", dtype=tango.DevFloat, access=READ_WRITE, unit="um", format="%4.0f"), dict(name='helicity', label='helicity', dtype=tango.DevLong, access=READ_WRITE, min_value=-1, max_value=1), dict(name='mono', label="monochromator", dtype=tango.DevFloat, access=READ, unit="eV", format="%6.2f"), dict(name='undugap', label='undulator gap', dtype=tango.DevFloat, access=READ, unit='mm'), # dict(name='undufactor', label='undulator scale factor', # access=READ, format='%3.2f', dtype=tango.DevFloat), dict(name='undushift', label='undulator shift', dtype=tango.DevFloat, access=READ, unit='mm'), dict(name='ringcurrent', label='ring current', dtype=tango.DevFloat, access=READ, unit='mA'), # dict(name='keithley1', label='beamline keithley', dtype=tango.DevFloat, # access=READ), # dict(name='keithley2', label='user keithley', dtype=tango.DevFloat, # access=READ), dict(name='slt2hleft', label='slit hor left', dtype=tango.DevFloat, access=READ), dict(name='slt2hright', label='slit hor right', dtype=tango.DevFloat, access=READ), dict(name='slt2vgap', label='slit ver gap', dtype=tango.DevFloat, access=READ), dict(name='slt2voffset', label='slit ver offset', dtype=tango.DevFloat, access=READ), # dict(name='exsu2bpm', label='exsu2bpm', dtype=tango.DevFloat, # access=READ), # dict(name='exsu2baffle', label='exsu2baffle', dtype=tango.DevFloat, # access=READ), # dict(name='pressure', label='experiment pressure', access=READ, # dtype=tango.DevFloat, unit='mbar', format='%.2E'), dict(name='screen', label='beamline screen', dtype=tango.DevLong, access=READ_WRITE, min_value=0, max_value=2, enum_labels=['closed', 'mesh', 'open']) ] ready_to_move = attribute(name='ready_to_move', label='in position', access=READ, dtype=tango.DevBoolean, polling_period=1000, fread="is_movable") host = device_property(dtype=str, mandatory=True, update_db=True) port = device_property(dtype=int, default_value=3002) def init_device(self): Device.init_device(self) self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.host, self.port)) self.s.setblocking(True) self.lock = Lock() self.set_state(DevState.ON) energy = self.read_attr('photonenergy')[0] self._setpoint_E = [energy, energy] self._setpoint_helicity = self.read_attr('helicity')[0] def initialize_dynamic_attributes(self): # TODO: setup polling and event filter for d in self.DYN_ATTRS: new_attr = attribute(fget=self.read_general, fset=self.write_general, **d) self.add_attribute(new_attr) @command(dtype_in=str) def query(self, msg): '''Send a query and wait for its reply.''' if self.lock.acquire(timeout=0.5): if not msg.endswith(' eoc'): msg += ' eoc' # print('sent:', msg, file=self.log_debug) self.s.sendall(msg.encode()) ans = self.s.recv(1024).decode() # print('received:', ans, file=self.log_debug) assert ans.endswith('eoa') self.lock.release() return ans[:-4] else: print(f"can't send '{msg}': socket is locked", file=self.log_error) return 'busy' def read_general(self, attr): key = attr.get_name() # print('reading', key, file=self.log_debug) val, time, quality = self.read_attr(key) attr.set_value(val) # def write_general(self, attr): # key = attr.get_name() # val = attr.get_write_value() # send_attrs = ['photonenergy', 'exitslit', 'helicity', 'screen'] # cmd = 'send' if key in send_attrs else 'set' # ntries = 0 # while ntries < 10: # ntries += 1 # if self.is_movable(): # ans = self.query(f'{cmd} {key} {val}') # print(f'setting {key}: {val} ({ntries}/10)', file=self.log_debug) # if ans == 'started': # self.set_state(DevState.MOVING) # print(f'[key] moving to {val}', file=self.log_debug) # return # time.sleep(1) # print(f'could not send {key} to {val}', file=self.log_error) def write_general(self, attr): key = attr.get_name() val = attr.get_write_value() send_attrs = ['photonenergy', 'exitslit', 'helicity', 'screen'] cmd = 'send' if key in send_attrs else 'set' if key == 'photonenergy': self._setpoint_E[0] = val if key == 'helicity': self._setpoint_helicity = val ans = self.query(f'{cmd} {key} {val}') print(f'setting {key}: {val}', file=self.log_debug) if ans == 'started': self._setpoint_E[1] = val self.set_state(DevState.MOVING) print(f'[key] moving to {val}', file=self.log_debug) return print(f'could not send {key} to {val}', file=self.log_error) def is_movable(self): '''Check whether undulator and monochromator are in position.''' in_pos = self.query('check photonenergy') in_pos = True if in_pos == '1' else False if (self._setpoint_E[0] != self._setpoint_E[1]) and in_pos: ans_set = self.query(f'send photonenergy {self._setpoint_E[0]}') if ans_set == 'started': self._setpoint_E[1] = self._setpoint_E[0] helicity = self.read_attr('helicity')[0] state = (helicity == self._setpoint_helicity) and in_pos self.set_state(DevState.ON if state else DevState.MOVING) return in_pos @command(dtype_in=str, dtype_out=str) def cmd_async(self, msg, test): '''Send a command without waiting for it to finish. The socket will still be blocked! ''' t = Thread(target=self.query, args=(msg, )) t.daemon = True t.start() @command def closeconnection(self): ans = self.query('closeconnection') if 'bye!' in ans: self.s.close() self.set_state(DevState.OFF) def read_attr(self, attr): '''Queries the position of given attribute name. Returns ------- val : float tstamp : time stamp quality : AttrQuality instance (ATTR_VALID, ATTR_CHANGING, ...) ''' ans = self.query(f'read {attr}') if 'Current value' in ans: val = float(ans.split(':')[1]) return val, time(), AttrQuality.ATTR_VALID else: self.error_stream('Socket busy or unexpected/incomplete answer') return None, time(), AttrQuality.ATTR_INVALID
class SKAAlarmHandler(with_metaclass(DeviceMeta, SKABaseDevice)): """ A generic base device for Alarms for SKA. """ # PROTECTED REGION ID(SKAAlarmHandler.class_variable) ENABLED START # # PROTECTED REGION END # // SKAAlarmHandler.class_variable # ----------------- # Device Properties # ----------------- SubAlarmHandlers = device_property(dtype=('str', ), ) AlarmConfigFile = device_property(dtype='str', ) # ---------- # Attributes # ---------- statsNrAlerts = attribute( dtype='int', doc="Number of active Alerts", ) statsNrAlarms = attribute( dtype='int', doc="Number of active Alarms", ) statsNrNewAlarms = attribute( dtype='int', doc="Number of New active alarms", ) statsNrUnackAlarms = attribute( dtype='double', doc="Number of unacknowledged alarms", ) statsNrRtnAlarms = attribute( dtype='double', doc="Number of returned alarms", ) activeAlerts = attribute( dtype=('str', ), max_dim_x=10000, doc="List of active alerts", ) activeAlarms = attribute( dtype=('str', ), max_dim_x=10000, doc="List of active alarms", ) # --------------- # General methods # --------------- def init_device(self): SKABaseDevice.init_device(self) self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # PROTECTED REGION ID(SKAAlarmHandler.init_device) ENABLED START # # PROTECTED REGION END # // SKAAlarmHandler.init_device def always_executed_hook(self): # PROTECTED REGION ID(SKAAlarmHandler.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKAAlarmHandler.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKAAlarmHandler.delete_device) ENABLED START # pass # PROTECTED REGION END # // SKAAlarmHandler.delete_device # ------------------ # Attributes methods # ------------------ def read_statsNrAlerts(self): # PROTECTED REGION ID(SKAAlarmHandler.statsNrAlerts_read) ENABLED START # """ Reads number of active alerts. :return: Number of active alerts """ return 0 # PROTECTED REGION END # // SKAAlarmHandler.statsNrAlerts_read def read_statsNrAlarms(self): # PROTECTED REGION ID(SKAAlarmHandler.statsNrAlarms_read) ENABLED START # """ Reads number of active alarms. :return: Number of active alarms """ return 0 # PROTECTED REGION END # // SKAAlarmHandler.statsNrAlarms_read def read_statsNrNewAlarms(self): # PROTECTED REGION ID(SKAAlarmHandler.statsNrNewAlarms_read) ENABLED START # """ Reads number of new active alarms. :return: Number of new active alarms """ return 0 # PROTECTED REGION END # // SKAAlarmHandler.statsNrNewAlarms_read def read_statsNrUnackAlarms(self): # PROTECTED REGION ID(SKAAlarmHandler.statsNrUnackAlarms_read) ENABLED START # """ Reads number of unacknowledged alarms. :return: Number of unacknowledged alarms. """ return 0.0 # PROTECTED REGION END # // SKAAlarmHandler.statsNrUnackAlarms_read def read_statsNrRtnAlarms(self): # PROTECTED REGION ID(SKAAlarmHandler.statsNrRtnAlarms_read) ENABLED START # """ Reads number of returned alarms. :return: Number of returned alarms """ return 0.0 # PROTECTED REGION END # // SKAAlarmHandler.statsNrRtnAlarms_read def read_activeAlerts(self): # PROTECTED REGION ID(SKAAlarmHandler.activeAlerts_read) ENABLED START # """ Reads list of active alerts. :return: List of active alerts """ return [''] # PROTECTED REGION END # // SKAAlarmHandler.activeAlerts_read def read_activeAlarms(self): # PROTECTED REGION ID(SKAAlarmHandler.activeAlarms_read) ENABLED START # """ Reads list of active alarms. :return: List of active alarms """ return [''] # PROTECTED REGION END # // SKAAlarmHandler.activeAlarms_read # -------- # Commands # -------- @command( dtype_in='str', doc_in="Alarm name", dtype_out='str', doc_out="JSON string", ) @DebugIt() def GetAlarmRule(self, argin): # PROTECTED REGION ID(SKAAlarmHandler.GetAlarmRule) ENABLED START # """ Get all configuration info of the alarm, e.g. rule, defined action, etc. :param argin: Name of the alarm :return: JSON string containing configuration information of the alarm """ return "" # PROTECTED REGION END # // SKAAlarmHandler.GetAlarmRule @command( dtype_in='str', doc_in="Alarm name", dtype_out='str', doc_out="JSON string", ) @DebugIt() def GetAlarmData(self, argin): # PROTECTED REGION ID(SKAAlarmHandler.GetAlarmData) ENABLED START # """ Get list of current value, quality factor and status of all attributes participating in the alarm rule. :param argin: Name of the alarm :return: JSON string containing alarm data """ return "" # PROTECTED REGION END # // SKAAlarmHandler.GetAlarmData @command( dtype_in='str', doc_in="Alarm name", dtype_out='str', doc_out="JSON string", ) @DebugIt() def GetAlarmAdditionalInfo(self, argin): # PROTECTED REGION ID(SKAAlarmHandler.GetAlarmAdditionalInfo) ENABLED START # """ Get additional alarm information. :param argin: Name of the alarm :return: JSON string containing additional alarm information """ return "" # PROTECTED REGION END # // SKAAlarmHandler.GetAlarmAdditionalInfo @command( dtype_out='str', doc_out="JSON string", ) @DebugIt() def GetAlarmStats(self): # PROTECTED REGION ID(SKAAlarmHandler.GetAlarmStats) ENABLED START # """ Get current alarm stats. :return: JSON string containing alarm statistics """ return "" # PROTECTED REGION END # // SKAAlarmHandler.GetAlarmStats @command( dtype_out='str', doc_out="JSON string", ) @DebugIt() def GetAlertStats(self): # PROTECTED REGION ID(SKAAlarmHandler.GetAlertStats) ENABLED START # """ Get current alert stats. :return: JSON string containing alert statistics """ return ""
class AndrewDev(Device): """ Andrew's hack of example power supply device from the PyTango documentation. """ # green_mode = GreenMode.Asyncio voltage = attribute(label="Voltage", dtype=float, display_level=DispLevel.OPERATOR, access=AttrWriteType.READ, unit="V", format="%4.2f", doc="output voltage") current = attribute(label="Current", dtype=float, display_level=DispLevel.EXPERT, access=AttrWriteType.READ_WRITE, unit="A", format="%3.2f", min_value=0.0, max_value=8.5, min_alarm=0.1, max_alarm=8.4, min_warning=0.5, max_warning=8.0, abs_change=0.01, fget="get_current", fset="set_current", doc="output current") temperature = attribute(label="Temperature", dtype=int, display_level=DispLevel.EXPERT, access=AttrWriteType.READ, unit="degC", format="%3.1f", # '°' symbol displays strangely on GUI? display_unit=0.1, # tells UI to divide by 10 abs_change=50, doc="internal temperature") host = device_property(dtype=str) port = device_property(dtype=int, default_value=9788) def __init__(self, device_class, device_name): super().__init__(device_class, device_name) self.__current = 0.0 self.__temperature = 200 def init_device(self): """Initialise device""" Device.init_device(self) self.set_current(0.0) self.set_state(DevState.STANDBY) def read_voltage(self): """Read voltage""" self.info_stream("read_voltage(%s, %d)", self.host, self.port) return 240, time.time(), AttrQuality.ATTR_VALID def get_current(self): """Get the current""" if self.get_state() in (DevState.STANDBY, DevState.OFF): return 0 return self.__current def set_current(self, current): """Set the current""" self.__current = current def read_temperature(self): """Read internal temperature""" # very coarse thermal loss simulation if self.get_state() in (DevState.STANDBY, DevState.OFF): self.__temperature = 200 else: self.__temperature = 200 + (self.__current**2)*10 return self.__temperature + randint(-4, 4) @command def turn_on(self): """Turn the device on""" # turn on the actual power supply here self.set_state(DevState.ON) self.push_change_event('temperature', self.__temperature) self.push_change_event('current', self.__current) @command def turn_off(self): """Turn the device off""" # turn off the actual power supply here self.set_state(DevState.OFF) self.push_change_event('temperature', self.__temperature) self.push_change_event('current', self.__current) @command(dtype_in=float, doc_in="Ramp target current", dtype_out=bool, doc_out="True if ramping went well, " "False otherwise") def ramp(self, target_current): """Ramp output to the target current""" # should do the ramping. This doesn't. self.set_current(target_current) return True
def add_dyn_attr(self, name): attr = attribute(name=name, dtype='float', fget=self.read) self.add_attribute(attr)
class Vcc(CspSubElementObsDevice): """ Vcc TANGO device class for the prototype """ #TODO: remove temporary manual flag for unit testing #set True to bypass band and search window device proxies TEST_CONTEXT = False # ----------------- # Device Properties # ----------------- VccID = device_property( dtype='DevUShort' ) Band1And2Address = device_property( dtype='str' ) Band3Address = device_property( dtype='str' ) Band4Address = device_property( dtype='str' ) Band5Address = device_property( dtype='str' ) SW1Address = device_property( dtype='str' ) SW2Address = device_property( dtype='str' ) # ---------- # Attributes # ---------- receptorID = attribute( dtype='uint16', access=AttrWriteType.READ_WRITE, label="Receptor ID", doc="Receptor ID", ) subarrayMembership = attribute( dtype='uint16', access=AttrWriteType.READ_WRITE, label="subarrayMembership", doc="Subarray membership", ) frequencyBand = attribute( dtype='DevEnum', access=AttrWriteType.READ, label="Frequency band", doc="Frequency band; an int in the range [0, 5]", enum_labels=["1", "2", "3", "4", "5a", "5b", ], ) band5Tuning = attribute( dtype=('float',), max_dim_x=2, access=AttrWriteType.READ_WRITE, label="Stream tuning (GHz)", doc="Stream tuning (GHz)" ) frequencyBandOffsetStream1 = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="Frequency band offset (stream 1) (Hz)", doc="Frequency band offset (stream 1) (Hz)" ) frequencyBandOffsetStream2 = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="Frequency band offset (stream 2) (Hz)", doc="Frequency band offset (stream 2) (Hz)" ) dopplerPhaseCorrection = attribute( dtype=('float',), access=AttrWriteType.READ_WRITE, max_dim_x=4, label="Doppler phase correction coefficients", doc="Doppler phase correction coefficients" ) rfiFlaggingMask = attribute( dtype='str', access=AttrWriteType.READ_WRITE, label="RFI Flagging Mask", doc="RFI Flagging Mask" ) scfoBand1 = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="SCFO (band 1)", doc="Sample clock frequency offset for band 1", ) scfoBand2 = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="SCFO (band 2)", doc="Sample clock frequency offset for band 2", ) scfoBand3 = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="SCFO (band 3)", doc="Sample clock frequency offset for band 3", ) scfoBand4 = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="SCFO (band 4)", doc="Sample clock frequency offset for band 4", ) scfoBand5a = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="SCFO (band 5a)", doc="Sample clock frequency offset for band 5a", ) scfoBand5b = attribute( dtype='int', access=AttrWriteType.READ_WRITE, label="SCFO (band 5b)", doc="Sample clock frequency offset for band 5b", ) delayModel = attribute( dtype=(('double',),), max_dim_x=6, max_dim_y=26, access=AttrWriteType.READ, label="Delay model coefficients", doc="Delay model coefficients, given per frequency slice" ) jonesMatrix = attribute( dtype=(('double',),), max_dim_x=16, max_dim_y=26, access=AttrWriteType.READ, label='Jones Matrix elements', doc='Jones Matrix elements, given per frequency slice' ) scanID = attribute( dtype='DevULong', access=AttrWriteType.READ_WRITE, label="scanID", doc="scan ID", ) configID = attribute( dtype='DevString', access=AttrWriteType.READ_WRITE, label="config ID", doc="config ID", ) # --------------- # General methods # --------------- # PROTECTED REGION ID(Vcc.class_variable) ENABLED START # def init_command_objects(self): """ Sets up the command objects """ super().init_command_objects() device_args = (self, self.state_model, self.logger) self.register_command_object( "ConfigureScan", self.ConfigureScanCommand(*device_args) ) self.register_command_object( "GoToIdle", self.GoToIdleCommand(*device_args) ) # PROTECTED REGION END # // Vcc.class_variable class InitCommand(CspSubElementObsDevice.InitCommand): """ A class for the Vcc's init_device() "command". """ def do(self): """ Stateless hook for device initialisation. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ self.logger.debug("Entering InitCommand()") super().do() device = self.target # Make a private copy of the device properties: self._vcc_id = device.VccID # initialize attribute values device._receptor_ID = 0 device._freq_band_name = "" device._frequency_band = 0 device._subarray_membership = 0 device._stream_tuning = (0, 0) device._frequency_band_offset_stream_1 = 0 device._frequency_band_offset_stream_2 = 0 device._doppler_phase_correction = (0, 0, 0, 0) device._rfi_flagging_mask = "" device._scfo_band_1 = 0 device._scfo_band_2 = 0 device._scfo_band_3 = 0 device._scfo_band_4 = 0 device._scfo_band_5a = 0 device._scfo_band_5b = 0 device._delay_model = [[0] * 6 for i in range(26)] device._jones_matrix = [[0] * 16 for i in range(26)] device._scan_id = "" device._config_id = "" # device._fqdns = [ # device.Band1And2Address, # device.Band3Address, # device.Band4Address, # device.Band5Address, # device.SW1Address, # device.SW2Address # ] # TODO # device._func_devices = [ # tango.DeviceProxy(fqdn) for fqdn in device._fqdns # ] device.set_change_event("subarrayMembership", True, True) # TODO - create a command for the proxy connections # similar to InitialSetup() in ska-low-mccs stations.py #self.__get_capability_proxies() # TODO - To support unit testing, use a wrapper class for the # connection instead of directly DeviceProxy (see MccsDeviceProxy) device._dev_factory = DevFactory() device._proxy_band_12 = None device._proxy_band_3 = None device._proxy_band_4 = None device._proxy_band_5 = None device._proxy_sw_1 = None device._proxy_sw_2 = None message = "Vcc Init command completed OK" device.logger.info(message) return (ResultCode.OK, message) def always_executed_hook(self): """Method always executed before any TANGO command is executed.""" # PROTECTED REGION ID(Vcc.always_executed_hook) ENABLED START # self.logger.info("ALWAYS EXECUTED HOOK") self.logger.info("%s", self._dev_factory._test_context) try: #TODO: remove temporary flag to disable Vcc proxies for unit testing if Vcc.TEST_CONTEXT is False: if self._proxy_band_12 is None: self.logger.info("Connect to band proxy device") self._proxy_band_12 = self._dev_factory.get_device( self.Band1And2Address ) if self._proxy_band_3 is None: self.logger.info("Connect to band proxy device") self._proxy_band_3 = self._dev_factory.get_device( self.Band3Address ) if self._proxy_band_4 is None: self.logger.info("Connect to band proxy device") self._proxy_band_4 = self._dev_factory.get_device( self.Band4Address ) if self._proxy_band_5 is None: self.logger.info("Connect to band proxy device") self._proxy_band_5 = self._dev_factory.get_device( self.Band5Address ) if self._proxy_sw_1 is None: self.logger.info("Connect to search window proxy device") self._proxy_sw_1 = self._dev_factory.get_device( self.SW1Address ) if self._proxy_sw_2 is None: self.logger.info("Connect to search window proxy device") self._proxy_sw_2 = self._dev_factory.get_device( self.SW2Address ) except Exception as ex: self.logger.info( "Unexpected error on DeviceProxy creation %s", str(ex) ) # PROTECTED REGION END # // Vcc.always_executed_hook def delete_device(self): """ Hook to delete resources allocated in the :py:meth:`~.Vcc.InitCommand.do` method of the nested :py:class:`~.Vcc.InitCommand` class. This method allows for any memory or other resources allocated in the :py:meth:`~.Vcc.InitCommand.do` method to be released. This method is called by the device destructor, and by the Init command when the Tango device server is re-initialised. """ pass # ------------------ # Attributes methods # ------------------ def read_receptorID(self): # PROTECTED REGION ID(Vcc.receptorID_read) ENABLED START # """Return recptorID attribut(int)""" return self._receptor_ID # PROTECTED REGION END # // Vcc.receptorID_read def write_receptorID(self, value): # PROTECTED REGION ID(Vcc.receptorID_write) ENABLED START # """Set receptor ID attribute(int)""" self._receptor_ID = value # PROTECTED REGION END # // Vcc.receptorID_write def read_subarrayMembership(self): # PROTECTED REGION ID(Vcc.subarrayMembership_read) ENABLED START # """Return subarrayMembership attribute: sub-array affiliation of the VCC(0 of no affliation)""" self.logger.debug("Entering read_subarrayMembership(), _subarray_membership = {}".format(self._subarray_membership)) return self._subarray_membership # PROTECTED REGION END # // Vcc.subarrayMembership_read def write_subarrayMembership(self, value): # PROTECTED REGION ID(Vcc.subarrayMembership_write) ENABLED START # """Set subarrayMembership attribute: sub-array affiliation of the VCC(0 of no affliation)""" self.logger.debug("Entering write_subarrayMembership(), value = {}".format(value)) self._subarray_membership = value self.push_change_event("subarrayMembership",value) if not value: self._update_obs_state(ObsState.IDLE) # PROTECTED REGION END # // Vcc.subarrayMembership_write def read_frequencyBand(self): # PROTECTED REGION ID(Vcc.frequencyBand_read) ENABLED START # """Return frequencyBand attribute: frequency band being observed by the current scan (one of ["1", "2", "3", "4", "5a", "5b", ])""" return self._frequency_band # PROTECTED REGION END # // Vcc.frequencyBand_read def read_band5Tuning(self): # PROTECTED REGION ID(Vcc.band5Tuning_read) ENABLED START # """Return band5Tuning attribute: Stream tuning (GHz) in float""" return self._stream_tuning # PROTECTED REGION END # // Vcc.band5Tuning_read def write_band5Tuning(self, value): # PROTECTED REGION ID(Vcc.band5Tuning_write) ENABLED START # """Set band5Tuning attribute: Stream tuning (GHz) in float""" self._stream_tuning = value # PROTECTED REGION END # // Vcc.band5Tuning_write def read_frequencyBandOffsetStream1(self): # PROTECTED REGION ID(Vcc.frequencyBandOffsetStream1_read) ENABLED START # """Return frequecyBandOffsetStream1 attribute(int)""" return self._frequency_band_offset_stream_1 # PROTECTED REGION END # // Vcc.frequencyBandOffsetStream1_read def write_frequencyBandOffsetStream1(self, value): # PROTECTED REGION ID(Vcc.frequencyBandOffsetStream1_write) ENABLED START # """Set frequecyBandOffsetStream1 attribute(int)""" self._frequency_band_offset_stream_1 = value # PROTECTED REGION END # // Vcc.frequencyBandOffsetStream1_write def read_frequencyBandOffsetStream2(self): # PROTECTED REGION ID(Vcc.frequencyBandOffsetStream2_read) ENABLED START # """Return frequecyBandOffsetStream2 attribute(int)""" return self._frequency_band_offset_stream_2 # PROTECTED REGION END # // Vcc.frequencyBandOffsetStream2_read def write_frequencyBandOffsetStream2(self, value): # PROTECTED REGION ID(Vcc.frequencyBandOffsetStream2_write) ENABLED START # """Set frequecyBandOffsetStream2 attribute(int)""" self._frequency_band_offset_stream_2 = value # PROTECTED REGION END # // Vcc.frequencyBandOffsetStream2_write def read_dopplerPhaseCorrection(self): # PROTECTED REGION ID(Vcc.dopplerPhaseCorrection_read) ENABLED START # """Return dopplerPhaseCorrection attribute(float)""" return self._doppler_phase_correction # PROTECTED REGION END # // Vcc.dopplerPhaseCorrection_read def write_dopplerPhaseCorrection(self, value): # PROTECTED REGION ID(Vcc.dopplerPhaseCorrection_write) ENABLED START # """Set dopplerPhaseCorrection attribute(float)""" self._doppler_phase_correction = value # PROTECTED REGION END # // Vcc.dopplerPhaseCorrection_write def read_rfiFlaggingMask(self): # PROTECTED REGION ID(Vcc.rfiFlaggingMask_read) ENABLED START # """Return rfiFlaggingMask attribute(str/JSON)""" return self._rfi_flagging_mask # PROTECTED REGION END # // Vcc.rfiFlaggingMask_read def write_rfiFlaggingMask(self, value): # PROTECTED REGION ID(Vcc.rfiFlaggingMask_write) ENABLED START # """Set rfiFlaggingMask attribute(str/JSON)""" self._rfi_flagging_mask = value # PROTECTED REGION END # // Vcc.rfiFlaggingMask_write def read_scfoBand1(self): # PROTECTED REGION ID(Vcc.scfoBand1_read) ENABLED START # """Return scfoBand1 attribute(int): Sample clock frequency offset for band 1""" return self._scfo_band_1 # PROTECTED REGION END # // Vcc.scfoBand1_read def write_scfoBand1(self, value): # PROTECTED REGION ID(Vcc.scfoBand1_write) ENABLED START # """Set scfoBand1 attribute(int): Sample clock frequency offset for band 1""" self._scfo_band_1 = value # PROTECTED REGION END # // Vcc.scfoBand1_write def read_scfoBand2(self): # PROTECTED REGION ID(Vcc.scfoBand2_read) ENABLED START # """Return scfoBand2 attribute(int): Sample clock frequency offset for band 2""" return self._scfo_band_2 # PROTECTED REGION END # // Vcc.scfoBand2_read def write_scfoBand2(self, value): # PROTECTED REGION ID(Vcc.scfoBand2_write) ENABLED START # """Set scfoBand2 attribute(int): Sample clock frequency offset for band 2""" self._scfo_band_2 = value # PROTECTED REGION END # // Vcc.scfoBand2_write def read_scfoBand3(self): # PROTECTED REGION ID(Vcc.scfoBand3_read) ENABLED START # """Return scfoBand3 attribute(int): Sample clock frequency offset for band 3""" return self._scfo_band_3 # PROTECTED REGION END # // Vcc.scfoBand3_read def write_scfoBand3(self, value): # PROTECTED REGION ID(Vcc.scfoBand3_write) ENABLED START # """Set scfoBand3 attribute(int): Sample clock frequency offset for band 3""" self._scfo_band_3 = value # PROTECTED REGION END # // Vcc.scfoBand3_write def read_scfoBand4(self): # PROTECTED REGION ID(Vcc.scfoBand4_read) ENABLED START # """Return scfoBand4 attribute(int): Sample clock frequency offset for band 4""" return self._scfo_band_4 # PROTECTED REGION END # // Vcc.scfoBand4_read def write_scfoBand4(self, value): # PROTECTED REGION ID(Vcc.scfoBand4_write) ENABLED START # """Set scfoBand4 attribute(int): Sample clock frequency offset for band 4""" self._scfo_band_4 = value # PROTECTED REGION END # // Vcc.scfoBand4_write def read_scfoBand5a(self): # PROTECTED REGION ID(Vcc.scfoBand5a_read) ENABLED START # """Return scfoBand5a attribute(int): Sample clock frequency offset for band 5a""" return self._scfo_band_5a # PROTECTED REGION END # // Vcc.scfoBand5a_read def write_scfoBand5a(self, value): # PROTECTED REGION ID(Vcc.scfoBand5a_write) ENABLED START # """Set scfoBand5a attribute(int): Sample clock frequency offset for band 5a""" self._scfo_band_5a = value # PROTECTED REGION END # // Vcc.scfoBand5a_write def read_scfoBand5b(self): # PROTECTED REGION ID(Vcc.scfoBand5b_read) ENABLED START # """Return scfoBand5b attribute(int): Sample clock frequency offset for band 5b""" return self._scfo_band_5b # PROTECTED REGION END # // Vcc.scfoBand5b_read def write_scfoBand5b(self, value): # PROTECTED REGION ID(Vcc.scfoBand5b_write) ENABLED START # """Set scfoBand5b attribute(int): Sample clock frequency offset for band 5b""" self._scfo_band_5b = value # PROTECTED REGION END # // Vcc.scfoBand5b_write def read_delayModel(self): # PROTECTED REGION ID(Vcc.delayModel_read) ENABLED START # """Return delayModel attribute(2 dim, max=6*26 array): Delay model coefficients, given per frequency slice""" return self._delay_model # PROTECTED REGION END # // Vcc.delayModel_read def read_jonesMatrix(self): # PROTECTED REGION ID(Vcc.jonesMatrix_read) ENABLED START # """Return jonesMatrix attribute(max=16 array): Jones Matrix, given per frequency slice""" return self._jones_matrix # PROTECTED REGION END # // Vcc.jonesMatrix_read def read_scanID(self): # PROTECTED REGION ID(Vcc.scanID_read) ENABLED START # """Return the scanID attribute.""" return self._scan_id # PROTECTED REGION END # // Vcc.scanID_read def write_scanID(self, value): # PROTECTED REGION ID(Vcc.scanID_write) ENABLED START # """Set the scanID attribute.""" self._scan_id=value # PROTECTED REGION END # // Vcc.scanID_write def read_configID(self): # PROTECTED REGION ID(Vcc.configID_read) ENABLED START # """Return the configID attribute.""" return self._config_id # PROTECTED REGION END # // Vcc.configID_read def write_configID(self, value): # PROTECTED REGION ID(Vcc.configID_write) ENABLED START # """Set the configID attribute.""" self._config_id = value # PROTECTED REGION END # // Vcc.configID_write # -------- # Commands # -------- class ConfigureScanCommand(CspSubElementObsDevice.ConfigureScanCommand): """ A class for the Vcc's ConfigureScan() command. """ def do(self, argin): """ Stateless hook for ConfigureScan() command functionality. :param argin: The configuration as JSON formatted string :type argin: str :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) :raises: ``CommandError`` if the configuration data validation fails. """ self.logger.debug("Entering ConfigureScanCommand()") device = self.target # By this time, the receptor_ID should be set: self.logger.debug(("device._receptor_ID = {}". format(device._receptor_ID))) # validate the input args (result_code, msg) = self.validate_input(argin) if result_code == ResultCode.OK: # TODO: cosider to turn_on the selected band device # via a separate command if Vcc.TEST_CONTEXT is False: self.turn_on_band_device(device._freq_band_name) # store the configuration on command success device._last_scan_configuration = argin msg = "Configure command completed OK" return(result_code, msg) def validate_input(self, argin): """ Validate the configuration parameters against allowed values, as needed. :param argin: The JSON formatted string with configuration for the device. :type argin: 'DevString' :return: A tuple containing a return code and a string message. :rtype: (ResultCode, str) """ device = self.target try: config_dict = json.loads(argin) device._config_id = config_dict['config_id'] freq_band_name = config_dict['frequency_band'] device._freq_band_name = freq_band_name device._frequency_band = freq_band_dict()[freq_band_name] # call the method to validate the data sent with # the configuration, as needed. return (ResultCode.OK, "ConfigureScan arguments validation successfull") except (KeyError, json.JSONDecodeError) as err: msg = "Validate configuration failed with error:{}".format(err) except Exception as other_errs: msg = "Validate configuration failed with unknown error:{}".format( other_errs) self.logger.error(msg) return (ResultCode.FAILED, msg) def turn_on_band_device(self, freq_band_name): """ Constraint: Must be called AFTER validate_input() Set corresponding band of this VCC. # TODO - remove this Send ON signal to the corresponding band, and DISABLE signal to all others """ device = self.target # TODO: can be done in a more Pythonian way; broken? if freq_band_name in ["1", "2"]: device._proxy_band_12.On() device._proxy_band_3.Disable() device._proxy_band_4.Disable() device._proxy_band_5.Disable() elif freq_band_name == "3": device._proxy_band_12.Disable() device._proxy_band_3.On() device._proxy_band_4.Disable() device._proxy_band_5.Disable() elif freq_band_name == "4": device._proxy_band_12.Disable() device._proxy_band_3.Disable() device._proxy_band_4.On() device._proxy_band_5.Disable() elif freq_band_name in ["5a", "5b"]: device._proxy_band_12.Disable() device._proxy_band_3.Disable() device._proxy_band_4.Disable() device._proxy_band_5.On() else: # The frequnecy band name has been validated at this point # so this shouldn't happen pass @command( dtype_in='DevString', doc_in="JSON formatted string with the scan configuration.", dtype_out='DevVarLongStringArray', doc_out="A tuple containing a return code and a string message indicating status. " "The message is for information purpose only.", ) @DebugIt() def ConfigureScan(self, argin): # PROTECTED REGION ID(Vcc.ConfigureScan) ENABLED START # """ Configure the observing device parameters for the current scan. :param argin: JSON formatted string with the scan configuration. :type argin: 'DevString' :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ command = self.get_command_object("ConfigureScan") (return_code, message) = command(argin) return [[return_code], [message]] # PROTECTED REGION END # // Vcc.ConfigureScan class GoToIdleCommand(CspSubElementObsDevice.GoToIdleCommand): """ A class for the Vcc's GoToIdle command. """ def do(self): """ Stateless hook for GoToIdle() command functionality. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ self.logger.debug("Entering GoToIdleCommand()") device = self.target # Reset all values intialized in InitCommand.do(): device._freq_band_name = "" device._frequency_band = 0 # device._receptor_ID - DO NOT reset! # _receptor_ID is set via an explicit write # BEFORE ConfigureScan() is executed; # device._subarray_membership - DO NOT reset! device._stream_tuning = (0, 0) device._frequency_band_offset_stream_1 = 0 device._frequency_band_offset_stream_2 = 0 device._doppler_phase_correction = (0, 0, 0, 0) device._rfi_flagging_mask = "" device._scfo_band_1 = 0 device._scfo_band_2 = 0 device._scfo_band_3 = 0 device._scfo_band_4 = 0 device._scfo_band_5a = 0 device._scfo_band_5b = 0 device._delay_model = [[0] * 6 for i in range(26)] device._jones_matrix = [[0] * 16 for i in range(26)] device._scan_id = 0 device._config_id = "" if device.state_model.obs_state == ObsState.IDLE: return (ResultCode.OK, "GoToIdle command completed OK. Device already IDLE") return (ResultCode.OK, "GoToIdle command completed OK") def is_UpdateDelayModel_allowed(self): """allowed when Devstate is ON and ObsState is READY OR SCANNIGN""" self.logger.debug("Entering is_UpdateDelayModel_allowed()") self.logger.debug("self._obs_state = {}.format(self.dev_state())") if self.dev_state() == tango.DevState.ON and \ self._obs_state in [ObsState.READY, ObsState.SCANNING]: return True return False @command( dtype_in='str', doc_in="Delay model, given per frequency slice" ) def UpdateDelayModel(self, argin): # PROTECTED REGION ID(Vcc.UpdateDelayModel) ENABLED START # """update VCC's delay model(serialized JSON object)""" self.logger.debug("Entering UpdateDelayModel()") argin = json.loads(argin) self.logger.debug(("self._receptor_ID = {}". format(self._receptor_ID))) for delayDetails in argin: self.logger.debug(("delayDetails[receptor] = {}". format(delayDetails["receptor"]))) if delayDetails["receptor"] != self._receptor_ID: continue for frequency_slice in delayDetails["receptorDelayDetails"]: if 1 <= frequency_slice["fsid"] <= 26: if len(frequency_slice["delayCoeff"]) == 6: self._delay_model[frequency_slice["fsid"] - 1] = \ frequency_slice["delayCoeff"] else: log_msg = "'delayCoeff' not valid for frequency slice {} of " \ "receptor {}".format(frequency_slice["fsid"], self._receptor_ID) self.logger.error(log_msg) else: log_msg = "'fsid' {} not valid for receptor {}".format( frequency_slice["fsid"], self._receptor_ID ) self.logger.error(log_msg) # PROTECTED REGION END # // Vcc.UpdateDelayModel def is_UpdateJonesMatrix_allowed(self): """allowed when Devstate is ON and ObsState is READY OR SCANNINNG""" if self.dev_state() == tango.DevState.ON and \ self._obs_state in [ObsState.READY, ObsState.SCANNING]: return True return False @command( dtype_in='str', doc_in="Jones Matrix, given per frequency slice" ) def UpdateJonesMatrix(self, argin): # PROTECTED REGION ID(Vcc.UpdateJonesMatrix) ENABLED START # self.logger.debug("Vcc.UpdateJonesMatrix") """update FSP's Jones matrix (serialized JSON object)""" argin = json.loads(argin) for receptor in argin: if receptor["receptor"] == self._receptor_ID: for frequency_slice in receptor["receptorMatrix"]: fs_id = frequency_slice["fsid"] matrix = frequency_slice["matrix"] if 1 <= fs_id <= 26: if len(matrix) == 16: self._jones_matrix[fs_id-1] = matrix.copy() else: log_msg = "'matrix' not valid for frequency slice {} of " \ "receptor {}".format(fs_id, self._receptor_ID) self.logger.error(log_msg) else: log_msg = "'fsid' {} not valid for receptor {}".format( fs_id, self._receptor_ID ) self.logger.error(log_msg) # PROTECTED REGION END # // Vcc.UpdateJonesMatrix def is_ValidateSearchWindow_allowed(self): # This command has no constraints: return True @command( dtype_in='str', doc_in='JSON object to configure a search window' ) def ValidateSearchWindow(self, argin): """validate a search window configuration. The input is JSON object with the search window parameters. Called by the subarray""" # try to deserialize input string to a JSON object self.logger.debug("Entering ValidateSearchWindow()") try: argin = json.loads(argin) except json.JSONDecodeError: # argument not a valid JSON object msg = "Search window configuration object is not a valid JSON object." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) # Validate searchWindowID. if "search_window_id" in argin: if int(argin["search_window_id"]) in [1, 2]: pass else: # searchWindowID not in valid range msg = "'searchWindowID' must be one of [1, 2] (received {}).".format( str(argin["search_window_id"]) ) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: msg = "Search window specified, but 'searchWindowID' not given." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) # Validate searchWindowTuning. if "search_window_tuning" in argin: freq_band_name = argin["frequency_band"] if freq_band_name not in ["5a", "5b"]: # frequency band is not band 5 frequencyBand_mi = freq_band_dict()[freq_band_name] frequencyBand = ["1", "2", "3", "4", "5a", "5b"].index(argin["frequency_band"]) assert frequencyBand_mi == frequencyBand start_freq_Hz, stop_freq_Hz = [ const.FREQUENCY_BAND_1_RANGE_HZ, const.FREQUENCY_BAND_2_RANGE_HZ, const.FREQUENCY_BAND_3_RANGE_HZ, const.FREQUENCY_BAND_4_RANGE_HZ ][frequencyBand] self.logger.debug("start_freq_Hz = {}".format(start_freq_Hz)) self.logger.debug("stop_freq_Hz = {}".format(stop_freq_Hz)) if start_freq_Hz + argin["frequency_band_offset_stream_1"] <= \ int(argin["search_window_tuning"]) <= \ stop_freq_Hz + argin["frequency_band_offset_stream_1"]: pass else: msg = "'searchWindowTuning' must be within observed band." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: # frequency band 5a or 5b (two streams with bandwidth 2.5 GHz) if argin["band_5_tuning"] == [0,0]: # band 5 tuning not specified in configuration pass else: frequency_band_range_1 = ( argin["band_5_tuning"][0] * 10 ** 9 + argin["frequency_band_offset_stream_1"] - \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2, argin["band_5_tuning"][0] * 10 ** 9 + argin["frequency_band_offset_stream_1"] + \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2 ) frequency_band_range_2 = ( argin["band_5_tuning"][1] * 10 ** 9 + argin["frequency_band_offset_stream_2"] - \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2, argin["band_5_tuning"][1] * 10 ** 9 + argin["frequency_band_offset_stream_2"] + \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2 ) if (frequency_band_range_1[0] <= \ int(argin["search_window_tuning"]) <= \ frequency_band_range_1[1]) or \ (frequency_band_range_2[0] <= \ int(argin["search_window_tuning"]) <= \ frequency_band_range_2[1]): pass else: msg = "'searchWindowTuning' must be within observed band." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: msg = "Search window specified, but 'searchWindowTuning' not given." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) # Validate tdcEnable. if "tdc_enable" in argin: if argin["tdc_enable"] in [True, False]: pass else: msg = "Search window specified, but 'tdcEnable' not given." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: msg = "Search window specified, but 'tdcEnable' not given." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) # Validate tdcNumBits. if argin["tdc_enable"]: if "tdc_num_bits" in argin: if int(argin["tdc_num_bits"]) in [2, 4, 8]: pass else: msg = "'tdcNumBits' must be one of [2, 4, 8] (received {}).".format( str(argin["tdc_num_bits"]) ) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: msg = "Search window specified with TDC enabled, but 'tdcNumBits' not given." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) # Validate tdcPeriodBeforeEpoch. if "tdc_period_before_epoch" in argin: if int(argin["tdc_period_before_epoch"]) > 0: pass else: msg = "'tdcPeriodBeforeEpoch' must be a positive integer (received {}).".format( str(argin["tdc_period_before_epoch"]) ) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: pass # Validate tdcPeriodAfterEpoch. if "tdc_period_after_epoch" in argin: if int(argin["tdc_period_after_epoch"]) > 0: pass else: msg = "'tdcPeriodAfterEpoch' must be a positive integer (received {}).".format( str(argin["tdc_period_after_epoch"]) ) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) else: pass # Validate tdcDestinationAddress. if argin["tdc_enable"]: try: for receptor in argin["tdc_destination_address"]: if int(receptor["receptor_id"]) == self._receptor_ID: # TODO: validate input break else: # receptorID not found raise KeyError # just handle all the errors in one place except KeyError: # tdcDestinationAddress not given or receptorID not in tdcDestinationAddress msg = "Search window specified with TDC enabled, but 'tdcDestinationAddress' " \ "not given or missing receptors." self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureSearchWindow execution", tango.ErrSeverity.ERR) def is_ConfigureSearchWindow_allowed(self): """allowed if DevState is ON and ObsState is CONFIGURING""" if self.dev_state() == tango.DevState.ON and \ (self._obs_state == ObsState.CONFIGURING or \ self._obs_state == ObsState.READY): return True return False @command( dtype_in='str', doc_in='JSON object to configure a search window' ) def ConfigureSearchWindow(self, argin): # PROTECTED REGION ID(Vcc.ConfigureSearchWindow) ENABLED START # # """ configure SearchWindow by sending parameters from the input(JSON) to SearchWindow device. This function is called by the subarray after the configuration has already been validated, so the checks here have been removed to reduce overhead. """ self.logger.debug("Entering ConfigureSearchWindow()") argin = json.loads(argin) # variable to use as SW proxy proxy_sw = None # Configure searchWindowID. #TODO: remove temporary flag to disable Vcc proxies for unit testing if Vcc.TEST_CONTEXT is False: if int(argin["search_window_id"]) == 1: proxy_sw = self._proxy_sw_1 elif int(argin["search_window_id"]) == 2: proxy_sw = self._proxy_sw_2 # Configure searchWindowTuning. if self._frequency_band in list(range(4)): # frequency band is not band 5 proxy_sw.searchWindowTuning = argin["search_window_tuning"] start_freq_Hz, stop_freq_Hz = [ const.FREQUENCY_BAND_1_RANGE_HZ, const.FREQUENCY_BAND_2_RANGE_HZ, const.FREQUENCY_BAND_3_RANGE_HZ, const.FREQUENCY_BAND_4_RANGE_HZ ][self._frequency_band] if start_freq_Hz + self._frequency_band_offset_stream_1 + \ const.SEARCH_WINDOW_BW_HZ / 2 <= \ int(argin["search_window_tuning"]) <= \ stop_freq_Hz + self._frequency_band_offset_stream_1 - \ const.SEARCH_WINDOW_BW_HZ / 2: # this is the acceptable range pass else: # log a warning message log_msg = "'searchWindowTuning' partially out of observed band. " \ "Proceeding." self.logger.warn(log_msg) else: # frequency band 5a or 5b (two streams with bandwidth 2.5 GHz) proxy_sw.searchWindowTuning = argin["search_window_tuning"] frequency_band_range_1 = ( self._stream_tuning[0] * 10 ** 9 + self._frequency_band_offset_stream_1 - \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2, self._stream_tuning[0] * 10 ** 9 + self._frequency_band_offset_stream_1 + \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2 ) frequency_band_range_2 = ( self._stream_tuning[1] * 10 ** 9 + self._frequency_band_offset_stream_2 - \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2, self._stream_tuning[1] * 10 ** 9 + self._frequency_band_offset_stream_2 + \ const.BAND_5_STREAM_BANDWIDTH * 10 ** 9 / 2 ) if (frequency_band_range_1[0] + \ const.SEARCH_WINDOW_BW * 10 ** 6 / 2 <= \ int(argin["search_window_tuning"]) <= \ frequency_band_range_1[1] - \ const.SEARCH_WINDOW_BW * 10 ** 6 / 2) or \ (frequency_band_range_2[0] + \ const.SEARCH_WINDOW_BW * 10 ** 6 / 2 <= \ int(argin["search_window_tuning"]) <= \ frequency_band_range_2[1] - \ const.SEARCH_WINDOW_BW * 10 ** 6 / 2): # this is the acceptable range pass else: # log a warning message log_msg = "'searchWindowTuning' partially out of observed band. " \ "Proceeding." self.logger.warn(log_msg) # Configure tdcEnable. proxy_sw.tdcEnable = argin["tdc_enable"] if argin["tdc_enable"]: proxy_sw.On() else: proxy_sw.Disable() # Configure tdcNumBits. if argin["tdc_enable"]: proxy_sw.tdcNumBits = int(argin["tdc_num_bits"]) # Configure tdcPeriodBeforeEpoch. if "tdc_period_before_epoch" in argin: proxy_sw.tdcPeriodBeforeEpoch = int(argin["tdc_period_before_epoch"]) else: proxy_sw.tdcPeriodBeforeEpoch = 2 log_msg = "Search window specified, but 'tdcPeriodBeforeEpoch' not given. " \ "Defaulting to 2." self.logger.warn(log_msg) # Configure tdcPeriodAfterEpoch. if "tdc_period_after_epoch" in argin: proxy_sw.tdcPeriodAfterEpoch = int(argin["tdc_period_after_epoch"]) else: proxy_sw.tdcPeriodAfterEpoch = 22 log_msg = "Search window specified, but 'tdcPeriodAfterEpoch' not given. " \ "Defaulting to 22." self.logger.warn(log_msg) # Configure tdcDestinationAddress. if argin["tdc_enable"]: for receptor in argin["tdc_destination_address"]: if int(receptor["receptor_id"]) == self._receptor_ID: # TODO: validate input proxy_sw.tdcDestinationAddress = \ receptor["tdc_destination_address"] break