1# Copyright 2016 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Controller module for attenuators. 15 16Sample Config: 17 18.. code-block:: python 19 20 "Attenuator": [ 21 { 22 "address": "192.168.1.12", 23 "port": 23, 24 "model": "minicircuits", 25 "paths": ["AP1-2G", "AP1-5G", "AP2-2G", "AP2-5G"] 26 }, 27 { 28 "address": "192.168.1.14", 29 "port": 23, 30 "model": "minicircuits", 31 "paths": ["AP-DUT"] 32 } 33 ] 34""" 35import importlib 36import logging 37 38MOBLY_CONTROLLER_CONFIG_NAME = "Attenuator" 39# Keys used inside a config dict for attenuator. 40# Keys for making the connection to the attenuator device. Right now we only 41# use telnet lib. This can be refactored when the need for a different 42# communication protocol arises. 43KEY_ADDRESS = "address" 44KEY_PORT = "port" 45# A string that is the model of the attenuator used. This is essentially the 46# module name for the underlying driver for the attenuator hardware. 47KEY_MODEL = "model" 48# A list of strings, each describing what's the connected to this attenuation 49# path 50KEY_PATHS = "paths" 51 52PACKAGE_PATH_TEMPLATE = "mobly.controllers.attenuator_lib.%s" 53 54 55def create(configs): 56 objs = [] 57 for config in configs: 58 _validate_config(config) 59 attenuator_model = config[KEY_MODEL] 60 # Import the correct driver module for the attenuator device 61 module_name = PACKAGE_PATH_TEMPLATE % attenuator_model 62 module = importlib.import_module(module_name) 63 # Create each 64 attenuation_device = module.AttenuatorDevice( 65 path_count=len(config[KEY_PATHS])) 66 attenuation_device.model = attenuator_model 67 instances = attenuation_device.open(config[KEY_ADDRESS], config[KEY_PORT]) 68 for idx, path_name in enumerate(config[KEY_PATHS]): 69 path = AttenuatorPath(attenuation_device, idx=idx, name=path_name) 70 objs.append(path) 71 return objs 72 73 74def destroy(objs): 75 for attenuation_path in objs: 76 attenuation_path.attenuation_device.close() 77 78 79class Error(Exception): 80 """This is the Exception class defined for all errors generated by 81 Attenuator-related modules. 82 """ 83 84 85def _validate_config(config): 86 """Verifies that a config dict for an attenuator device is valid. 87 88 Args: 89 config: A dict that is the configuration for an attenuator device. 90 91 Raises: 92 attenuator.Error: A config is not valid. 93 """ 94 required_keys = [KEY_ADDRESS, KEY_MODEL, KEY_PORT, KEY_PATHS] 95 for key in required_keys: 96 if key not in config: 97 raise Error("Required key %s missing from config %s", (key, config)) 98 99 100class AttenuatorPath: 101 """A convenience class that allows users to control each attenuator path 102 separately as different objects, as opposed to passing in an index number 103 to the functions of an attenuator device object. 104 105 This decouples the test code from the actual attenuator device used in the 106 physical test bed. 107 108 For example, if a test needs to attenuate four signal paths, this allows the 109 test to do: 110 111 .. code-block:: python 112 113 self.attenuation_paths[0].set_atten(50) 114 self.attenuation_paths[1].set_atten(40) 115 116 instead of: 117 118 .. code-block:: python 119 120 self.attenuators[0].set_atten(0, 50) 121 self.attenuators[0].set_atten(1, 40) 122 123 The benefit the former is that the physical test bed can use either four 124 single-channel attenuators, or one four-channel attenuators. Whereas the 125 latter forces the test bed to use a four-channel attenuator. 126 """ 127 128 def __init__(self, attenuation_device, idx=0, name=None): 129 self.model = attenuation_device.model 130 self.attenuation_device = attenuation_device 131 self.idx = idx 132 if (self.idx >= attenuation_device.path_count): 133 raise IndexError("Attenuator index out of range!") 134 135 def set_atten(self, value): 136 """This function sets the attenuation of Attenuator. 137 138 Args: 139 value: This is a floating point value for nominal attenuation to be 140 set. Unit is db. 141 """ 142 self.attenuation_device.set_atten(self.idx, value) 143 144 def get_atten(self): 145 """Gets the current attenuation setting of Attenuator. 146 147 Returns: 148 A float that is the current attenuation value. Unit is db. 149 """ 150 151 return self.attenuation_device.get_atten(self.idx) 152 153 def get_max_atten(self): 154 """Gets the max attenuation supported by the Attenuator. 155 156 Returns: 157 A float that is the max attenuation value. 158 """ 159 return self.attenuation_device.max_atten 160