1#!/usr/bin/env python3
2#
3#   Copyright 2017 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16import collections
17
18from acts.controllers.relay_lib.ak_xb10_speaker import AkXB10Speaker
19from acts.controllers.relay_lib.dongles import SingleButtonDongle
20from acts.controllers.relay_lib.dongles import ThreeButtonDongle
21from acts.controllers.relay_lib.earstudio_receiver import EarstudioReceiver
22from acts.controllers.relay_lib.errors import RelayConfigError
23from acts.controllers.relay_lib.fugu_remote import FuguRemote
24from acts.controllers.relay_lib.generic_relay_device import GenericRelayDevice
25from acts.controllers.relay_lib.headset import Headset
26from acts.controllers.relay_lib.helpers import validate_key
27from acts.controllers.relay_lib.i6s_headset import I6sHeadset
28from acts.controllers.relay_lib.jaybird_x3 import JaybirdX3Earbuds
29from acts.controllers.relay_lib.logitech_headset import LogitechAudioReceiver
30from acts.controllers.relay_lib.power_supply import PowerSupply
31from acts.controllers.relay_lib.rdl_relay_board import RdlRelayBoard
32from acts.controllers.relay_lib.sain_smart_board import SainSmartBoard
33from acts.controllers.relay_lib.sain_smart_8_channel_usb_relay_board import SainSmart8ChannelUsbRelayBoard
34from acts.controllers.relay_lib.skullcandy import Skullcandy
35from acts.controllers.relay_lib.sony_xb2_speaker import SonyXB2Speaker
36from acts.controllers.relay_lib.sony_xb20_speaker import SonyXB20Speaker
37from acts.controllers.relay_lib.tao_tronics_headset import TaoTronicsCarkit
38
39
40class RelayRig:
41    """A group of relay boards and their connected devices.
42
43    This class is also responsible for handling the creation of the relay switch
44    boards, as well as the devices and relays associated with them.
45
46    The boards dict can contain different types of relay boards. They share a
47    common interface through inheriting from RelayBoard. This layer can be
48    ignored by the user.
49
50    The relay devices are stored in a dict of (device_name: device). These
51    device references should be used by the user when they want to directly
52    interface with the relay switches. See RelayDevice or GeneralRelayDevice for
53    implementation.
54
55    """
56    DUPLICATE_ID_ERR_MSG = 'The {} "{}" is not unique. Duplicated in:\n {}'
57
58    # A dict of lambdas that instantiate relay board upon invocation.
59    # The key is the class type name, the value is the lambda.
60    _board_constructors = {
61        'SainSmartBoard':
62        lambda x: SainSmartBoard(x),
63        'RdlRelayBoard':
64        lambda x: RdlRelayBoard(x),
65        'SainSmart8ChannelUsbRelayBoard':
66        lambda x: SainSmart8ChannelUsbRelayBoard(x),
67    }
68
69    # Similar to the dict above, except for devices.
70    _device_constructors = {
71        'GenericRelayDevice': lambda x, rig: GenericRelayDevice(x, rig),
72        'FuguRemote': lambda x, rig: FuguRemote(x, rig),
73        'I6sHeadset': lambda x, rig: I6sHeadset(x, rig),
74        'JaybirdX3Earbuds': lambda x, rig: JaybirdX3Earbuds(x, rig),
75        "LogitechAudioReceiver" :lambda x, rig: LogitechAudioReceiver(x, rig),
76        'SonyXB2Speaker': lambda x, rig: SonyXB2Speaker(x, rig),
77        'SonyXB20Speaker': lambda x, rig: SonyXB20Speaker(x, rig),
78        'TaoTronicsCarkit': lambda x, rig: TaoTronicsCarkit(x, rig),
79        'AkXB10Speaker': lambda x, rig: AkXB10Speaker(x, rig),
80        'SingleButtonDongle': lambda x, rig: SingleButtonDongle(x, rig),
81        'ThreeButtonDongle': lambda x, rig: ThreeButtonDongle(x, rig),
82        'EarstudioReceiver': lambda x, rig: EarstudioReceiver(x, rig),
83        'Headset': lambda x, rig: Headset(x, rig),
84        'Skullcandy': lambda x, rig: Skullcandy(x, rig),
85        'PowerSupply': lambda x, rig: PowerSupply(x, rig),
86    }
87
88    def __init__(self, config):
89        self.relays = dict()
90        self.boards = dict()
91        self.devices = collections.OrderedDict()
92
93        validate_key('boards', config, list, 'relay config file')
94
95        for elem in config['boards']:
96            board = self.create_relay_board(elem)
97            if board.name in self.boards:
98                raise RelayConfigError(
99                    self.DUPLICATE_ID_ERR_MSG.format('name', elem['name'],
100                                                     elem))
101            self.boards[board.name] = board
102
103        # Note: 'boards' is a necessary value, 'devices' is not.
104        if 'devices' in config:
105            for elem in config['devices']:
106                relay_device = self.create_relay_device(elem)
107                if relay_device.name in self.devices:
108                    raise RelayConfigError(
109                        self.DUPLICATE_ID_ERR_MSG.format(
110                            'name', elem['name'], elem))
111                self.devices[relay_device.name] = relay_device
112        else:
113            device_config = dict()
114            device_config['name'] = 'GenericRelayDevice'
115            device_config['relays'] = dict()
116            for relay_id in self.relays:
117                device_config['relays'][relay_id] = relay_id
118            self.devices['device'] = self.create_relay_device(device_config)
119
120    def create_relay_board(self, config):
121        """Builds a RelayBoard from the given config.
122
123        Args:
124            config: An object containing 'type', 'name', 'relays', and
125            (optionally) 'properties'. See the example json file.
126
127        Returns:
128            A RelayBoard with the given type found in the config.
129
130        Raises:
131            RelayConfigError if config['type'] doesn't exist or is not a string.
132
133        """
134        validate_key('type', config, str, '"boards" element')
135        try:
136            ret = self._board_constructors[config['type']](config)
137        except LookupError:
138            raise RelayConfigError(
139                'RelayBoard with type {} not found. Has it been added '
140                'to the _board_constructors dict?'.format(config['type']))
141        for _, relay in ret.relays.items():
142            self.relays[relay.relay_id] = relay
143        return ret
144
145    def create_relay_device(self, config):
146        """Builds a RelayDevice from the given config.
147
148        When given no 'type' key in the config, the function will default to
149        returning a GenericRelayDevice with the relays found in the 'relays'
150        array.
151
152        Args:
153            config: An object containing 'name', 'relays', and (optionally)
154            type.
155
156        Returns:
157            A RelayDevice with the given type found in the config. If no type is
158            found, it will default to GenericRelayDevice.
159
160        Raises:
161            RelayConfigError if the type given does not match any from the
162            _device_constructors dictionary.
163
164        """
165        if 'type' in config:
166            if config['type'] not in RelayRig._device_constructors:
167                raise RelayConfigError(
168                    'Device with type {} not found. Has it been added '
169                    'to the _device_constructors dict?'.format(config['type']))
170            else:
171                device = self._device_constructors[config['type']](config,
172                                                                   self)
173
174        else:
175            device = GenericRelayDevice(config, self)
176
177        return device
178