OLD | NEW |
(Empty) | |
| 1 # Copyright 2017 The Chromium 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 |
| 5 import atexit |
| 6 import py_utils |
| 7 import re |
| 8 import subprocess |
| 9 import tempfile |
| 10 |
| 11 USB_PASSTHROUGH_OFF = "off" |
| 12 USB_PASSTHROUGH_ON = "on" |
| 13 USB_PASSTHROUGH_AUTO = "auto" |
| 14 |
| 15 UsbModes = [USB_PASSTHROUGH_OFF, USB_PASSTHROUGH_ON, USB_PASSTHROUGH_AUTO] |
| 16 |
| 17 class MonsoonWrapper(object): |
| 18 def __init__(self, script): |
| 19 self.script = script |
| 20 self._process = None |
| 21 # Register atexit handler to kill any monsoon processes in the case of |
| 22 # abnormal program termination. |
| 23 atexit.register(self.TerminateMonsoonProcess) |
| 24 |
| 25 def ReadStatusProperties(self): |
| 26 """ Reads status properties from Monsoon, returning a dictionary of |
| 27 properties. |
| 28 """ |
| 29 status_text = self.ExecuteBlocking(["--status"]) |
| 30 status = {} |
| 31 for status_line in status_text: |
| 32 # Sometimes transient USB serial errors are reported. Just ignore them |
| 33 # and only use strings with : in them. |
| 34 if ":" in status_line: |
| 35 key, value = status_line.split(':') |
| 36 status[key] = value |
| 37 return status |
| 38 |
| 39 def EnableUSB(self): |
| 40 """ Shorthand for SetUSBMode(USB_PASSTHROUGH_ON) """ |
| 41 self.SetUSBMode(USB_PASSTHROUGH_ON) |
| 42 |
| 43 def DisableUSB(self): |
| 44 """ Shorthand for SetUSBMode(USB_PASSTHROUGH_OFF) """ |
| 45 self.SetUSBMode(USB_PASSTHROUGH_OFF) |
| 46 |
| 47 def SetUSBMode(self, mode): |
| 48 """ Set the Monsoon USB mode. |
| 49 """ |
| 50 if mode not in UsbModes: raise Exception("Invalid USB mode " + mode) |
| 51 # Sometimes Monsoon fails to make USB passthrough mode changes |
| 52 # permanent if there are transient USB serial errors. |
| 53 retry_count = 5 |
| 54 while self.GetUSBMode() is not mode: |
| 55 self.ExecuteBlocking(["--usbpassthrough", mode]) |
| 56 if retry_count is 0: Exception("Failed to set USB mode!") |
| 57 retry_count -= 1 |
| 58 |
| 59 def GetUSBMode(self): |
| 60 """ Retrieves the current Monsoon USB mode, returning USB_PASSTHROUGH_OFF |
| 61 if the status could not be retrieved. |
| 62 """ |
| 63 properties = self.ReadStatusProperties() |
| 64 if "usbPassthroughMode" in properties: |
| 65 return UsbModes[int(properties["usbPassthroughMode"])] |
| 66 return USB_PASSTHROUGH_OFF |
| 67 |
| 68 def ReadPower(self, time, frequency): |
| 69 """ Reads power from the Monsoon device over a specified perdiod while |
| 70 blocking, returning a file containing raw monsoon data. """ |
| 71 sample_count = time * frequency |
| 72 args = ["--samples", str(sample_count), "--hz", str(frequency), |
| 73 "--timestamp"] |
| 74 return self.ExecuteBlocking(args) |
| 75 |
| 76 def ExecuteBlocking(self, args): |
| 77 """ Executes a blocking operation on the Monsoon device, returning a file |
| 78 containing stdout output. """ |
| 79 if self.IsProcessRunning(): |
| 80 raise Exception("Monsoon subprocess already running!") |
| 81 result_file = tempfile.TemporaryFile() |
| 82 subprocess.check_call([self.script] + args, stdout=result_file) |
| 83 result_file.seek(0) |
| 84 return result_file |
| 85 |
| 86 def BeginReadPower(self, frequency, out=subprocess.PIPE): |
| 87 """ Begins a non-blocking read of power from the Monsoon device, returning |
| 88 the stdout file of the subprocess. |
| 89 """ |
| 90 args = ["--samples", "-1", "--hz", str(frequency), "--timestamp"] |
| 91 return self.BeginExecution(args=args, out=out) |
| 92 |
| 93 def BeginExecution(self, args = [], out=subprocess.PIPE): |
| 94 """ Begins a non-blocking operation on the Monsoon device, returning the |
| 95 stdout file of the subprocess. |
| 96 """ |
| 97 if self.IsProcessRunning(): |
| 98 raise Exception("Monsoon subprocess already running!") |
| 99 self._process = subprocess.Popen([self.script] + args, stdout=out) |
| 100 return self._process.stdout |
| 101 |
| 102 def EndExecution(self): |
| 103 """ Ends non-blocking execution of the Monsoon subprocess, returning the |
| 104 stdout file of the subprocess. """ |
| 105 if self._process is None or self._process.poll() is not None: |
| 106 raise Exception("Monsoon subprocess not running when going to end " |
| 107 + "execution.") |
| 108 self._process.terminate() |
| 109 self._process.wait() |
| 110 result_file = self._process.stdout |
| 111 self._process = None |
| 112 self._result_file = None |
| 113 return result_file |
| 114 |
| 115 def IsProcessRunning(self): |
| 116 """ Returns whether or not the Monsoon subprocess is running. |
| 117 """ |
| 118 return self._process is not None and self._process.poll() is None |
| 119 |
| 120 def TerminateMonsoonProcess(self): |
| 121 """ Terminates the Monsoon subprocess if it is running. |
| 122 """ |
| 123 if self._process is not None and self._process.poll() is None: |
| 124 self._process.kill() |
OLD | NEW |