blob: 4274603509a22087535438307ab1039a39c492fa [file] [log] [blame]
Omar El Ayach2f9c4c42019-01-22 18:24:04 -08001#!/usr/bin/env python3
2#
3# Copyright 2019 - 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.
16
Omar El Ayach27522c62019-11-06 08:51:15 -080017import contextlib
18import io
Alfie Chen2dd86982020-10-05 11:49:44 +080019import serial
Omar El Ayach7f7f10e2019-07-10 16:53:39 -070020import time
Omar El Ayach2f9c4c42019-01-22 18:24:04 -080021from acts import logger
22from acts import utils
23
Omar El Ayachd4425122020-01-30 16:28:48 -080024SHORT_SLEEP = 1
Omar El Ayach7f7f10e2019-07-10 16:53:39 -070025CHAMBER_SLEEP = 30
26
Omar El Ayach2f9c4c42019-01-22 18:24:04 -080027
28def create(configs):
29 """Factory method for OTA chambers.
30
31 Args:
32 configs: list of dicts with chamber settings. settings must contain the
33 following: type (string denoting type of chamber)
34 """
35 objs = []
36 for config in configs:
37 try:
Omar El Ayach7f7f10e2019-07-10 16:53:39 -070038 chamber_class = globals()[config['model']]
Omar El Ayach2f9c4c42019-01-22 18:24:04 -080039 except KeyError:
40 raise KeyError('Invalid chamber configuration.')
41 objs.append(chamber_class(config))
42 return objs
43
44
45def detroy(objs):
46 return
47
48
49class OtaChamber(object):
50 """Base class implementation for OTA chamber.
51
52 Base class provides functions whose implementation is shared by all
53 chambers.
54 """
Omar El Ayach7f7f10e2019-07-10 16:53:39 -070055 def reset_chamber(self):
56 """Resets the chamber to its zero/home state."""
57 raise NotImplementedError
58
59 def set_orientation(self, orientation):
Omar El Ayach2f9c4c42019-01-22 18:24:04 -080060 """Set orientation for turn table in OTA chamber.
61
62 Args:
63 angle: desired turn table orientation in degrees
64 """
65 raise NotImplementedError
66
Omar El Ayach7f7f10e2019-07-10 16:53:39 -070067 def set_stirrer_pos(self, stirrer_id, position):
68 """Starts turntables and stirrers in OTA chamber."""
69 raise NotImplementedError
70
71 def start_continuous_stirrers(self):
72 """Starts turntables and stirrers in OTA chamber."""
73 raise NotImplementedError
74
75 def stop_continuous_stirrers(self):
76 """Stops turntables and stirrers in OTA chamber."""
77 raise NotImplementedError
78
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070079 def step_stirrers(self, steps):
80 """Move stepped stirrers in OTA chamber to next step."""
81 raise NotImplementedError
82
Omar El Ayach2f9c4c42019-01-22 18:24:04 -080083
Omar El Ayach68395c42019-03-01 11:25:47 -080084class MockChamber(OtaChamber):
85 """Class that implements mock chamber for test development and debug."""
Omar El Ayach68395c42019-03-01 11:25:47 -080086 def __init__(self, config):
87 self.config = config.copy()
88 self.device_id = self.config['device_id']
89 self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
90 self.device_id))
Omar El Ayach3903e342019-09-17 19:17:03 -070091 self.current_mode = None
Omar El Ayach68395c42019-03-01 11:25:47 -080092
93 def set_orientation(self, orientation):
94 self.log.info('Setting orientation to {} degrees.'.format(orientation))
95
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070096 def reset_chamber(self):
Omar El Ayach7f7f10e2019-07-10 16:53:39 -070097 self.log.info('Resetting chamber to home state')
98
99 def set_stirrer_pos(self, stirrer_id, position):
100 """Starts turntables and stirrers in OTA chamber."""
101 self.log.info('Setting stirrer {} to {}.'.format(stirrer_id, position))
102
103 def start_continuous_stirrers(self):
104 """Starts turntables and stirrers in OTA chamber."""
105 self.log.info('Starting continuous stirrer motion')
106
107 def stop_continuous_stirrers(self):
108 """Stops turntables and stirrers in OTA chamber."""
109 self.log.info('Stopping continuous stirrer motion')
110
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700111 def configure_stepped_stirrers(self, steps):
112 """Programs parameters for stepped stirrers in OTA chamber."""
113 self.log.info('Configuring stepped stirrers')
114
115 def step_stirrers(self, steps):
116 """Move stepped stirrers in OTA chamber to next step."""
117 self.log.info('Moving stirrers to the next step')
118
Omar El Ayach68395c42019-03-01 11:25:47 -0800119
Omar El Ayach2f9c4c42019-01-22 18:24:04 -0800120class OctoboxChamber(OtaChamber):
121 """Class that implements Octobox chamber."""
Omar El Ayach2f9c4c42019-01-22 18:24:04 -0800122 def __init__(self, config):
123 self.config = config.copy()
124 self.device_id = self.config['device_id']
125 self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
126 self.device_id))
127 self.TURNTABLE_FILE_PATH = '/usr/local/bin/fnPerformaxCmd'
Omar El Ayach7e895432019-05-20 10:24:00 -0700128 utils.exe_cmd('sudo {} -d {} -i 0'.format(self.TURNTABLE_FILE_PATH,
129 self.device_id))
Omar El Ayach7d661262019-09-18 08:29:36 -0700130 self.current_mode = None
Omar El Ayach2f9c4c42019-01-22 18:24:04 -0800131
132 def set_orientation(self, orientation):
Omar El Ayach2f9c4c42019-01-22 18:24:04 -0800133 self.log.info('Setting orientation to {} degrees.'.format(orientation))
Omar El Ayach27522c62019-11-06 08:51:15 -0800134 utils.exe_cmd('sudo {} -d {} -p {}'.format(self.TURNTABLE_FILE_PATH,
135 self.device_id,
136 orientation))
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700137
138 def reset_chamber(self):
139 self.log.info('Resetting chamber to home state')
140 self.set_orientation(0)
141
142
143class ChamberAutoConnect(object):
144 def __init__(self, chamber, chamber_config):
145 self._chamber = chamber
146 self._config = chamber_config
147
148 def __getattr__(self, item):
149 def chamber_call(*args, **kwargs):
150 self._chamber.connect(self._config['ip_address'],
151 self._config['username'],
152 self._config['password'])
153 return getattr(self._chamber, item)(*args, **kwargs)
154
155 return chamber_call
156
157
158class BluetestChamber(OtaChamber):
159 """Class that implements Octobox chamber."""
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700160 def __init__(self, config):
Omar El Ayach033b33a2019-08-01 11:32:08 -0700161 import flow
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700162 self.config = config.copy()
163 self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
164 self.config['ip_address']))
165 self.chamber = ChamberAutoConnect(flow.Flow(), self.config)
166 self.stirrer_ids = [0, 1, 2]
167 self.current_mode = None
168
Omar El Ayach27522c62019-11-06 08:51:15 -0800169 # Capture print output decorator
170 @staticmethod
171 def _capture_output(func, *args, **kwargs):
172 """Creates a decorator to capture stdout from bluetest module"""
173 f = io.StringIO()
174 with contextlib.redirect_stdout(f):
175 func(*args, **kwargs)
176 output = f.getvalue()
177 return output
178
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700179 def _connect(self):
180 self.chamber.connect(self.config['ip_address'],
181 self.config['username'], self.config['password'])
182
183 def _init_manual_mode(self):
184 self.current_mode = 'manual'
185 for stirrer_id in self.stirrer_ids:
Omar El Ayach27522c62019-11-06 08:51:15 -0800186 out = self._capture_output(
187 self.chamber.chamber_stirring_manual_init, stirrer_id)
188 if "failed" in out:
189 self.log.warning("Initialization error: {}".format(out))
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700190 time.sleep(CHAMBER_SLEEP)
191
192 def _init_continuous_mode(self):
193 self.current_mode = 'continuous'
Omar El Ayach27522c62019-11-06 08:51:15 -0800194 self.chamber.chamber_stirring_continuous_init()
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700195
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700196 def _init_stepped_mode(self, steps):
197 self.current_mode = 'stepped'
198 self.current_stepped_pos = 0
199 self.chamber.chamber_stirring_stepped_init(steps, False)
200
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700201 def set_stirrer_pos(self, stirrer_id, position):
202 if self.current_mode != 'manual':
203 self._init_manual_mode()
204 self.log.info('Setting stirrer {} to {}.'.format(stirrer_id, position))
Omar El Ayach27522c62019-11-06 08:51:15 -0800205 out = self._capture_output(
206 self.chamber.chamber_stirring_manual_set_pos, stirrer_id, position)
207 if "failed" in out:
208 self.log.warning("Bluetest error: {}".format(out))
209 self.log.warning("Set position failed. Retrying.")
210 self.current_mode = None
211 self.set_stirrer_pos(stirrer_id, position)
212 else:
213 self._capture_output(self.chamber.chamber_stirring_manual_wait,
214 CHAMBER_SLEEP)
215 self.log.warning('Stirrer {} at {}.'.format(stirrer_id, position))
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700216
217 def set_orientation(self, orientation):
218 self.set_stirrer_pos(2, orientation * 100 / 360)
219
220 def start_continuous_stirrers(self):
221 if self.current_mode != 'continuous':
222 self._init_continuous_mode()
Omar El Ayach27522c62019-11-06 08:51:15 -0800223 self.chamber.chamber_stirring_continuous_start()
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700224
225 def stop_continuous_stirrers(self):
Omar El Ayach27522c62019-11-06 08:51:15 -0800226 self.chamber.chamber_stirring_continuous_stop()
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700227
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700228 def step_stirrers(self, steps):
229 if self.current_mode != 'stepped':
230 self._init_stepped_mode(steps)
231 if self.current_stepped_pos == 0:
232 self.current_stepped_pos += 1
233 return
234 self.current_stepped_pos += 1
235 self.chamber.chamber_stirring_stepped_next_pos()
236
Omar El Ayach7f7f10e2019-07-10 16:53:39 -0700237 def reset_chamber(self):
238 if self.current_mode == 'continuous':
239 self._init_continuous_mode()
Omar El Ayachd4425122020-01-30 16:28:48 -0800240 time.sleep(SHORT_SLEEP)
241 self._init_continuous_mode()
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700242 else:
Alfie Chen2dd86982020-10-05 11:49:44 +0800243 self._init_manual_mode()
244
245
246class EInstrumentChamber(OtaChamber):
247 """Class that implements Einstrument Chamber."""
248 def __init__(self, config):
249 self.config = config.copy()
250 self.device_id = self.config['device_id']
251 self.log = logger.create_tagged_trace_logger('EInstrumentChamber|{}'.format(
252 self.device_id))
253 self.current_mode = None
254 self.ser = self._get_serial(config['port'])
255
256 def _get_serial(self, port, baud=9600):
257 """Read com port.
258
259 Args:
260 port: turn table com port
261 baud: baud rate
262 """
263 ser = serial.Serial(port, baud)
264 return ser
265
266 def set_orientation(self, orientation):
267 if int(orientation) > 360:
268 orientation = int(orientation) % 360
269 elif int(orientation) < 0:
270 orientation = 0
271 self.log.info('Setting orientation to {} degrees.'.format(orientation))
272 orientation = str('DG') + str(orientation) + str(';')
273 self.ser.write(orientation.encode())
274 return orientation
275
276 def reset_chamber(self):
277 self.log.info('Resetting turn table to zero degree')
278 self.set_orientation(0)