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