Home | History | Annotate | Download | only in controllers
      1 #
      2 #   Copyright 2016 - The Android Open Source Project
      3 #
      4 #   Licensed under the Apache License, Version 2.0 (the "License");
      5 #   you may not use this file except in compliance with the License.
      6 #   You may obtain a copy of the License at
      7 #
      8 #       http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 #   Unless required by applicable law or agreed to in writing, software
     11 #   distributed under the License is distributed on an "AS IS" BASIS,
     12 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 #   See the License for the specific language governing permissions and
     14 #   limitations under the License.
     15 
     16 import importlib
     17 
     18 from vts.runners.host.keys import Config
     19 
     20 VTS_CONTROLLER_CONFIG_NAME = "Attenuator"
     21 VTS_CONTROLLER_REFERENCE_NAME = "attenuators"
     22 
     23 
     24 def create(configs, logger):
     25     objs = []
     26     for c in configs:
     27         attn_model = c["Model"]
     28         # Default to telnet.
     29         protocol = "telnet"
     30         if "Protocol" in c:
     31             protocol = c["Protocol"]
     32         module_name = "vts.utils.python.controllers.attenuator_lib.%s.%s" % (
     33             attn_model, protocol)
     34         module = importlib.import_module(module_name)
     35         inst_cnt = c["InstrumentCount"]
     36         attn_inst = module.AttenuatorInstrument(inst_cnt)
     37         attn_inst.model = attn_model
     38         insts = attn_inst.open(c[Config.key_address.value],
     39                                c[Config.key_port.value])
     40         for i in range(inst_cnt):
     41             attn = Attenuator(attn_inst, idx=i)
     42             if "Paths" in c:
     43                 try:
     44                     setattr(attn, "path", c["Paths"][i])
     45                 except IndexError:
     46                     logger.error("No path specified for attenuator %d." % i)
     47                     raise
     48             objs.append(attn)
     49     return objs
     50 
     51 
     52 def destroy(objs):
     53     return
     54 
     55 
     56 r"""
     57 Base classes which define how attenuators should be accessed, managed, and manipulated.
     58 
     59 Users will instantiate a specific child class, but almost all operation should be performed
     60 on the methods and data members defined here in the base classes or the wrapper classes.
     61 """
     62 
     63 
     64 class AttenuatorError(Exception):
     65     r"""This is the Exception class defined for all errors generated by Attenuator-related modules.
     66     """
     67     pass
     68 
     69 
     70 class InvalidDataError(AttenuatorError):
     71     r"""This exception is  thrown when an unexpected result is seen on the transport layer below
     72     the module.
     73 
     74     When this exception is seen, closing an re-opening the link to the attenuator instrument is
     75     probably necessary. Something has gone wrong in the transport.
     76     """
     77     pass
     78 
     79 
     80 class InvalidOperationError(AttenuatorError):
     81     r"""Certain methods may only be accessed when the instance upon which they are invoked is in
     82     a certain state. This indicates that the object is not in the correct state for a method to be
     83     called.
     84     """
     85     pass
     86 
     87 
     88 class AttenuatorInstrument():
     89     r"""This is a base class that defines the primitive behavior of all attenuator
     90     instruments.
     91 
     92     The AttenuatorInstrument class is designed to provide a simple low-level interface for
     93     accessing any step attenuator instrument comprised of one or more attenuators and a
     94     controller. All AttenuatorInstruments should override all the methods below and call
     95     AttenuatorInstrument.__init__ in their constructors. Outside of setup/teardown,
     96     devices should be accessed via this generic "interface".
     97     """
     98     model = None
     99     INVALID_MAX_ATTEN = 999.9
    100 
    101     def __init__(self, num_atten=0):
    102         r"""This is the Constructor for Attenuator Instrument.
    103 
    104         Parameters
    105         ----------
    106         num_atten : This optional parameter is the number of attenuators contained within the
    107         instrument. In some instances setting this number to zero will allow the driver to
    108         auto-determine, the number of attenuators; however, this behavior is not guaranteed.
    109 
    110         Raises
    111         ------
    112         NotImplementedError
    113             This constructor should never be called directly. It may only be called by a child.
    114 
    115         Returns
    116         -------
    117         self
    118             Returns a newly constructed AttenuatorInstrument
    119         """
    120 
    121         if type(self) is AttenuatorInstrument:
    122             raise NotImplementedError(
    123                 "Base class should not be instantiated directly!")
    124 
    125         self.num_atten = num_atten
    126         self.max_atten = AttenuatorInstrument.INVALID_MAX_ATTEN
    127         self.properties = None
    128 
    129     def set_atten(self, idx, value):
    130         r"""This function sets the attenuation of an attenuator given its index in the instrument.
    131 
    132         Parameters
    133         ----------
    134         idx : This zero-based index is the identifier for a particular attenuator in an
    135         instrument.
    136         value : This is a floating point value for nominal attenuation to be set.
    137 
    138         Raises
    139         ------
    140         NotImplementedError
    141             This constructor should never be called directly. It may only be called by a child.
    142         """
    143         raise NotImplementedError("Base class should not be called directly!")
    144 
    145     def get_atten(self, idx):
    146         r"""This function returns the current attenuation from an attenuator at a given index in
    147         the instrument.
    148 
    149         Parameters
    150         ----------
    151         idx : This zero-based index is the identifier for a particular attenuator in an instrument.
    152 
    153         Raises
    154         ------
    155         NotImplementedError
    156             This constructor should never be called directly. It may only be called by a child.
    157 
    158         Returns
    159         -------
    160         float
    161             Returns a the current attenuation value
    162         """
    163         raise NotImplementedError("Base class should not be called directly!")
    164 
    165 
    166 class Attenuator():
    167     r"""This class defines an object representing a single attenuator in a remote instrument.
    168 
    169     A user wishing to abstract the mapping of attenuators to physical instruments should use this
    170     class, which provides an object that obscures the physical implementation an allows the user
    171     to think only of attenuators regardless of their location.
    172     """
    173 
    174     def __init__(self, instrument, idx=0, offset=0):
    175         r"""This is the constructor for Attenuator
    176 
    177         Parameters
    178         ----------
    179         instrument : Reference to an AttenuatorInstrument on which the Attenuator resides
    180         idx : This zero-based index is the identifier for a particular attenuator in an instrument.
    181         offset : A power offset value for the attenuator to be used when performing future
    182         operations. This could be used for either calibration or to allow group operations with
    183         offsets between various attenuators.
    184 
    185         Raises
    186         ------
    187         TypeError
    188             Requires a valid AttenuatorInstrument to be passed in.
    189         IndexError
    190             The index of the attenuator in the AttenuatorInstrument must be within the valid range.
    191 
    192         Returns
    193         -------
    194         self
    195             Returns a newly constructed Attenuator
    196         """
    197         if not isinstance(instrument, AttenuatorInstrument):
    198             raise TypeError("Must provide an Attenuator Instrument Ref")
    199         self.model = instrument.model
    200         self.instrument = instrument
    201         self.idx = idx
    202         self.offset = offset
    203 
    204         if (self.idx >= instrument.num_atten):
    205             raise IndexError(
    206                 "Attenuator index out of range for attenuator instrument")
    207 
    208     def set_atten(self, value):
    209         r"""This function sets the attenuation of Attenuator.
    210 
    211         Parameters
    212         ----------
    213         value : This is a floating point value for nominal attenuation to be set.
    214 
    215         Raises
    216         ------
    217         ValueError
    218             The requested set value+offset must be less than the maximum value.
    219         """
    220 
    221         if value + self.offset > self.instrument.max_atten:
    222             raise ValueError(
    223                 "Attenuator Value+Offset greater than Max Attenuation!")
    224 
    225         self.instrument.set_atten(self.idx, value + self.offset)
    226 
    227     def get_atten(self):
    228         r"""This function returns the current attenuation setting of Attenuator, normalized by
    229         the set offset.
    230 
    231         Returns
    232         -------
    233         float
    234             Returns a the current attenuation value
    235         """
    236 
    237         return self.instrument.get_atten(self.idx) - self.offset
    238 
    239     def get_max_atten(self):
    240         r"""This function returns the max attenuation setting of Attenuator, normalized by
    241         the set offset.
    242 
    243         Returns
    244         -------
    245         float
    246             Returns a the max attenuation value
    247         """
    248         if (self.instrument.max_atten ==
    249                 AttenuatorInstrument.INVALID_MAX_ATTEN):
    250             raise ValueError("Invalid Max Attenuator Value")
    251 
    252         return self.instrument.max_atten - self.offset
    253 
    254 
    255 class AttenuatorGroup(object):
    256     r"""This is a handy abstraction for groups of attenuators that will share behavior.
    257 
    258     Attenuator groups are intended to further facilitate abstraction of testing functions from
    259     the physical objects underlying them. By adding attenuators to a group, it is possible to
    260     operate on functional groups that can be thought of in a common manner in the test. This
    261     class is intended to provide convenience to the user and avoid re-implementation of helper
    262     functions and small loops scattered throughout user code.
    263 
    264     """
    265 
    266     def __init__(self, name=""):
    267         r"""This is the constructor for AttenuatorGroup
    268 
    269         Parameters
    270         ----------
    271         name : The name is an optional parameter intended to further facilitate the passing of
    272         easily tracked groups of attenuators throughout code. It is left to the user to use the
    273         name in a way that meets their needs.
    274 
    275         Returns
    276         -------
    277         self
    278             Returns a newly constructed AttenuatorGroup
    279         """
    280         self.name = name
    281         self.attens = []
    282         self._value = 0
    283 
    284     def add_from_instrument(self, instrument, indices):
    285         r"""This function provides a way to create groups directly from the Attenuator Instrument.
    286 
    287         This function will create Attenuator objects for all of the indices passed in and add
    288         them to the group.
    289 
    290         Parameters
    291         ----------
    292         instrument : A ref to the instrument from which attenuators will be added
    293         indices : You pay pass in the indices either as a range, a list, or a single integer.
    294 
    295         Raises
    296         ------
    297         TypeError
    298             Requires a valid AttenuatorInstrument to be passed in.
    299         """
    300 
    301         if not instrument or not isinstance(instrument, AttenuatorInstrument):
    302             raise TypeError("Must provide an Attenuator Instrument Ref")
    303 
    304         if type(indices) is range or type(indices) is list:
    305             for i in indices:
    306                 self.attens.append(Attenuator(instrument, i))
    307         elif type(indices) is int:
    308             self.attens.append(Attenuator(instrument, indices))
    309 
    310     def add(self, attenuator):
    311         r"""This function adds an already constructed Attenuator object to the AttenuatorGroup.
    312 
    313         Parameters
    314         ----------
    315         attenuator : An Attenuator object.
    316 
    317         Raises
    318         ------
    319         TypeError
    320             Requires a valid Attenuator to be passed in.
    321         """
    322 
    323         if not isinstance(attenuator, Attenuator):
    324             raise TypeError("Must provide an Attenuator")
    325 
    326         self.attens.append(attenuator)
    327 
    328     def synchronize(self):
    329         r"""This function can be called to ensure all Attenuators within a group are set
    330         appropriately.
    331         """
    332 
    333         self.set_atten(self._value)
    334 
    335     def is_synchronized(self):
    336         r"""This function queries all the Attenuators in the group to determine whether or not
    337         they are synchronized.
    338 
    339         Returns
    340         -------
    341         bool
    342             True if the attenuators are synchronized.
    343         """
    344 
    345         for att in self.attens:
    346             if att.get_atten() != self._value:
    347                 return False
    348         return True
    349 
    350     def set_atten(self, value):
    351         r"""This function sets the attenuation value of all attenuators in the group.
    352 
    353         Parameters
    354         ----------
    355         value : This is a floating point value for nominal attenuation to be set.
    356 
    357         Returns
    358         -------
    359         bool
    360             True if the attenuators are synchronized.
    361         """
    362 
    363         value = float(value)
    364         for att in self.attens:
    365             att.set_atten(value)
    366         self._value = value
    367 
    368     def get_atten(self):
    369         r"""This function returns the current attenuation setting of AttenuatorGroup.
    370 
    371         This returns a cached value that assumes the attenuators are synchronized. It avoids a
    372         relatively expensive call for a common operation, and trusts the user to ensure
    373         synchronization.
    374 
    375         Returns
    376         -------
    377         float
    378             Returns a the current attenuation value for the group, which is independent of any
    379             individual attenuator offsets.
    380         """
    381 
    382         return float(self._value)
    383