aboutsummaryrefslogtreecommitdiff
path: root/mobly/controllers/attenuator.py
blob: cbe3cb308cf384b81373344671f7cbd127bb4f52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Controller module for attenuators.

Sample Config:

.. code-block:: python

    "Attenuator": [
        {
            "address": "192.168.1.12",
            "port": 23,
            "model": "minicircuits",
            "paths": ["AP1-2G", "AP1-5G", "AP2-2G", "AP2-5G"]
        },
        {
            "address": "192.168.1.14",
            "port": 23,
            "model": "minicircuits",
            "paths": ["AP-DUT"]
        }
    ]
"""
import importlib
import logging

MOBLY_CONTROLLER_CONFIG_NAME = "Attenuator"
# Keys used inside a config dict for attenuator.
# Keys for making the connection to the attenuator device. Right now we only
# use telnet lib. This can be refactored when the need for a different
# communication protocol arises.
KEY_ADDRESS = "address"
KEY_PORT = "port"
# A string that is the model of the attenuator used. This is essentially the
# module name for the underlying driver for the attenuator hardware.
KEY_MODEL = "model"
# A list of strings, each describing what's the connected to this attenuation
# path
KEY_PATHS = "paths"

PACKAGE_PATH_TEMPLATE = "mobly.controllers.attenuator_lib.%s"


def create(configs):
  objs = []
  for config in configs:
    _validate_config(config)
    attenuator_model = config[KEY_MODEL]
    # Import the correct driver module for the attenuator device
    module_name = PACKAGE_PATH_TEMPLATE % attenuator_model
    module = importlib.import_module(module_name)
    # Create each
    attenuation_device = module.AttenuatorDevice(
        path_count=len(config[KEY_PATHS]))
    attenuation_device.model = attenuator_model
    instances = attenuation_device.open(config[KEY_ADDRESS], config[KEY_PORT])
    for idx, path_name in enumerate(config[KEY_PATHS]):
      path = AttenuatorPath(attenuation_device, idx=idx, name=path_name)
      objs.append(path)
  return objs


def destroy(objs):
  for attenuation_path in objs:
    attenuation_path.attenuation_device.close()


class Error(Exception):
  """This is the Exception class defined for all errors generated by
    Attenuator-related modules.
    """


def _validate_config(config):
  """Verifies that a config dict for an attenuator device is valid.

    Args:
        config: A dict that is the configuration for an attenuator device.

    Raises:
        attenuator.Error: A config is not valid.
    """
  required_keys = [KEY_ADDRESS, KEY_MODEL, KEY_PORT, KEY_PATHS]
  for key in required_keys:
    if key not in config:
      raise Error("Required key %s missing from config %s", (key, config))


class AttenuatorPath:
  """A convenience class that allows users to control each attenuator path
    separately as different objects, as opposed to passing in an index number
    to the functions of an attenuator device object.

    This decouples the test code from the actual attenuator device used in the
    physical test bed.

    For example, if a test needs to attenuate four signal paths, this allows the
    test to do:

    .. code-block:: python

        self.attenuation_paths[0].set_atten(50)
        self.attenuation_paths[1].set_atten(40)

    instead of:

    .. code-block:: python

        self.attenuators[0].set_atten(0, 50)
        self.attenuators[0].set_atten(1, 40)

    The benefit the former is that the physical test bed can use either four
    single-channel attenuators, or one four-channel attenuators. Whereas the
    latter forces the test bed to use a four-channel attenuator.
    """

  def __init__(self, attenuation_device, idx=0, name=None):
    self.model = attenuation_device.model
    self.attenuation_device = attenuation_device
    self.idx = idx
    if (self.idx >= attenuation_device.path_count):
      raise IndexError("Attenuator index out of range!")

  def set_atten(self, value):
    """This function sets the attenuation of Attenuator.

        Args:
            value: This is a floating point value for nominal attenuation to be
                set. Unit is db.
        """
    self.attenuation_device.set_atten(self.idx, value)

  def get_atten(self):
    """Gets the current attenuation setting of Attenuator.

        Returns:
            A float that is the current attenuation value. Unit is db.
        """

    return self.attenuation_device.get_atten(self.idx)

  def get_max_atten(self):
    """Gets the max attenuation supported by the Attenuator.

        Returns:
            A float that is the max attenuation value.
        """
    return self.attenuation_device.max_atten