| Index: common/monsoon/monsoon/monsoon_wrapper.py
|
| diff --git a/common/monsoon/monsoon/monsoon_wrapper.py b/common/monsoon/monsoon/monsoon_wrapper.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dbbc76aa429c38147dab5cda8125222170f99b63
|
| --- /dev/null
|
| +++ b/common/monsoon/monsoon/monsoon_wrapper.py
|
| @@ -0,0 +1,124 @@
|
| +# Copyright 2017 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +import atexit
|
| +import py_utils
|
| +import re
|
| +import subprocess
|
| +import tempfile
|
| +
|
| +USB_PASSTHROUGH_OFF = "off"
|
| +USB_PASSTHROUGH_ON = "on"
|
| +USB_PASSTHROUGH_AUTO = "auto"
|
| +
|
| +UsbModes = [USB_PASSTHROUGH_OFF, USB_PASSTHROUGH_ON, USB_PASSTHROUGH_AUTO]
|
| +
|
| +class MonsoonWrapper(object):
|
| + def __init__(self, script):
|
| + self.script = script
|
| + self._process = None
|
| + # Register atexit handler to kill any monsoon processes in the case of
|
| + # abnormal program termination.
|
| + atexit.register(self.TerminateMonsoonProcess)
|
| +
|
| + def ReadStatusProperties(self):
|
| + """ Reads status properties from Monsoon, returning a dictionary of
|
| + properties.
|
| + """
|
| + status_text = self.ExecuteBlocking(["--status"])
|
| + status = {}
|
| + for status_line in status_text:
|
| + # Sometimes transient USB serial errors are reported. Just ignore them
|
| + # and only use strings with : in them.
|
| + if ":" in status_line:
|
| + key, value = status_line.split(':')
|
| + status[key] = value
|
| + return status
|
| +
|
| + def EnableUSB(self):
|
| + """ Shorthand for SetUSBMode(USB_PASSTHROUGH_ON) """
|
| + self.SetUSBMode(USB_PASSTHROUGH_ON)
|
| +
|
| + def DisableUSB(self):
|
| + """ Shorthand for SetUSBMode(USB_PASSTHROUGH_OFF) """
|
| + self.SetUSBMode(USB_PASSTHROUGH_OFF)
|
| +
|
| + def SetUSBMode(self, mode):
|
| + """ Set the Monsoon USB mode.
|
| + """
|
| + if mode not in UsbModes: raise Exception("Invalid USB mode " + mode)
|
| + # Sometimes Monsoon fails to make USB passthrough mode changes
|
| + # permanent if there are transient USB serial errors.
|
| + retry_count = 5
|
| + while self.GetUSBMode() is not mode:
|
| + self.ExecuteBlocking(["--usbpassthrough", mode])
|
| + if retry_count is 0: Exception("Failed to set USB mode!")
|
| + retry_count -= 1
|
| +
|
| + def GetUSBMode(self):
|
| + """ Retrieves the current Monsoon USB mode, returning USB_PASSTHROUGH_OFF
|
| + if the status could not be retrieved.
|
| + """
|
| + properties = self.ReadStatusProperties()
|
| + if "usbPassthroughMode" in properties:
|
| + return UsbModes[int(properties["usbPassthroughMode"])]
|
| + return USB_PASSTHROUGH_OFF
|
| +
|
| + def ReadPower(self, time, frequency):
|
| + """ Reads power from the Monsoon device over a specified perdiod while
|
| + blocking, returning a file containing raw monsoon data. """
|
| + sample_count = time * frequency
|
| + args = ["--samples", str(sample_count), "--hz", str(frequency),
|
| + "--timestamp"]
|
| + return self.ExecuteBlocking(args)
|
| +
|
| + def ExecuteBlocking(self, args):
|
| + """ Executes a blocking operation on the Monsoon device, returning a file
|
| + containing stdout output. """
|
| + if self.IsProcessRunning():
|
| + raise Exception("Monsoon subprocess already running!")
|
| + result_file = tempfile.TemporaryFile()
|
| + subprocess.check_call([self.script] + args, stdout=result_file)
|
| + result_file.seek(0)
|
| + return result_file
|
| +
|
| + def BeginReadPower(self, frequency, out=subprocess.PIPE):
|
| + """ Begins a non-blocking read of power from the Monsoon device, returning
|
| + the stdout file of the subprocess.
|
| + """
|
| + args = ["--samples", "-1", "--hz", str(frequency), "--timestamp"]
|
| + return self.BeginExecution(args=args, out=out)
|
| +
|
| + def BeginExecution(self, args = [], out=subprocess.PIPE):
|
| + """ Begins a non-blocking operation on the Monsoon device, returning the
|
| + stdout file of the subprocess.
|
| + """
|
| + if self.IsProcessRunning():
|
| + raise Exception("Monsoon subprocess already running!")
|
| + self._process = subprocess.Popen([self.script] + args, stdout=out)
|
| + return self._process.stdout
|
| +
|
| + def EndExecution(self):
|
| + """ Ends non-blocking execution of the Monsoon subprocess, returning the
|
| + stdout file of the subprocess. """
|
| + if self._process is None or self._process.poll() is not None:
|
| + raise Exception("Monsoon subprocess not running when going to end "
|
| + + "execution.")
|
| + self._process.terminate()
|
| + self._process.wait()
|
| + result_file = self._process.stdout
|
| + self._process = None
|
| + self._result_file = None
|
| + return result_file
|
| +
|
| + def IsProcessRunning(self):
|
| + """ Returns whether or not the Monsoon subprocess is running.
|
| + """
|
| + return self._process is not None and self._process.poll() is None
|
| +
|
| + def TerminateMonsoonProcess(self):
|
| + """ Terminates the Monsoon subprocess if it is running.
|
| + """
|
| + if self._process is not None and self._process.poll() is None:
|
| + self._process.kill()
|
|
|