1# Copyright (c) 2018 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from autotest_lib.client.common_lib import error
6from autotest_lib.server.cros.network import telnet_helper
7
8
9class Attenuator(object):
10    """Represents a minicircuits telnet-controlled 4-channel variable
11    attenuator."""
12
13    def __init__(self, host, num_atten=0):
14        self._tnhelper = telnet_helper.TelnetHelper(
15                tx_cmd_separator="\r\n", rx_cmd_separator="\r\n", prompt="")
16        self.host = host
17        self.num_atten = num_atten
18        self.open(host)
19
20
21    def __del__(self):
22        if self.is_open():
23            self.close()
24
25    def open(self, host, port=22):
26        """Opens a telnet connection to the attenuator and queries basic
27        information.
28
29        @param host: Valid hostname
30        @param port: Optional port number, defaults to 22
31
32        """
33        self._tnhelper.open(host, port)
34
35        if self.num_atten == 0:
36            self.num_atten = 1
37
38        config_str = self._tnhelper.cmd("MN?")
39
40        if config_str.startswith("MN="):
41            config_str = config_str[len("MN="):]
42
43        self.properties = dict(zip(['model', 'max_freq', 'max_atten'],
44                                   config_str.split("-", 2)))
45        self.max_atten = float(self.properties['max_atten'])
46        self.min_atten = 0
47
48    def is_open(self):
49        """Returns true if telnet connection to attenuator is open."""
50        return bool(self._tnhelper.is_open())
51
52    def reopen(self, host, port=22):
53        """Close and reopen telnet connection to the attenuator."""
54        self._tnhelper.close()
55        self._tnhelper.open(host, port)
56
57    def close(self):
58        """Closes the telnet connection."""
59        self._tnhelper.close()
60
61    def set_atten(self, channel, value):
62        """Set attenuation of the attenuator for given channel (0-3).
63
64        @param channel: Zero-based attenuator channel to set attenuation (0-3)
65        @param value: Floating point value for attenuation to be set
66        """
67        if not self.is_open():
68            raise error.TestError("Connection not open!")
69
70        if channel >= self.num_atten:
71            raise error.TestError("Attenuator channel out of range! Requested "
72                                  "%d; max available %d" %
73                                  (channel, self.num_atten))
74
75        if not (self.min_atten <= value <= self.max_atten):
76            raise error.TestError("Requested attenuator value %d not in range "
77                                  "(%d - %d)" %
78                                  (value, self.min_atten, self.max_atten))
79        # The actual device uses one-based channel for channel numbers.
80        if (int(self._tnhelper.cmd("CHAN:%d:SETATT:%d" %
81                                  (channel + 1, value))) != 1):
82            raise error.TestError("Error while setting attenuation on %d" %
83                                  channel)
84
85    def get_atten(self, channel):
86        """Returns current attenuation of the attenuator for given channel.
87
88        @param channel: Attenuator channel
89        @returns the current attenuation value as a float
90        """
91        if not self.is_open():
92            raise error.TestError("Connection not open!")
93
94        if channel >= self.num_atten or channel < 0:
95            raise error.TestError("Attenuator channel out of range! Requested "
96                                  "%d; should be between 0 and max available "
97                                  "%d" % (channel, self.num_atten))
98
99        return float(self._tnhelper.cmd("CHAN:%d:ATT?" % (channel + 1)))
100