Remove Centroiding Visualizer Tool
Code cleanup - centroiding visualizer tool has not been used for a long time
BUG=b:206572894
TEST=Tested on chromeos devices & make sure webplot behavior is unaffected
Change-Id: Ib24b34879c3958cfeea8884b4ba729eb55412368
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/touch_firmware_test/+/3770318
Commit-Queue: Jingyuan Liang <jingyliang@google.com>
Reviewed-by: Kenneth Albanowski <kenalba@google.com>
Tested-by: Jingyuan Liang <jingyliang@google.com>
Reviewed-by: Sean O'Brien <seobrien@chromium.org>
Auto-Submit: Jingyuan Liang <jingyliang@google.com>
diff --git a/webplot/centroiding.html b/webplot/centroiding.html
deleted file mode 100644
index 73084b3..0000000
--- a/webplot/centroiding.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Copyright 2015 The Chromium OS Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<html>
-<head>
- <script src="centroiding.js"></script>
-</head>
-
-<body style="margin:0; padding:0; background-color:black"
- onbeforeunload="quit()"
- onload="createWS()"
- onkeyup="keyupHandler()"
- onresize="resizeCanvas()">
-
- <div id="websocketUrl" hidden>%(websocketUrl)s</div>
- <div id="dataScale" hidden>%(dataScale)s</div>
- <div id="dataOffset" hidden>%(dataOffset)s</div>
- <div id="dataWidth" hidden>%(dataWidth)s</div>
- <div id="dataHeight" hidden>%(dataHeight)s</div>
- <canvas id="canvasWebplot"></canvas>
-
-</body>
-</html>
diff --git a/webplot/centroiding.js b/webplot/centroiding.js
deleted file mode 100644
index e7e260f..0000000
--- a/webplot/centroiding.js
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-/**
- * Object of a reported finger contact model.
- * @constructor
- * @param {array of float} paramArray the array of all contact parameters.
- * [0]: contact amplitude
- * [1]: x-positon of contact
- * [2]: y-position of contact
- * [3]: x-sigma of contact gaussian model
- * [4]: y-sigma of contact gaussian model
- * [5]: theta of contact gaussian model
- * [6]: tracking ID of contact
- * [7]: thumb likelihood
- * [8]: palm indicator (0: finger, 1: palm)
- */
-function Contact(paramArray) {
- this.amp = paramArray[0];
- this.xPos = paramArray[1];
- this.yPos = paramArray[2];
- this.xSigma = paramArray[3];
- this.ySigma = paramArray[4];
- this.theta = paramArray[5];
- this.trackId = paramArray[6];
- this.thumbLikelihood = 0;
- this.isPalm = 0;
- if (paramArray.length > 7)
- this.thumbLikelihood = paramArray[7];
- if (paramArray.length > 8)
- this.isPalm = paramArray[8];
-}
-
-
-/**
- * Object of webplot.
- * @constructor
- * @param {Element} canvas the canvas to draw circles and clicks.
- * @param {string} dataScale the max scale of heatmap raw data.
- * @param {string} dataOffset the offset of heatmap raw data for presenting.
- * @param {string} dataWidth the size of width of heatmap raw data.
- * @param {string} dataHeight the size of height of heatmap raw data.
- */
-function Webplot(canvas, dataScale, dataOffset, dataWidth, dataHeight) {
- this.canvas = canvas;
- this.ctx = canvas.getContext('2d');
- this.scale = 1 / parseInt(dataScale);
- this.offset = parseInt(dataOffset);
- this.width = parseInt(dataWidth);
- this.height = parseInt(dataHeight);
- this.gridSize = null;
- this.pointRadius = 6.0;
- this.contactLineWidth = 3.5;
-}
-
-
-/**
- * Update the width and height of the canvas, the max radius of circles,
- * and the edge of click rectangles.
- */
-Webplot.prototype.updateCanvasDimension = function() {
- var newWidth = document.body.clientWidth;
- var newHeight = document.body.clientHeight;
-
- if (this.canvas.width != newWidth || this.canvas.height != newHeight) {
- var dataRatio = this.height / this.width;
- var canvasRatio = (newHeight / newWidth);
-
- // The actual dimension of the viewport.
- this.canvas.width = newWidth;
- this.canvas.height = newHeight;
-
- // Calculate the inner area of the viewport on which to draw finger traces.
- // This inner area has the same height/width ratio as the touch device.
- if (dataRatio >= canvasRatio) {
- this.canvas.innerWidth = Math.round(newHeight / dataRatio);
- this.canvas.innerHeight = newHeight;
- this.canvas.innerOffsetLeft = Math.round(
- (newWidth - this.canvas.innerWidth) / 2);
- this.canvas.innerOffsetTop = 0;
- this.gridSize = newHeight / this.height;
- } else {
- this.canvas.innerWidth = newWidth;
- this.canvas.innerHeight = Math.round(newWidth * dataRatio);
- this.canvas.innerOffsetLeft = 0;
- this.canvas.innerOffsetTop = Math.round(
- (newHeight - this.canvas.innerHeight) / 2);
- this.gridSize = newWidth / this.width;
- }
- }
- this.drawFrame(this.canvas.innerOffsetLeft, this.canvas.innerOffsetTop,
- this.canvas.innerWidth, this.canvas.innerHeight, 'gray');
-}
-
-
-/**
- * Draw a rectangle of heatmap frame border.
- * @param {int} x the x coordinate of upper left corner of the rectangle.
- * @param {int} y the y coordinate of upper left corner of the rectangle.
- * @param {int} width the width of the rectangle.
- * @param {int} height the height of the rectangle.
- * @param {string} colorName
- */
-Webplot.prototype.drawFrame = function(x, y, width, height, colorName) {
- this.ctx.beginPath();
- this.ctx.lineWidth = "4";
- this.ctx.strokeStyle = colorName;
- this.ctx.rect(x, y, width, height);
- this.ctx.stroke();
-}
-
-
-/**
- * Draw a circle of presenting contact gaussian model.
- * @param {Element} contact the contact object of gaussian finger model.
- */
-Webplot.prototype.drawContactArea = function(contact) {
- var ctxLeft = this.canvas.innerOffsetLeft;
- var ctxTop = this.canvas.innerOffsetTop;
- var grid = this.gridSize;
- var offset = grid / 2;
- var x = contact.xPos;
- var y = (this.height - 1) - contact.yPos; // Reflect over x-axis.
- var cx = x * grid + offset + ctxLeft;
- var cy = y * grid + offset + ctxTop;
-
- this.ctx.beginPath();
- this.ctx.save(); // save state
- this.ctx.translate(cx, cy);
- this.ctx.rotate(contact.theta);
- this.ctx.scale(contact.xSigma * grid, contact.ySigma * grid);
- this.ctx.arc(0, 0, 1, 0, 2 * Math.PI, false);
- this.ctx.restore(); // restore to original state
- this.ctx.lineWidth = this.contactLineWidth;
- if (contact.isPalm)
- this.ctx.strokeStyle = 'gold';
- else
- this.ctx.strokeStyle = 'deeppink';
- this.ctx.stroke();
-}
-
-
-/**
- * Draw a point of presenting contact center.
- * @param {Element} contact the contact object of gaussian finger model.
- */
-Webplot.prototype.drawContactCenter = function(contact) {
- var ctxLeft = this.canvas.innerOffsetLeft;
- var ctxTop = this.canvas.innerOffsetTop;
- var grid = this.gridSize;
- var colors = ['blue', 'yellow', 'purple', 'red', 'orange', 'green',
- 'gold', 'cyan', 'brown', 'limegreen'];
- var offset = grid / 2;
- var x = contact.xPos;
- var y = (this.height - 1) - contact.yPos; // Reflect over x-axis.
- var cx = x * grid + offset + ctxLeft;
- var cy = y * grid + offset + ctxTop;
-
- this.ctx.beginPath();
- this.ctx.arc(cx, cy, this.pointRadius, 0, 2 * Math.PI, false);
- this.ctx.fillStyle = colors[contact.trackId % colors.length];
- this.ctx.fill();
-
- if (contact.isPalm) {
- var text = 'PALM';
- var text_color = 'blue';
- } else {
- var text = contact.thumbLikelihood.toFixed(4);
- var green = parseInt(255 * (1 - contact.thumbLikelihood)).toString(16);
- green = (green.length < 2) ? '0' + green : green;
- var red = parseInt(255 * contact.thumbLikelihood).toString(16);
- red = (red.length < 2) ? '0' + red : red;
- var text_color = '#' + red + green + '00';
- }
- this.fillText(text, cx + 4, cy + 20, '18px Arial', text_color);
-}
-
-
-/**
- * Fill text.
- * @param {string} text the text to display
- * @param {int} x the x coordinate of upper left corner of the text.
- * @param {int} y the y coordinate of upper left corner of the text.
- * @param {string} font the size and the font.
- * @param {string} color the color of the text.
- */
-Webplot.prototype.fillText = function(text, x, y, font, color) {
- this.ctx.font = font;
- this.ctx.fillStyle = color;
- this.ctx.fillText(text, x, y);
-}
-
-
-/**
- * Capture the canvas image.
- * @return {string} the image represented in base64 text
- */
-Webplot.prototype.captureCanvasImage = function() {
- var imageData = this.canvas.toDataURL('image/png');
- // Strip off the header.
- return imageData.replace(/^data:image\/png;base64,/, '');
-}
-
-
-/**
- * Process an incoming snapshot.
- * @param {object} snapshot
- *
- * A snapshot received from python server looks like:
- * Snapshot(
- * data=[125,123,90,0,0, ... 230,231],
- * contact=[[337.311, 1.55013, 6.43009, 0.747915, 0.97476, -0.026596, 1,
- * 0.13422, 0]],
- * fps=[128.234, 49.992]
- * )
- *
- * data: a total length of width * height integers array of heatmap raw data.
- * contact: an array of contact arrays, each contact array has the following
- * elements [amplitude, x-position, y-position, x-sigma, y-sigma,
- * theta, track-ID, thumb likelihood, palm indicator], and they are
- * all floating numbers.
- * fps: an 2-element array of float for receiver FPS and plot FPS.
- */
-Webplot.prototype.processSnapshot = function(snapshot) {
- var ctxLeft = this.canvas.innerOffsetLeft;
- var ctxTop = this.canvas.innerOffsetTop;
- var grid = this.gridSize;
- var gridFullPixel = Math.ceil(grid);
- var data = snapshot.data;
-
- // Clean up canvas.
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
-
- // Draw the frame border.
- this.drawFrame(this.canvas.innerOffsetLeft, this.canvas.innerOffsetTop,
- this.canvas.innerWidth, this.canvas.innerHeight, 'gray');
-
- // Draw heatmap raw data (data grid).
- for (var i = 0; i < this.width; i++) {
- for (var j = 0; j < this.height; j++) {
- var value = this.scale * (data[j + i * this.height] + this.offset);
- var x = Math.floor(ctxLeft + i * grid);
- // Reflect over x-axis.
- var y = Math.floor(ctxTop + (this.height - 1 - j) * grid);
- var color = parseInt(255 * value).toString(16);
- color = (color.length < 2) ? "0" + color : color;
- this.ctx.fillStyle = "#" + color + color + color;
- this.ctx.fillRect(x, y, gridFullPixel, gridFullPixel);
- }
- }
-
- // Draw finger contact models.
- for (var i = 0; i < snapshot.contact.length; i++) {
- var contact = new Contact(snapshot.contact[i]);
- this.drawContactArea(contact);
- this.drawContactCenter(contact);
- }
-
- // Show FPS rates.
- this.fillText("Algorithm FPS: " + snapshot.fps[0],
- 20 + ctxLeft, 25 + ctxTop, '14px Verdana', 'bisque');
- this.fillText("Plot FPS: " + snapshot.fps[1],
- 240 + ctxLeft, 25 + ctxTop, '14px Verdana', 'cyan');
-}
-
-
-Webplot.quitFlag = false;
-
-
-/**
- * An handler for onresize event to update the canvas dimensions.
- */
-function resizeCanvas() {
- webplot.updateCanvasDimension();
-}
-
-
-/**
- * Send a 'quit' message to the server and display the event file name
- * on the canvas.
- * @param {boolean} closed_by_server True if this is requested by the server.
- */
-function quit(closed_by_server) {
- var canvas = document.getElementById('canvasWebplot');
- var webplot = window.webplot;
- var startX = 100;
- var startY = 100;
- var font = '30px Verdana';
-
- // Capture the image before clearing the canvas and send it to the server,
- // and notify the server that this client quits.
- if (!Webplot.quitFlag) {
- Webplot.quitFlag = true;
- window.ws.send('quit');
-
- clear(false);
- if (closed_by_server) {
- webplot.fillText('The python server has quit.',
- startX, startY, font, 'red');
- }
- webplot.fillText('Centroiding data are saved inside "/tmp/"',
- startX, startY + 100, font, 'red');
- }
-}
-
-function save() {
- window.ws.send('save:' + webplot.captureCanvasImage());
-}
-
-/**
- * A handler for keyup events to handle user hot keys.
- */
-function keyupHandler() {
- var webplot = window.webplot;
- var key = String.fromCharCode(event.which).toLowerCase();
- var ESC = String.fromCharCode(27);
-
- switch(String.fromCharCode(event.which).toLowerCase()) {
- // ESC: clearing the canvas
- case ESC:
- clear(true);
- break;
-
- // 'b': toggle the background color between black and white
- // default: black
- case 'b':
- document.bgColor = (document.bgColor == 'Black' ? 'White' : 'Black');
- break;
-
- // 'f': toggle full screen
- // default: non-full screen
- // Note: entering or leaving full screen will trigger onresize events.
- case 'f':
- if (document.documentElement.webkitRequestFullscreen) {
- if (document.webkitFullscreenElement)
- document.webkitCancelFullScreen();
- else
- document.documentElement.webkitRequestFullscreen(
- Element.ALLOW_KEYBOARD_INPUT);
- }
- webplot.updateCanvasDimension();
- break;
-
- // 'q': Quit the server (and save the plot and logs first)
- case 'q':
- save();
- quit(false);
- break;
-
- // 's': Tell the server to save the touch events and a png of the plot
- case 's':
- save();
- break;
- }
-}
-
-
-function clear(should_redraw_border) {
- var canvas = document.getElementById('canvasWebplot');
- canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
- if (should_redraw_border) {
- window.webplot.updateCanvasDimension();
- }
-}
-
-
-/**
- * Create a web socket and a new webplot object.
- */
-function createWS() {
- var websocket = document.getElementById('websocketUrl').innerText;
- var dataScale = document.getElementById('dataScale').innerText;
- var dataOffset = document.getElementById('dataOffset').innerText;
- var dataWidth = document.getElementById('dataWidth').innerText;
- var dataHeight = document.getElementById('dataHeight').innerText;
- if (window.WebSocket) {
- ws = new WebSocket(websocket);
- ws.addEventListener("message", function(event) {
- if (event.data == 'quit') {
- save();
- quit(true);
- } else if (event.data == 'clear') {
- clear(true);
- } else if (event.data == 'save') {
- save();
- } else {
- var snapshot = JSON.parse(event.data);
- webplot.processSnapshot(snapshot);
- }
- });
- } else {
- alert('WebSocket is not supported on this browser!')
- }
-
- webplot = new Webplot(document.getElementById('canvasWebplot'),
- dataScale, dataOffset, dataWidth, dataHeight);
- webplot.updateCanvasDimension();
-}
diff --git a/webplot/centroiding/__init__.py b/webplot/centroiding/__init__.py
deleted file mode 100644
index 4bc803c..0000000
--- a/webplot/centroiding/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from centroiding_device import CentroidingDevice
-from centroiding_parser import CentroidingDataParser
-from centroiding_receiver import CentroidingDataReceiver
diff --git a/webplot/centroiding/centroiding_device.py b/webplot/centroiding/centroiding_device.py
deleted file mode 100644
index 2fb8f4c..0000000
--- a/webplot/centroiding/centroiding_device.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Device object for centroiding visualizer tool."""
-
-import os
-import yaml
-
-
-class CentroidingDevice(object):
- def __init__(self, config):
- with open(os.path.join(os.path.dirname(__file__), config), 'r') as f:
- self.config = yaml.load(f)
-
- @property
- def device(self):
- return self.config.get('device')
-
- @property
- def data_scale(self):
- return self.config.get('data_scale')
-
- @property
- def data_offset(self):
- return self.config.get('data_offset')
-
- @property
- def width(self):
- return self.config.get('width')
-
- @property
- def height(self):
- return self.config.get('height')
diff --git a/webplot/centroiding/centroiding_parser.py b/webplot/centroiding/centroiding_parser.py
deleted file mode 100644
index 7f54548..0000000
--- a/webplot/centroiding/centroiding_parser.py
+++ /dev/null
@@ -1,192 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Parser for recorded centroiding data stream. It will generate data files in
-analytical format for our centroiding algorithm library.
-
-It will generate 3 files:
-
- Trace file: will be generated in path <input_file>/<input_file>
- To view it by centroiding source tree's tool, put the trace file into
- traces/ in source tree.
-
- Finger contact file: will be generated in path <input_file>/<input_file>.json
- To view it by centroiding source tree's tool, put the contact file into
- outputs/centroiding/ in source tree.
-
- Track ID file: will be generated in path <input_file>/<input_file>.id.json
-"""
-
-import argparse
-import json
-import os
-
-from centroiding_device import CentroidingDevice
-
-_JSON_HEADER = '{\n "events": [\n'
-_JSON_TAIL = ' ]\n}'
-
-
-class CentroidingDataParserError(Exception):
- pass
-
-class CentroidingDataParser(object):
- """Parser for recorded centroiding raw data stream."""
- def __init__(self, input_file, device):
- self.input_file = input_file
- self.device = device
-
- self.head_trim_thres = self.device.data_scale >> 2
- self.is_head = True
-
- # Use the root name of input_file as generated output folder name.
- self.output_dir = os.path.splitext(input_file)[0]
- if not os.path.exists(self.output_dir):
- os.makedirs(self.output_dir)
- filename = os.path.basename(self.output_dir)
- self.output_trace = os.path.join(self.output_dir, filename)
- self.output_finger = os.path.join(self.output_dir, '%s.json' % filename)
- self.output_id = os.path.join(self.output_dir, '%s.id.json' % filename)
-
- self.file_trace = open(self.output_trace, 'w')
- self.file_finger = open(self.output_finger, 'w')
- self.file_id = open(self.output_id, 'w')
-
- def _PrintHeader(self):
- """Prints header lines of json files."""
- self.file_finger.write(_JSON_HEADER)
- self.file_id.write(_JSON_HEADER)
-
- def _PrintTail(self):
- """Prints tail lines of json files."""
- self.file_finger.write(_JSON_TAIL)
- self.file_id.write(_JSON_TAIL)
-
- def _AssertBadData(self, json_data):
- """Asserts exception if received data is not as expected."""
- if len(json_data['data']) != self.device.width * self.device.height:
- raise CentroidingDataParserError('Mismatched data length')
- if any(len(contact) != 7 for contact in json_data['contact']):
- raise CentroidingDataParserError('Mismatched contact length')
-
- def _IsEmptyHead(self, json_data, line_number):
- """Checks if current frame is empty for head frame trimming.
-
- Head frame trimming is used to trim empty frames from the beginning since
- user may not drop fingers immediately after opening data receiver.
-
- Frames will be regard empty-headed if there is no finger contact reported
- and all trace data is below device.data_scale / 4. Once a frame is not
- empty, all frames afterward will be parsed.
-
- Args:
- json_data: Frame data in json format.
- line_number: Current line number.
-
- Returns:
- True if this frame is empty-headed; otherwise False.
- """
- if not self.is_head:
- return False
- if len(json_data['contact']) > 0:
- self.is_head = False
- print '[Parse] Trimmed empty content in the beginning.'
- print '[Parse] Parse from line %d...' % line_number
- return False
- if any(d >= self.head_trim_thres for d in json_data['data']):
- self.is_head = False
- print '[Parse] Trimmed empty content in the beginning.'
- print '[Parse] Parse from line %d...' % line_number
- return False
- return True
-
- def _PrintTrace(self, data):
- """Prints trace data into trace file.
-
- For example, device width = 2, height = 3, data = [1, 2, 3, 4, 5, 6]
- will be printed as 1 2 3
- 4 5 6
- """
- for j in xrange(self.device.width):
- data_line = data[j * self.device.height: (j + 1) * self.device.height]
- self.file_trace.write(' '.join(str(d) for d in data_line))
- self.file_trace.write('\n')
- self.file_trace.write('\n')
-
- def _PrintFingerAndId(self, contact):
- """Prints contact fingers and tracking IDs into files."""
- track_id = [c.pop() for c in contact]
- self.file_finger.write(' %s,\n' % str(contact))
- self.file_id.write(' %s,\n' % str(track_id))
-
- def _PrintDummyEndFrame(self):
- """Prints one last dummy frame into files.
-
- Since json does not accept trailing comma in an array and it is difficult
- to know the last frame data in file readline loop, printing a dummy frame
- in the end can solve this issue easily.
- """
- for j in xrange(self.device.width):
- self.file_trace.write(' '.join(['0' for i in xrange(self.device.height)]))
- if j < self.device.width - 1:
- self.file_trace.write('\n')
- self.file_finger.write(' []\n')
- self.file_id.write(' []\n')
-
- def _CloseFiles(self):
- """Closes all file objects."""
- self.file_trace.close()
- self.file_finger.close()
- self.file_id.close()
-
- def Parse(self):
- """Starts parsing trace data and finger data into output files."""
- self._PrintHeader()
- with open(self.input_file) as f:
- line_number = 0
- for line in f:
- line_number += 1
- strip_line = line.strip()
- if len(strip_line) == 0:
- continue
- try:
- json_data = json.loads(strip_line)
- self._AssertBadData(json_data)
- if not self._IsEmptyHead(json_data, line_number):
- self._PrintTrace(json_data['data'])
- self._PrintFingerAndId(json_data['contact'])
- except Exception as e:
- print '[Parse] Invalid data line %d: %s' % (line_number, strip_line)
- print e
- self._PrintDummyEndFrame()
- self._PrintTail()
- self._CloseFiles()
- print '[Parse] created trace data:', self.output_trace
- print '[Parse] created finger json:', self.output_finger
- print '[Parse] created track id json:', self.output_id
-
-
-def _ParseArguments():
- """Parses the command line options."""
- parser = argparse.ArgumentParser(description='Centroiding Main')
-
- parser.add_argument('-i', '--input_file',
- help='the input file path of raw data')
- parser.add_argument('-c', '--config', help='the config file of device')
-
- args = parser.parse_args()
- return args
-
-
-def Main():
- args = _ParseArguments()
-
- print 'parsing raw data %s to analytical format...' % args.input_file
- device = CentroidingDevice(config=args.config)
- data_parser = CentroidingDataParser(args.input_file, device)
- data_parser.Parse()
-
-
-if __name__ == '__main__':
- Main()
diff --git a/webplot/centroiding/centroiding_receiver.py b/webplot/centroiding/centroiding_receiver.py
deleted file mode 100644
index b5311ab..0000000
--- a/webplot/centroiding/centroiding_receiver.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Centroiding receiver handles centroiding data stream from dut and forwards
-to webplot module for data visualizing.
-"""
-
-import ctypes
-import ctypes.util
-import json
-import os
-import socket
-import time
-from builtins import range
-
-# Pre-load librt.so for MonotonicTime()
-librt_name = ctypes.util.find_library('rt')
-librt = ctypes.cdll.LoadLibrary(librt_name)
-
-
-class TimeSpec(ctypes.Structure):
- """A representation of struct timespec in C."""
- _fields_ = [
- ('tv_sec', ctypes.c_long),
- ('tv_nsec', ctypes.c_long),
- ]
-
-
-clock_gettime = librt.clock_gettime
-clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(TimeSpec)]
-
-
-def MonotonicTime():
- """Gets the raw monotonic time.
-
- This function opens librt.so with ctypes and call:
- int clock_gettime(clockid_t clk_id, struct timespec *tp);
- to get raw monotonic time.
-
- Returns:
- The system monotonic time in seconds.
- """
- CLOCK_MONOTONIC_RAW = 4
- t = TimeSpec()
- if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(t)) != 0:
- errno = ctypes.get_errno()
- raise OSError(errno, os.strerror(errno))
- return t.tv_sec + 1e-9 * t.tv_nsec
-
-
-class FPSCounter(object):
- """The frame counter and frame rate calculator."""
- def __init__(self, frame_interval=200):
- """The init method of FPS counter.
-
- Args:
- frame_interval: FPS will be calculated and updated every # frames.
- """
- self.frame_interval = frame_interval
- self.fps = 0
- self.frame_counter = 0
- self.timestamp = MonotonicTime()
-
- def GetFPS(self):
- """Gets current FPS in string."""
- return '%.4f' % self.fps
-
- def Count(self):
- """Counts a new frame."""
- self.frame_counter += 1
- if self.frame_counter >= self.frame_interval:
- self._UpdateFPS()
- self.frame_counter = 0
-
- def _UpdateFPS(self):
- """Updates current FPS."""
- new_timestamp = MonotonicTime()
- time_interval = new_timestamp - self.timestamp
- instant_fps = float(self.frame_counter) / time_interval
- if self.fps == 0:
- self.fps = instant_fps
- else:
- self.fps = self.fps * 0.9 + instant_fps * 0.1
- self.timestamp = new_timestamp
-
-
-class CentroidingDataReceiver(object):
- """The socket receiver of centroiding data."""
- _CHUNK_BYTE_SIZE = 1024
- _NULL_CHAR = '\x00'
-
- def __init__(self, server_address, server_port, webplot,
- save_data=None, plot_fps=0):
- """The init method of centroiding TCP client.
-
- Args:
- server_address: Address of TCP socket server from testing device.
- server_port: TCP socket server port.
- webplot: Webplot object for plotting snapshots.
- save_data: File name for saving received centroiding data. Set to None to
- disable saving data.
- plot_fps: Target FPS for plotting snapshot. Flow control is managed by
- server and overflow raw data sockets will be skipped. Set 0 to
- disable flow control.
- """
- self.webplot = webplot
- self.is_saving = save_data is not None
- self.save_path = save_data if self.is_saving else '/dev/null'
-
- self.plot_time_interval = 1.0 / plot_fps if plot_fps else 0
- self.plot_time_target = 0
- self.fps_receiver = FPSCounter()
- self.fps_plot = FPSCounter()
-
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.connect((server_address, server_port))
-
- def StartReceive(self):
- """Starts routine of receiving data and plotting."""
- try:
- with open(self.save_path, 'w') as f:
- chunk_buffer = ''
- while True:
- try:
- chunk = self.sock.recv(self._CHUNK_BYTE_SIZE)
- chunk_buffer += chunk
- if self._NULL_CHAR in chunk_buffer:
- chunk_buffer = self._ExtractSnapshots(chunk_buffer, f)
- except KeyboardInterrupt:
- print('Keyboard interrupt accepted!')
- print('Centroiding receiver is terminated...')
- print('Webplot server is terminated...')
- self.sock.close()
- self.webplot.Quit()
- break
- except IOError as e:
- print('Oops!! something wrong while saving data to %s: %s' % (
- self.save_path, str(e)))
- self.sock.close()
- self.webplot.Quit()
-
- def _ExtractSnapshots(self, chunk_buffer, save_file):
- """Extracts snapshot data from received chunk buffer.
-
- Args:
- chunk_buffer: Current received chunk string buffer.
- save_file: File object for saving received data.
-
- Returns:
- The residue on chunk buffer after snapshot extraction.
- """
- snapshots = chunk_buffer.split(self._NULL_CHAR)
- for i in range(len(snapshots)):
- if i == len(snapshots) - 1:
- return snapshots[i]
- if len(snapshots[i]) == 0:
- continue
- if self.is_saving:
- save_file.write(snapshots[i] + '\n')
- save_file.flush()
- self.fps_receiver.Count()
- if self._PlotWithFlowControl():
- self.fps_plot.Count()
- self._PlotSnapshot(snapshots[i])
-
- def _PlotWithFlowControl(self):
- """Determines whether to plot this snapshot with flow control.
-
- Returns:
- True to plot this snapshot; False otherwise.
- """
- if not self.plot_time_interval:
- return True
- current_time = MonotonicTime()
- if current_time > self.plot_time_target:
- self.plot_time_target = current_time + self.plot_time_interval
- return True
- return False
-
- def _PlotSnapshot(self, snapshot):
- """Plots snapshot data to webplot server.
-
- Args:
- snapshot: Snapshot data for centroiding in string.
- """
- try:
- snapshot_json = json.loads(snapshot.strip())
- snapshot_json.update(
- {"fps": [self.fps_receiver.GetFPS(), self.fps_plot.GetFPS()]})
- self.webplot.AddSnapshot(snapshot_json)
- except Exception as e:
- print('Exception while plotting snapshot = %r:' % snapshot, e)
diff --git a/webplot/centroiding/nexus9.conf b/webplot/centroiding/nexus9.conf
deleted file mode 100644
index 42853a9..0000000
--- a/webplot/centroiding/nexus9.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-### ========================================================= ###
-### Config file for centroiding visualizer device parameters ###
-### ========================================================= ###
-### device: device name ###
-### data_scale: upper-bound scale of raw heatmap data ###
-### data_offset: data offset while presenting raw heatmap ###
-### width: the size of width of raw heatmap ###
-### height: the size of height of raw heatmap ###
-### ========================================================= ###
-
-device: nexus9
-data_scale: 4000
-data_offset: 500
-width: 28
-height: 38
diff --git a/webplot/centroiding/tango.conf b/webplot/centroiding/tango.conf
deleted file mode 100644
index 307b682..0000000
--- a/webplot/centroiding/tango.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-### ========================================================= ###
-### Config file for centroiding visualizer device parameters ###
-### ========================================================= ###
-### device: device name ###
-### data_scale: upper-bound scale of raw heatmap data ###
-### data_offset: data offset while presenting raw heatmap ###
-### width: the size of width of raw heatmap ###
-### height: the size of height of raw heatmap ###
-### ========================================================= ###
-
-device: tango
-data_scale: 300
-data_offset: 35
-width: 27
-height: 44
diff --git a/webplot/centroiding_main.py b/webplot/centroiding_main.py
deleted file mode 100644
index b01f4a4..0000000
--- a/webplot/centroiding_main.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Main function for showing centroiding visualizer tool.
-
-It will automatically find the dut connection (via USB cable), build up tunnel,
-and open a webplot server daemon and a socket client. After then You can go and
-check the visualizing demo on the browser.
-
-For different kind of dut, you may setup a config file for parameters of
-visualizer tool.
-
-Example:
- python centroiding_main.py -s 127.0.0.1 -p 8080 -c device.conf
-
-Server will load centroiding/device.conf as config, and build tunnel with port
-12345. The webplot will be in http://127.0.0.1:8080 on the browser.
-
-Add argument -l to record centroiding data into file.
-
-Note: this program needs to run adb (Android Debug Bridge) inside the chroot,
-please run this command in advance if you do not have adb in your chroot yet.
- sudo emerge android-tools
-
-"""
-import argparse
-from datetime import datetime
-import os
-import subprocess
-import time
-
-from centroiding import (CentroidingDataParser,
- CentroidingDataReceiver,
- CentroidingDevice)
-from webplot import Webplot
-
-ADB_GETSTATE_RETRIES = 50
-ADB_FORWARD_RETRIES = 10
-SEPARATION_LINEWIDTH = 70
-
-
-def EstablishADBConnectionTunnel(port):
- """Builds up tunnel to dut by ADB via connection cable.
-
- 1. Poll device presence by 'adb get-state'. This command will return 'device'
- once it detects a dut connected.
-
- 2. Make socket connection tunnel by 'adb forward tcp:<port> tcp:<port>'.
-
- 3. Poll until tunnel established by checking 'adb forward --list' result.
-
- Args:
- port: port number of tunneling between device and server.
-
- Returns:
- Return False if errors happen; otherwise return True.
- """
- print('\n' + '-' * SEPARATION_LINEWIDTH)
-
- # To prevent adb no permissions issue, it needs sudo to start adb server.
- os.system('sudo adb kill-server')
- os.system('sudo adb start-server')
-
- def _GetDeviceOnline():
- return subprocess.check_output(['adb', 'get-state']).strip() == 'device'
-
- if not _GetDeviceOnline():
- print('Currently no device attached!\nPlease attach your testing device...')
- retry_times = 0
- while True:
- time.sleep(0.2)
- if _GetDeviceOnline():
- break
- retry_times += 1
- if retry_times > ADB_GETSTATE_RETRIES:
- print('Timeout polling for testing device (%.1f seconds)...' % (
- 0.2 * ADB_GETSTATE_RETRIES))
- return False
-
- print('\nDevice is attached.\n')
- print('Try to build tunnel port %d between device and server...' % port)
-
- retry_times = 0
- while True:
- subprocess.check_call(
- ['adb', 'forward', 'tcp:%d' % port, 'tcp:%d' % port])
- time.sleep(0.2)
- get_port = subprocess.check_output(
- ['adb', 'forward', '--list']).strip()
- if 'tcp:%d' % port in get_port:
- break
- retry_times += 1
- if retry_times > ADB_FORWARD_RETRIES:
- return False
-
- print('\nTunnel is established successfully.\n')
- return True
-
-
-def _ParseArguments():
- """Parses the command line options."""
- parser = argparse.ArgumentParser(description='Centroiding Main')
-
- parser.add_argument('-s', '--server_addr', default='127.0.0.1',
- help='the address the webplot http server listens to')
- parser.add_argument('-p', '--server_port', default=8080, type=int,
- help='the port the web server listens to (default: 8080)')
- parser.add_argument('-f', '--dut_forward_port', default=12345, type=int,
- help='the forwarding port for centroiding socket server '
- '(default: 12345)')
- parser.add_argument('-c', '--config', default='tango.conf', type=str,
- help='config file name of device for centroiding '
- 'visualizing tool parameters.')
- parser.add_argument('-l', '--log_data', action='store_true',
- help='log centroiding data and save to file in /tmp/.')
- parser.add_argument('--fps', default=0, type=int,
- help='the target frame rate of visualizer plotting, set '
- '0 for keeping same as centroiding processing frame '
- 'rate.')
-
- args = parser.parse_args()
- return args
-
-
-def Main():
- args = _ParseArguments()
-
- print('\n' + '-' * SEPARATION_LINEWIDTH + '\n')
- print('**** Centroiding Data Visualizing Tool ****')
- print('webplot server address:', args.server_addr)
- print('webplot server port:', args.server_port)
-
- print('\n' + '-' * SEPARATION_LINEWIDTH + '\n')
- print('dut socket forwarding port:', args.dut_forward_port)
- print(' (this port will be tunneled between dut and server)\n')
- print('dut config file:', args.config)
-
- device = CentroidingDevice(config=args.config)
-
- print(' - device name:', device.device)
- print(' - data_scale:', device.data_scale)
- print(' - data_offset:', device.data_offset)
- print(' - width:', device.width)
- print(' - height:', device.height)
-
- if not EstablishADBConnectionTunnel(args.dut_forward_port):
- print('Connection to device failed. Task is aborted...')
- return
-
- print('\n' + '-' * SEPARATION_LINEWIDTH + '\n')
- print('webplot server has started...')
- print('port %d is listening on sockets from dut...' % args.dut_forward_port)
- print(('check http://%s:%d on browser of your device to view the plot...\n' %
- (args.server_addr, args.server_port)))
-
- if args.log_data:
- time_now = datetime.now().strftime('%Y%m%d%H%M%S')
- save_data = os.path.join('/tmp', '%s_%s.dat' % (device.device, time_now))
- else:
- save_data = None
-
- # Instantiate a webplot server daemon and start it.
- plot_server = Webplot(args.server_addr, args.server_port, device,
- logging=True, is_behind_iptables_firewall=False,
- is_centroiding=True)
- plot_server.start()
-
- receiver = CentroidingDataReceiver(
- '127.0.0.1', args.dut_forward_port, plot_server,
- save_data=save_data, plot_fps=args.fps)
- receiver.StartReceive()
-
- if args.log_data:
- print('\ncentroiding raw data are saved into file %s\n' % save_data)
- print('parsing saved raw data to analytical format...')
- data_parser = CentroidingDataParser(save_data, device)
- data_parser.Parse()
- print('parse finished...')
-
-
-if __name__ == '__main__':
- Main()
-
diff --git a/webplot/webplot.py b/webplot/webplot.py
index 3fc8de2..57eeeac 100755
--- a/webplot/webplot.py
+++ b/webplot/webplot.py
@@ -297,45 +297,6 @@
state.IncCount()
-class CentroidingRoot(object):
- """A class to handle requests about docroot."""
-
- def __init__(self, ip, port, data_scale, data_offset,
- data_width, data_height):
- self.ip = ip
- self.port = port
- self.data_scale = data_scale
- self.data_offset = data_offset
- self.data_width = data_width
- self.data_height = data_height
- self.scheme = 'ws'
- cherrypy.log('Root address: (%s, %s)' % (ip, str(port)))
- cherrypy.log('scheme: %s' % self.scheme)
-
- @cherrypy.expose
- def index(self):
- """This is the default index.html page."""
- websocket_dict = {
- 'websocketUrl': '%s://%s:%s/ws' % (self.scheme, self.ip, self.port),
- 'dataScale': str(self.data_scale),
- 'dataOffset': str(self.data_offset),
- 'dataWidth': str(self.data_width),
- 'dataHeight': str(self.data_height),
- }
- print(websocket_dict)
- root_page = os.path.join(os.path.abspath(os.path.dirname(__file__)),
- 'centroiding.html')
- with open(root_page) as f:
- return f.read() % websocket_dict
-
- @cherrypy.expose
- def ws(self):
- """This handles the request to create a new web socket per client."""
- cherrypy.log('A new client requesting for WS')
- cherrypy.log('WS handler created: %s' % repr(cherrypy.request.ws_handler))
- state.IncCount()
-
-
class Webplot(threading.Thread):
"""The server handling the Plotting of finger traces.
@@ -375,13 +336,11 @@
"""
def __init__(self, server_addr, server_port, device, saved_file=SAVED_FILE,
- logging=False, is_behind_iptables_firewall=False,
- is_centroiding=False):
+ logging=False, is_behind_iptables_firewall=False):
self._server_addr = server_addr
self._server_port = server_port
self._device = device
self._saved_file = saved_file
- self._is_centroiding = is_centroiding
super(Webplot, self).__init__(name='webplot thread')
self.daemon = True
@@ -414,37 +373,25 @@
# If the cherrypy server exits for whatever reason, close the device
# for required cleanup. Otherwise, there might exist local/remote
# zombie processes.
- if not self._is_centroiding:
- cherrypy.engine.subscribe('exit', self._device.__del__)
+ cherrypy.engine.subscribe('exit', self._device.__del__)
cherrypy.engine.signal_handler.handlers['SIGINT'] = InterruptHandler
cherrypy.engine.signal_handler.handlers['SIGTERM'] = InterruptHandler
def run(self):
"""Start the cherrypy engine."""
- if not self._is_centroiding:
- x_min, x_max = self._device.RangeX()
- y_min, y_max = self._device.RangeY()
- p_min, p_max = self._device.RangeP()
- tilt_x_min, tilt_x_max = self._device.RangeTiltX()
- tilt_y_min, tilt_y_max = self._device.RangeTiltY()
- major_min, major_max = self._device.RangeMajor()
- minor_min, minor_max = self._device.RangeMinor()
+ x_min, x_max = self._device.RangeX()
+ y_min, y_max = self._device.RangeY()
+ p_min, p_max = self._device.RangeP()
+ tilt_x_min, tilt_x_max = self._device.RangeTiltX()
+ tilt_y_min, tilt_y_max = self._device.RangeTiltY()
+ major_min, major_max = self._device.RangeMajor()
+ minor_min, minor_max = self._device.RangeMinor()
- root = TouchRoot(self._server_addr, self._server_port,
- x_min, x_max, y_min, y_max, p_min, p_max, tilt_x_min,
- tilt_x_max, tilt_y_min, tilt_y_max,
- major_min, major_max, minor_min, minor_max,)
- else:
- data_scale = self._device.data_scale
- data_offset = self._device.data_offset
- data_width = self._device.width
- data_height = self._device.height
- tilt_x_min, tilt_x_max = self._device.RangeTiltX()
- tilt_y_min, tilt_y_max = self._device.RangeTiltY()
- root = CentroidingRoot(self._server_addr, self._server_port,
- data_scale, data_offset, data_width, data_height,
- tilt_x_min, tilt_x_max, tilt_y_min, tilt_y_max)
+ root = TouchRoot(self._server_addr, self._server_port,
+ x_min, x_max, y_min, y_max, p_min, p_max, tilt_x_min,
+ tilt_x_max, tilt_y_min, tilt_y_max,
+ major_min, major_max, minor_min, minor_max,)
cherrypy.quickstart(
root,
@@ -513,8 +460,7 @@
def AddSnapshot(self, snapshot):
"""Convert the snapshot to a proper format and publish it to clients."""
- if not self._is_centroiding:
- snapshot = self._ConvertNamedtupleToDict(snapshot)
+ snapshot = self._ConvertNamedtupleToDict(snapshot)
cherrypy.engine.publish('websocket-broadcast', json.dumps(snapshot))
return snapshot
@@ -615,7 +561,7 @@
parser.add_argument('-s', '--server_addr', default='0.0.0.0',
help='the address the webplot http server listens to')
parser.add_argument('-t', '--dut_type', default='chromeos', type=str.lower,
- help='dut type: chromeos, android, centroiding')
+ help='dut type: chromeos, android')
parser.add_argument('--automatically_start_browser', action='store_true',
help=('When this flag is set the script will try to '
'start a web browser automatically once webplot '
@@ -625,19 +571,6 @@
help=('Which protocol does the device use? Choose from '
'auto, MTB, MTA, or stylus'))
- # Arguments especial for centroiding visualizing tool.
- # Please set "--dut_type centroiding" for centroiding utility.
- parser.add_argument('-f', '--dut_forward_port', default=12345, type=int,
- help='the forwarding port for centroiding socket server '
- '(default: 12345) (only needed for centroiding)')
- parser.add_argument('-c', '--config', default='tango.conf', type=str,
- help='Config file name of device for centroiding '
- 'visualizing tool parameters.')
- parser.add_argument('--fps', default=0, type=int,
- help='the target frame rate of visualizer plotting, set '
- '0 for keeping same as centroiding processing frame '
- 'rate.')
-
args = parser.parse_args()
args.grab = not args.nograb
@@ -652,20 +585,11 @@
configure_logger(level=logging.ERROR)
args = _ParseArguments()
- # Specify Webplot for centroiding purpose.
- is_centroiding = args.dut_type == 'centroiding'
-
print('\n' + '-' * 70)
- if is_centroiding:
- cherrypy.log('**** Centroiding Data Visualizing Tool ****')
- cherrypy.log('dut config file: %s' % args.config)
- cherrypy.log('dut address: %s' % args.dut_addr)
- cherrypy.log('dut socket forwarding port: %d' % args.dut_forward_port)
- else:
- cherrypy.log('dut machine type: %s' % args.dut_type)
- cherrypy.log('dut\'s touch device: %s' %
+ cherrypy.log('dut machine type: %s' % args.dut_type)
+ cherrypy.log('dut\'s touch device: %s' %
('touchscreen' if args.is_touchscreen else 'touchpad'))
- cherrypy.log('dut address: %s' % args.dut_addr)
+ cherrypy.log('dut address: %s' % args.dut_addr)
cherrypy.log('web server address: %s' % args.server_addr)
cherrypy.log('web server port: %s' % args.server_port)
cherrypy.log('grab the touch device: %s' % args.grab)
@@ -698,18 +622,13 @@
protocol=args.protocol)
elif args.dut_type == 'android':
device = AndroidTouchDevice(args.dut_addr, True, protocol=args.protocol)
- elif is_centroiding: # args.dut_type == 'centroiding'
- # Import centroiding library conditionally to avoid missing dependency.
- from centroiding import CentroidingDataReceiver, CentroidingDevice
- device = CentroidingDevice(args.config)
else:
print('Unrecognized dut_type: %s. Webplot is aborted...' % args.dut_type)
exit(1)
# Instantiate a webplot server daemon and start it.
webplot = Webplot(args.server_addr, args.server_port, device, logging=True,
- is_behind_iptables_firewall=args.behind_firewall,
- is_centroiding=is_centroiding)
+ is_behind_iptables_firewall=args.behind_firewall)
webplot.start()
if args.automatically_start_browser:
@@ -722,13 +641,8 @@
print('Please navigate to "%s" in a browser manually, instead' % url)
print('!' * 80)
- if not is_centroiding:
- # Get touch snapshots from the touch device and have clients plot them.
- webplot.GetAndPlotSnapshots()
- else:
- receiver = CentroidingDataReceiver(
- '127.0.0.1', args.dut_forward_port, webplot, plot_fps=args.fps)
- receiver.StartReceive()
+ # Get touch snapshots from the touch device and have clients plot them.
+ webplot.GetAndPlotSnapshots()
if __name__ == '__main__':