[Libreoffice-commits] online.git: 3 commits - loleaflet/build loleaflet/debug loleaflet/.eslintignore loleaflet/src loolwsd/Admin.cpp loolwsd/Admin.hpp loolwsd/AdminModel.hpp

Pranav Kant pranavk at collabora.com
Sun Mar 13 14:15:57 UTC 2016


 loleaflet/.eslintignore                      |    1 
 loleaflet/build/adminDeps.js                 |   12 +
 loleaflet/debug/document/admin.html          |    9 
 loleaflet/debug/document/adminAnalytics.html |  107 +++++++++++
 loleaflet/debug/document/adminSettings.html  |  115 ++++++++++++
 loleaflet/src/admin/AdminSocketAnalytics.js  |  245 +++++++++++++++++++++++++++
 loleaflet/src/admin/AdminSocketBase.js       |    3 
 loleaflet/src/admin/AdminSocketOverview.js   |   86 +++++----
 loleaflet/src/admin/AdminSocketSettings.js   |   59 ++++++
 loleaflet/src/admin/Util.js                  |   13 +
 loleaflet/src/control/Control.Styles.js      |   10 -
 loolwsd/Admin.cpp                            |  170 +++++++++++++++++-
 loolwsd/Admin.hpp                            |   64 +++++++
 loolwsd/AdminModel.hpp                       |  137 +++++++++++++--
 14 files changed, 965 insertions(+), 66 deletions(-)

New commits:
commit 7197dab02993ecd791148d38e984b014bdf65881
Author: Pranav Kant <pranavk at collabora.com>
Date:   Sun Mar 13 19:23:08 2016 +0530

    loleaflet: Fix lint errors
    
    Change-Id: I4023c81cad92b6f617076b39d3cdbe34e231ede3

diff --git a/loleaflet/src/control/Control.Styles.js b/loleaflet/src/control/Control.Styles.js
index 13671cb..3ab5914 100644
--- a/loleaflet/src/control/Control.Styles.js
+++ b/loleaflet/src/control/Control.Styles.js
@@ -135,11 +135,11 @@ L.Control.Styles = L.Control.extend({
 		// For impress documents, LOK STATE_CHANGED callback return these internal names
 		// which are different from what is returned by initial .uno:StyleApply.
 		// Convert these names to our stored internal names before processing
-		var impressMapping = {	"Titel":"title","Untertitel":"subtitle",
-					"Gliederung 1":"outline1","Gliederung 2":"outline2","Gliederung 3":"outline3",
-					"Gliederung 4":"outline4","Gliederung 5":"outline5","Gliederung 6":"outline6",
-					"Gliederung 7":"outline7","Gliederung 8":"outline8","Gliederung 9":"outline9",
-					"Hintergrund":"background","Hintergrundobjekte":"backgroundobjects","Notizen":"notes"};
+		var impressMapping = {'Titel':'title', 'Untertitel':'subtitle',
+					'Gliederung 1':'outline1', 'Gliederung 2':'outline2', 'Gliederung 3':'outline3',
+					'Gliederung 4':'outline4', 'Gliederung 5':'outline5', 'Gliederung 6':'outline6',
+					'Gliederung 7':'outline7', 'Gliederung 8':'outline8', 'Gliederung 9':'outline9',
+					'Hintergrund':'background', 'Hintergrundobjekte':'backgroundobjects', 'Notizen':'notes'};
 
 		// For impress documents, template name is prefixed with style name.
 		// Strip the template name until we support it
commit 8519a6a4620cafe98a31d4c85b21ad31109865b1
Author: Pranav Kant <pranavk at collabora.com>
Date:   Mon Mar 7 01:36:51 2016 +0530

    loleaflet: Support for memory subscription
    
    Library for creating graphs: D3.js
    License: BSD
    Currently only being served by a CDN, not copied into the source
    tree.
    
    Change-Id: Ib99ec9011da489fc42799c3610612a8c3c8e5c2b

diff --git a/loleaflet/.eslintignore b/loleaflet/.eslintignore
index dfbe7ac..45d125b 100644
--- a/loleaflet/.eslintignore
+++ b/loleaflet/.eslintignore
@@ -1,2 +1,3 @@
 dist/scrollbar
 dist/contextMenu
+src/admin/Base.js
diff --git a/loleaflet/build/adminDeps.js b/loleaflet/build/adminDeps.js
index 5286a6e..25757c4 100644
--- a/loleaflet/build/adminDeps.js
+++ b/loleaflet/build/adminDeps.js
@@ -20,6 +20,18 @@ var adminDeps = {
 		src: ['admin/AdminSocketOverview.js'],
 		desc: 'Socket to handle messages in overview page.',
 		deps: ['AdminSocketBase']
+	},
+
+	AdminSocketAnalytics: {
+		src: ['admin/AdminSocketAnalytics.js'],
+		desc: 'Socket to handle messages in analytics page.',
+		deps: ['AdminSocketBase']
+	},
+
+	AdminSocketSettings: {
+		src: ['admin/AdminSocketSettings.js'],
+		desc: 'Socket to handle settings from server',
+		deps: ['AdminSocketBase']
 	}
 };
 
diff --git a/loleaflet/debug/document/admin.html b/loleaflet/debug/document/admin.html
index 4da6645..2e66709 100644
--- a/loleaflet/debug/document/admin.html
+++ b/loleaflet/debug/document/admin.html
@@ -65,9 +65,8 @@
         </div>
         <div id="navbar" class="navbar-collapse collapse">
           <ul class="nav navbar-nav navbar-right">
-            <li><a href="#">Dashboard</a></li>
-            <li><a href="#">Settings</a></li>
-            <li><a href="#">Profile</a></li>
+            <li><a href="admin.html?host=ws://localhost:9989">Dashboard</a></li>
+            <li><a href="adminSettings.html?host=ws://localhost:9989">Settings</a></li>
             <li><a href="#">Help</a></li>
           </ul>
           <form class="navbar-form navbar-right">
@@ -82,9 +81,7 @@
         <div class="col-sm-3 col-md-2 sidebar">
           <ul class="nav nav-sidebar">
             <li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
-            <li><a href="#">Reports</a></li>
-            <li><a href="#">Analytics</a></li>
-            <li><a href="#">Export</a></li>
+            <li><a href="adminAnalytics.html?host=ws://localhost:9989">Analytics</a></li>
           </ul>
         </div>
         <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
diff --git a/loleaflet/debug/document/adminAnalytics.html b/loleaflet/debug/document/adminAnalytics.html
new file mode 100644
index 0000000..903be34
--- /dev/null
+++ b/loleaflet/debug/document/adminAnalytics.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <title>LibreOffice Online - Admin console</title>
+
+    <!-- Bootstrap core CSS -->
+    <link href="../../dist/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+
+    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+    <link href="../../dist/bootstrap/assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
+
+    <!-- Custom styles for this template -->
+    <link href="../../dist/bootstrap/dashboard.css" rel="stylesheet">
+
+    <link rel="stylesheet" href="../../dist/dialog/vex.css" />
+    <link rel="stylesheet" href="../../dist/dialog/vex-theme-plain.css" />
+
+    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+    <!--[if lt IE 9]>
+      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+    <![endif]-->
+  </head>
+
+  <body>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+    <script>window.jQuery || document.write('<script src="../../dist/bootstrap/assets/js/vendor/jquery.min.js"><\/script>')</script>
+    <script src="../../dist/dialog/vex.combined.min.js"></script>
+    <script>vex.defaultOptions.className = 'vex-theme-plain';</script>
+    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+    <script src="../../dist/admin-src.js"></script>
+    <script>
+
+	function getParameterByName(name) {
+		name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+		var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+		results = regex.exec(location.search);
+		return results === null ? "" : results[1].replace(/\+/g, " ");
+	}
+
+	var host = getParameterByName('host');
+	new AdminSocketAnalytics(host);
+
+    </script>
+
+    <nav class="navbar navbar-inverse navbar-fixed-top">
+      <div class="container-fluid">
+        <div class="navbar-header">
+          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+            <span class="sr-only">Toggle navigation</span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+          </button>
+          <a class="navbar-brand" href="#">LibreOffice Online - Admin console</a>
+        </div>
+        <div id="navbar" class="navbar-collapse collapse">
+          <ul class="nav navbar-nav navbar-right">
+	    <li><a href="admin.html?host=ws://localhost:9989">Dashboard</a></li>
+            <li><a href="adminSettings.html?host=ws://localhost:9989">Settings</a></li>
+            <li><a href="#">Help</a></li>
+          </ul>
+          <form class="navbar-form navbar-right">
+            <input type="text" class="form-control" placeholder="Search...">
+          </form>
+        </div>
+      </div>
+    </nav>
+
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-sm-3 col-md-2 sidebar">
+          <ul class="nav nav-sidebar">
+            <li><a href="admin.html?host=ws://localhost:9989">Overview <span class="sr-only">(current)</span></a></li>
+            <li class="active"><a href="adminAnalytics.html?host=ws://localhost:9989">Analytics</a></li>
+          </ul>
+        </div>
+        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
+          <h1 class="page-header">Graphs</h1>
+		        <div class="graph-container">
+		          <div class="jumbotron">
+		            <svg id="visualisation" width="1000" height="500"></svg>
+		          </div>
+		        </div>
+	      </div>
+      </div>
+    </div>
+
+    <!-- Bootstrap core JavaScript
+    ================================================== -->
+    <!-- Placed at the end of the document so the pages load faster -->
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+    <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
+    <script src="../../dist/bootstrap/js/bootstrap.min.js"></script>
+    <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
+    <script src="../../dist/bootstrap/assets/js/vendor/holder.min.js"></script>
+    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+    <script src="../../dist/bootstrap/assets/js/ie10-viewport-bug-workaround.js"></script>
+  </body>
+</html>
diff --git a/loleaflet/debug/document/adminSettings.html b/loleaflet/debug/document/adminSettings.html
new file mode 100644
index 0000000..0ad46c1
--- /dev/null
+++ b/loleaflet/debug/document/adminSettings.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <title>LibreOffice Online - Admin console</title>
+
+    <!-- Bootstrap core CSS -->
+    <link href="../../dist/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+
+    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+    <link href="../../dist/bootstrap/assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
+
+    <!-- Custom styles for this template -->
+    <link href="../../dist/bootstrap/dashboard.css" rel="stylesheet">
+
+    <link rel="stylesheet" href="../../dist/dialog/vex.css" />
+    <link rel="stylesheet" href="../../dist/dialog/vex-theme-plain.css" />
+
+    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+    <!--[if lt IE 9]>
+      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+    <![endif]-->
+  </head>
+
+  <body>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+    <script>window.jQuery || document.write('<script src="../../dist/bootstrap/assets/js/vendor/jquery.min.js"><\/script>')</script>
+    <script src="../../dist/dialog/vex.combined.min.js"></script>
+    <script>vex.defaultOptions.className = 'vex-theme-plain';</script>
+    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+    <script src="../../dist/admin-src.js"></script>
+    <script>
+
+	function getParameterByName(name) {
+		name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+		var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+		results = regex.exec(location.search);
+		return results === null ? "" : results[1].replace(/\+/g, " ");
+	}
+
+	var host = getParameterByName('host');
+	var settingsSocket = new AdminSocketSettings(host);
+
+    </script>
+
+    <nav class="navbar navbar-inverse navbar-fixed-top">
+      <div class="container-fluid">
+        <div class="navbar-header">
+          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+            <span class="sr-only">Toggle navigation</span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+          </button>
+          <a class="navbar-brand" href="#">LibreOffice Online - Admin console</a>
+        </div>
+        <div id="navbar" class="navbar-collapse collapse">
+          <ul class="nav navbar-nav navbar-right">
+            <li><a href="admin.html?host=ws://localhost:9989">Dashboard</a></li>
+            <li><a href="adminSettings.html?host=ws://localhost:9989">Settings</a></li>
+            <li><a href="#">Help</a></li>
+          </ul>
+          <form class="navbar-form navbar-right">
+            <input type="text" class="form-control" placeholder="Search...">
+          </form>
+        </div>
+      </div>
+    </nav>
+
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-sm-3 col-md-2 sidebar">
+          <ul class="nav nav-sidebar">
+            <li><a href="admin.html?host=ws://localhost:9989">Overview <span class="sr-only">(current)</span></a></li>
+            <li><a href="adminAnalytics.html?host=ws://localhost:9989">Analytics</a></li>
+          </ul>
+        </div>
+        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
+          <h1 class="page-header">Settings</h1>
+	  <form id="admin_settings">
+	    <label for="mem_stats_size">Memory Stats Cache size</label>
+	    <input type="text" id="mem_stats_size" name="Memory Stats Size"><br/>
+	    <label for="mem_stats_interval">Memory Stats Interval (in ms)</label>
+	    <input type="text" id="mem_stats_interval" name="Memory Stats Interval"><br/>
+
+	    <label for="cpu_stats_size">Cpu Stats Cache size</label>
+	    <input type="text" id="cpu_stats_size" name="Cpu Stats Size"><br/>
+	    <label for="cpu_stats_interval">Cpu Stats Interval (in ms)</label>
+	    <input type="text" id="cpu_stats_interval" name="Cpu Stats Interval"><br/>
+	    <input type="submit" value="Save"/><br/>
+	  </form>
+	</div>
+      </div>
+      </div>
+    </div>
+
+    <!-- Bootstrap core JavaScript
+    ================================================== -->
+    <!-- Placed at the end of the document so the pages load faster -->
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+    <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
+    <script src="../../dist/bootstrap/js/bootstrap.min.js"></script>
+    <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
+    <script src="../../dist/bootstrap/assets/js/vendor/holder.min.js"></script>
+    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+    <script src="../../dist/bootstrap/assets/js/ie10-viewport-bug-workaround.js"></script>
+  </body>
+</html>
diff --git a/loleaflet/src/admin/AdminSocketAnalytics.js b/loleaflet/src/admin/AdminSocketAnalytics.js
new file mode 100644
index 0000000..2f34b76
--- /dev/null
+++ b/loleaflet/src/admin/AdminSocketAnalytics.js
@@ -0,0 +1,245 @@
+/*
+	Socket to be intialized on opening the analytics page in Admin console
+	containing various graphs to show to the user on specified interval
+*/
+
+/* global d3 Util AdminSocketBase */
+var AdminSocketAnalytics = AdminSocketBase.extend({
+	constructor: function(host) {
+		this.base(host);
+	},
+
+	_memStatsData: [],
+	_cpuStatsData: [],
+
+	_memStatsSize: 0,
+	_memStatsInterval: 0,
+
+	_cpuStatsSize: 0,
+	_cpuStatsInterval: 0,
+
+	_initMemStatsData: function(memStatsSize, memStatsInterval, reset) {
+		if (reset) {
+			this._memStatsData = [];
+		}
+
+		var offset = this._memStatsData.length * memStatsInterval;
+		for (var i = 0; i < memStatsSize; i++) {
+			this._memStatsData.unshift({time: -(offset), value: 0});
+			offset += memStatsInterval;
+		}
+	},
+
+	_initCpuStatsData: function() {
+		for (var i = 0; i < this._cpuStatsSize; i++) {
+			this._cpuStatsData.push({time: -((this._cpuStatsSize - i - 1) * this._cpuStatsInterval), value: 0});
+		}
+	},
+
+	onSocketOpen: function() {
+		this.socket.send('subscribe mem_stats cpu_stats settings');
+		this.socket.send('settings');
+		this.socket.send('mem_stats');
+	},
+
+	_createMemData: function() {
+		for (var i = this._memStatsRawData.length - 1, j = this._memStatsData.length - 1; i >= 0 && j >= 0; i--, j--) {
+			this._memStatsData[j].value = parseInt(this._memStatsRawData[i]);
+		}
+	},
+
+	_d3xAxis: null,
+	_d3yAxis: null,
+	_d3line: null,
+	_xScale: null,
+	_yScale: null,
+
+	_graphWidth: 1000,
+	_graphHeight: 500,
+	_graphMargins: {
+		top: 20,
+		right: 20,
+		bottom: 20,
+		left: 100
+	},
+
+	_setUpAxis: function() {
+		this._xScale = d3.scale.linear().range([this._graphMargins.left, this._graphWidth - this._graphMargins.right]).domain([d3.min(this._memStatsData, function(d) {
+				return d.time;
+			}), d3.max(this._memStatsData, function(d) {
+				return d.time;
+			})]);
+
+
+		this._yScale = d3.scale.linear().range([this._graphHeight - this._graphMargins.bottom, this._graphMargins.top]).domain([d3.min(this._memStatsData, function(d) {
+			return d.value;
+		}), d3.max(this._memStatsData, function(d) {
+			return d.value;
+		})]);
+
+		this._d3xAxis = d3.svg.axis()
+			.scale(this._xScale)
+			.tickFormat(function(d) {
+				d = Math.abs(d / 1000);
+				var units = ['s', 'min', 'hr'];
+				for (var i = 0; i < units.length && Math.abs(d) >= 60; i++) {
+					d = parseInt(d / 60);
+				}
+				return parseInt(d) + units[i] + ' ago';
+			});
+
+		this._d3yAxis = d3.svg.axis()
+			.scale(this._yScale)
+			.tickFormat(function (d) {
+				return Util.humanize(d);
+			})
+			.orient('left');
+
+		var xScale = this._xScale;
+		var yScale = this._yScale;
+
+		this._d3line = d3.svg.line()
+			.x(function(d) {
+				return xScale(d.time);
+			})
+			.y(function(d) {
+				return yScale(d.value);
+			});
+	},
+
+	_createMemGraph: function() {
+		var vis = d3.select('#visualisation');
+
+		this._setUpAxis();
+
+		vis.append('svg:g')
+		.attr('class', 'x-axis')
+		.attr('transform', 'translate(0,' + (this._graphHeight - this._graphMargins.bottom) + ')')
+		.call(this._d3xAxis);
+
+		vis.append('svg:g')
+		.attr('class', 'y-axis')
+		.attr('transform', 'translate(' + this._graphMargins.left + ',0)')
+		.call(this._d3yAxis);
+
+		vis.append('svg:path')
+			.attr('d', this._d3line(this._memStatsData))
+			.attr('class', 'line')
+			.attr('stroke', 'blue')
+			.attr('stroke-width', 2)
+			.attr('fill', 'none');
+	},
+
+	_addNewMemData: function(data) {
+		// make a space for new data
+		for (var i = this._memStatsData.length - 1; i > 0; i--) {
+			this._memStatsData[i].time = this._memStatsData[i - 1].time;
+		}
+
+		// push new data at time '0'
+		this._memStatsData.push({time: 0, value: parseInt(data)});
+
+		// remove extra items
+		if (this._memStatsData.length > this._memStatsSize) {
+			this._memStatsData.shift();
+		}
+	},
+
+	_updateMemGraph: function() {
+		var svg = d3.select('#visualisation');
+
+		this._setUpAxis();
+
+		svg.select('.line')
+		.attr('d', this._d3line(this._memStatsData));
+
+		svg.select('.x-axis')
+		.call(this._d3xAxis);
+
+		svg.select('.y-axis')
+		.call(this._d3yAxis);
+	},
+
+	onSocketMessage: function(e) {
+		var textMsg;
+		if (typeof e.data === 'string') {
+			textMsg = e.data;
+		}
+		else {
+			textMsg = '';
+		}
+
+
+		if (textMsg.startsWith('settings')) {
+			textMsg = textMsg.substring('settings '.length);
+			textMsg = textMsg.split(' ');
+
+			//TODO: Add CPU statistics
+			var memStatsSize, memStatsInterval, cpuStatsSize, cpuStatsInterval;
+			var i, j, data;
+			memStatsSize = this._memStatsSize;
+			memStatsInterval = this._memStatsInterval;
+			cpuStatsSize = this._cpuStatsSize;
+			cpuStatsInterval = this._cpuStatsInterval;
+			for (i = 0; i < textMsg.length; i++) {
+				var setting = textMsg[i].split('=');
+				if (setting[0] === 'mem_stats_size') {
+					memStatsSize = parseInt(setting[1]);
+				}
+				else if (setting[0] === 'mem_stats_interval') {
+					memStatsInterval = parseInt(setting[1]);
+				}
+				else if (setting[0] === 'cpu_stats_size') {
+					cpuStatsSize = parseInt(setting[1]);
+				}
+				else if (setting[0] === 'cpu_stats_interval') {
+					cpuStatsInterval = parseInt(setting[1]);
+				}
+			}
+
+			// Fix the axes according to changed data
+			if (memStatsInterval !== this._memStatsInterval) {
+				// We can possibly reuse the data with a bit of work
+				this._initMemStatsData(memStatsSize, memStatsInterval, true);
+			}
+			else if (memStatsSize > this._memStatsSize) {
+				this._initMemStatsData(memStatsSize - this._memStatsSize, memStatsInterval, false);
+			}
+			else {
+				// just strip the extra items
+				for (i = 0; i < this._memStatsSize - memStatsSize; i++) {
+					this._memStatsData.shift();
+				}
+			}
+
+			this._memStatsSize = memStatsSize;
+			this._memStatsInterval = memStatsInterval;
+			this._cpuStatsSize = cpuStatsSize;
+			this._cpuStatsInterval = cpuStatsInterval;
+		}
+		else if (textMsg.startsWith('mem_stats') ||
+			textMsg.startsWith('cpu_stats')) {
+			textMsg = textMsg.split(' ')[1];
+			if (textMsg.endsWith(',')) {
+				// This is the result of query, not notification
+				data = textMsg.substring(0, textMsg.length - 1).split(',');
+				for (i = this._memStatsData.length - 1, j = data.length - 1; i >= 0 && j >= 0; i--, j--) {
+					this._memStatsData[i].value = parseInt(data[j]);
+				}
+
+				//this._createMemData(data);
+				this._createMemGraph();
+			}
+			else {
+				// this is a notification data; append to _memStatsData
+				data = textMsg.trim();
+				this._addNewMemData(data);
+				this._updateMemGraph();
+			}
+		}
+	},
+
+	onSocketClose: function() {
+		clearInterval(this._basicStatsIntervalId);
+	}
+});
diff --git a/loleaflet/src/admin/AdminSocketBase.js b/loleaflet/src/admin/AdminSocketBase.js
index f82bcfc..c0216d0 100644
--- a/loleaflet/src/admin/AdminSocketBase.js
+++ b/loleaflet/src/admin/AdminSocketBase.js
@@ -1,6 +1,9 @@
 /*
 	Abstract class
 */
+
+/* global vex Base */
+/* exported AdminSocketBase */
 var AdminSocketBase = Base.extend({
 	socket: null,
 
diff --git a/loleaflet/src/admin/AdminSocketOverview.js b/loleaflet/src/admin/AdminSocketOverview.js
index 6371d8e..ea62e55 100644
--- a/loleaflet/src/admin/AdminSocketOverview.js
+++ b/loleaflet/src/admin/AdminSocketOverview.js
@@ -1,6 +1,7 @@
 /*
 	Socket to be intialized on opening the overview page in Admin console
 */
+/* global vex $ Util AdminSocketBase */
 var AdminSocketOverview = AdminSocketBase.extend({
 	constructor: function(host) {
 		this.base(host);
@@ -40,7 +41,7 @@ var AdminSocketOverview = AdminSocketBase.extend({
 			$('#rowContextMenu').hide();
 		});
 
-		$('#rowContextMenu').on('click', 'a', function(e) {
+		$('#rowContextMenu').on('click', 'a', function() {
 			vex.dialog.confirm({
 				message: 'Are you sure you want to kill this child ?',
 				callback: function(value) {
@@ -59,86 +60,100 @@ var AdminSocketOverview = AdminSocketBase.extend({
 		if (typeof e.data === 'string') {
 			textMsg = e.data;
 		}
-		else
+		else {
 			textMsg = '';
+		}
 
 		var tableContainer = document.getElementById('doclist');
+		var rowContainer;
+		var pidEle, urlEle, viewsEle, memEle, docEle;
+		var nViews, nTotalViews;
+		var docProps, sPid, sUrl, sViews, sMem;
 		if (textMsg.startsWith('documents')) {
 			var documents = textMsg.substring('documents'.length);
 			documents = documents.trim().split('\n');
 			for (var i = 0; i < documents.length; i++) {
-				if (documents[i] == '')
+				if (documents[i] === '') {
 					continue;
-				var docProps = documents[i].trim().split(' ');
-				var sPid = docProps[0];
-				var sUrl = docProps[1];
-				var sViews = docProps[2];
-				var sMem = docProps[3];
+				}
+				docProps = documents[i].trim().split(' ');
+				sPid = docProps[0];
+				sUrl = docProps[1];
+				sViews = docProps[2];
+				sMem = docProps[3];
 				if (sUrl === '0') {
 					continue;
 				}
-				var rowContainer = document.createElement('tr');
+				rowContainer = document.createElement('tr');
 				rowContainer.id = 'doc' + sPid;
 				tableContainer.appendChild(rowContainer);
 
-				var pidEle = document.createElement('td');
+				pidEle = document.createElement('td');
 				pidEle.innerHTML = sPid;
 				rowContainer.appendChild(pidEle);
-				var urlEle = document.createElement('td');
+
+				urlEle = document.createElement('td');
 				urlEle.innerHTML = sUrl;
 				rowContainer.appendChild(urlEle);
-				var viewsEle = document.createElement('td');
+
+				viewsEle = document.createElement('td');
 				viewsEle.id = 'docview' + sPid;
 				viewsEle.innerHTML = sViews;
 				rowContainer.appendChild(viewsEle);
-				var memEle = document.createElement('td');
-				memEle.innerHTML = sMem;
+
+				memEle = document.createElement('td');
+				memEle.innerHTML = Util.humanize(parseInt(sMem));
 				rowContainer.appendChild(memEle);
 			}
 		}
 		else if (textMsg.startsWith('addview')) {
-			var sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
-			var nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
+			sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
+			nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
 			document.getElementById('docview' + sPid).innerHTML = nViews + 1;
-			var nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
+			nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
 			document.getElementById('active_users_count').innerHTML = nTotalViews + 1;
 		}
 		else if (textMsg.startsWith('rmview')) {
-			var sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
-			var nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
+			sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
+			nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
 			document.getElementById('docview' + sPid).innerHTML = nViews - 1;
-			var nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
+			nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
 			document.getElementById('active_users_count').innerHTML = nTotalViews - 1;
 		}
 		else if (textMsg.startsWith('document')) {
 			textMsg = textMsg.substring('document'.length);
-			var docProps = textMsg.trim().split(' ');
-			var sPid = docProps[0];
-			var sUrl = docProps[1];
-			var sMem = docProps[2];
-			var docEle = document.getElementById('doc' + sPid);
+			docProps = textMsg.trim().split(' ');
+			sPid = docProps[0];
+			sUrl = docProps[1];
+			sMem = docProps[2];
+
+			docEle = document.getElementById('doc' + sPid);
 			if (docEle) {
 				tableContainer.removeChild(docEle);
 			}
 			if (sUrl === '0') {
 				return;
 			}
-			var rowContainer = document.createElement('tr');
+
+			rowContainer = document.createElement('tr');
 			rowContainer.id = 'doc' + docProps[0];
 			tableContainer.appendChild(rowContainer);
 
-			var pidEle = document.createElement('td');
+			pidEle = document.createElement('td');
 			pidEle.innerHTML = docProps[0];
 			rowContainer.appendChild(pidEle);
-			var urlEle = document.createElement('td');
+
+			urlEle = document.createElement('td');
 			urlEle.innerHTML = docProps[1];
 			rowContainer.appendChild(urlEle);
-			var viewsEle = document.createElement('td');
+
+			viewsEle = document.createElement('td');
 			viewsEle.innerHTML = 0;
 			viewsEle.id = 'docview' + docProps[0];
 			rowContainer.appendChild(viewsEle);
-			var memEle = document.createElement('td');
-			memEle.innerHTML = sMem;
+
+			memEle = document.createElement('td');
+			memEle.innerHTML = Util.humanize(parseInt(sMem));
 			rowContainer.appendChild(memEle);
 
 			var totalUsersEle = document.getElementById('active_docs_count');
@@ -152,13 +167,16 @@ var AdminSocketOverview = AdminSocketBase.extend({
 			var sCommand = textMsg[0];
 			var nData = parseInt(textMsg[1]);
 
+			if (sCommand === 'total_mem') {
+				nData = Util.humanize(nData);
+			}
 			document.getElementById(sCommand).innerHTML = nData;
 		}
 		else if (textMsg.startsWith('rmdoc')) {
 			textMsg = textMsg.substring('rmdoc'.length);
-			var docProps = textMsg.trim().split(' ');
-			var sPid = docProps[0];
-			var docEle = document.getElementById('doc' + sPid);
+			docProps = textMsg.trim().split(' ');
+			sPid = docProps[0];
+			docEle = document.getElementById('doc' + sPid);
 			if (docEle) {
 				tableContainer.removeChild(docEle);
 			}
diff --git a/loleaflet/src/admin/AdminSocketSettings.js b/loleaflet/src/admin/AdminSocketSettings.js
new file mode 100644
index 0000000..070cd69
--- /dev/null
+++ b/loleaflet/src/admin/AdminSocketSettings.js
@@ -0,0 +1,59 @@
+/*
+	Socket to be intialized on opening the settings page in Admin console
+*/
+/* global $ AdminSocketBase */
+var AdminSocketSettings = AdminSocketBase.extend({
+	constructor: function(host) {
+		this.base(host);
+		this._init();
+	},
+
+	_init: function() {
+		var socketSettings = this.socket;
+		$(document).ready(function() {
+			$('#admin_settings').on('submit', function(e) {
+				e.preventDefault();
+				var memStatsSize = $('#mem_stats_size').val();
+				var memStatsInterval = $('#mem_stats_interval').val();
+				var cpuStatsSize = $('#cpu_stats_size').val();
+				var cpuStatsInterval = $('#cpu_stats_interval').val();
+				var command = 'set';
+				command += ' mem_stats_size=' + memStatsSize;
+				command += ' mem_stats_interval=' + memStatsInterval;
+				command += ' cpu_stats_size=' + cpuStatsSize;
+				command += ' cpu_stats_interval=' + cpuStatsInterval;
+				socketSettings.send(command);
+			});
+		});
+	},
+
+	onSocketOpen: function() {
+		this.socket.send('subscribe settings');
+		this.socket.send('settings');
+	},
+
+    onSocketMessage: function(e) {
+		var textMsg;
+		if (typeof e.data === 'string') {
+			textMsg = e.data;
+		}
+		else {
+			textMsg = '';
+		}
+
+		if (textMsg.startsWith('settings')) {
+			textMsg = textMsg.substring('settings '.length);
+			var settings = textMsg.split(' ');
+			for (var i = 0; i < settings.length; i++) {
+				var setting = settings[i].split('=');
+				var settingKey = setting[0];
+				var settingVal = setting[1];
+				document.getElementById(settingKey).value = settingVal;
+			}
+		}
+	},
+
+	onSocketClose: function() {
+		clearInterval(this._basicStatsIntervalId);
+	}
+});
diff --git a/loleaflet/src/admin/Util.js b/loleaflet/src/admin/Util.js
index 5ed7da2..a409328 100644
--- a/loleaflet/src/admin/Util.js
+++ b/loleaflet/src/admin/Util.js
@@ -1,6 +1,19 @@
 /*
 	Utility class
 */
+/* global Base */
 var Util = Base.extend({
 	constructor: null
+
+}, { // class itnerface
+
+	humanize: function humanFileSize(kbytes) {
+		var unit = 1000;
+		var units = ['kB', 'MB', 'GB', 'TB'];
+		for (var i = 0; Math.abs(kbytes) >= unit && i < units.length; i++) {
+			kbytes /= unit;
+		}
+
+		return kbytes.toFixed(1) + ' ' + units[i];
+	}
 });
commit ea29196fed2b4ffded88a414b7252594209722e1
Author: Pranav Kant <pranavk at collabora.com>
Date:   Sun Mar 6 02:09:35 2016 +0530

    loolwsd: Periodic memory usage subscription
    
    By default, queries and stores the total memory usage in
    AdminModel every 5 seconds, and caches the last 100 such values.
    Both cache size and interval can be changed by simple commands
    from the clients.
    
    Change-Id: I86cf8228d0129dc8aab0a03856c12dfeb240b169

diff --git a/loolwsd/Admin.cpp b/loolwsd/Admin.cpp
index b3fb390..5d77ebb 100644
--- a/loolwsd/Admin.cpp
+++ b/loolwsd/Admin.cpp
@@ -8,6 +8,7 @@
  */
 
 #include <cassert>
+#include <mutex>
 #include <sys/poll.h>
 #include <sys/prctl.h>
 
@@ -18,6 +19,7 @@
 #include <Poco/Net/NetException.h>
 #include <Poco/Net/WebSocket.h>
 #include <Poco/StringTokenizer.h>
+#include <Poco/Util/Timer.h>
 
 #include "Admin.hpp"
 #include "AdminModel.hpp"
@@ -65,9 +67,12 @@ public:
 
             auto ws = std::make_shared<WebSocket>(request, response);
 
-            // Subscribe the websocket of any AdminModel updates
-            AdminModel& model = _admin->getModel();
-            model.subscribe(nSessionId, ws);
+            {
+                std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+                // Subscribe the websocket of any AdminModel updates
+                AdminModel& model = _admin->getModel();
+                model.subscribe(nSessionId, ws);
+            }
 
             const Poco::Timespan waitTime(POLL_TIMEOUT_MS * 1000);
             int flags = 0;
@@ -118,6 +123,10 @@ public:
                         if (tokens.count() < 1)
                             continue;
 
+                        // Lock the model mutex before interacting with it
+                        std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+                        AdminModel& model = _admin->getModel();
+
                         if (tokens[0] == "stats")
                         {
                             //TODO: Collect stats and reply back to admin.
@@ -168,13 +177,9 @@ public:
                         }
                         else if (tokens[0] == "total_mem")
                         {
-                            Poco::Process::PID nBrokerPid = _admin->getBrokerPid();
-                            unsigned totalMem = Util::getMemoryUsage(nBrokerPid);
-                            totalMem += model.getTotalMemoryUsage();
-                            totalMem += Util::getMemoryUsage(Poco::Process::id());
-
-                            std::string responseFrame = "total_mem " + std::to_string(totalMem);
-                            ws->sendFrame(responseFrame.data(), responseFrame.size());
+                            unsigned totalMem = _admin->getTotalMemoryUsage(model);
+                            std::string response = "total_mem " + std::to_string(totalMem);
+                            ws->sendFrame(response.data(), response.size());
                         }
                         else if (tokens[0] == "active_users_count")
                         {
@@ -199,6 +204,89 @@ public:
                                 Log::warn() << "Could not kill given PID" << Log::end;
                             }
                         }
+                        else if (tokens[0] == "mem_stats")
+                        {
+                            std::ostringstream oss;
+                            oss << tokens[0] << " "
+                                << model.query(tokens[0]);
+
+                            std::string response = oss.str();
+                            ws->sendFrame(response.data(), response.size());
+                        }
+                        else if (tokens[0] == "cpu_stats")
+                        {
+                            std::ostringstream oss;
+                            oss << tokens[0] << " "
+                                << model.query(tokens[0]);
+
+                            std::string response = oss.str();
+                            ws->sendFrame(response.data(), response.size());
+                        }
+                        else if (tokens[0] == "settings")
+                        {
+                            // for now, we have only these settings
+                            std::ostringstream oss;
+                            oss << tokens[0] << " "
+                                << "mem_stats_size=" << model.query("mem_stats_size") << " "
+                                << "mem_stats_interval=" << std::to_string(_admin->getMemStatsInterval()) << " "
+                                << "cpu_stats_size="  << model.query("cpu_stats_size") << " "
+                                << "cpu_stats_interval=" << std::to_string(_admin->getCpuStatsInterval());
+
+                            std::string response = oss.str();
+                            ws->sendFrame(response.data(), response.size());
+                        }
+                        else if (tokens[0] == "set" && tokens.count() > 1)
+                        {
+                            for (unsigned i = 1; i < tokens.count(); i++)
+                            {
+                                StringTokenizer setting(tokens[i], "=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+                                unsigned settingVal = 0;
+                                try
+                                {
+                                    settingVal = std::stoi(setting[1]);
+                                }
+                                catch (const std::exception& exc)
+                                {
+                                    Log::warn() << "Invalid setting value: "
+                                                << setting[1] << " for "
+                                                << setting[0] << Log::end;
+                                    continue;
+                                }
+
+                                if (setting[0] == "mem_stats_size")
+                                {
+                                    if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
+                                    {
+                                        model.setMemStatsSize(settingVal);
+                                    }
+                                }
+                                else if (setting[0] == "mem_stats_interval")
+                                {
+                                    if (settingVal != _admin->getMemStatsInterval())
+                                    {
+                                        _admin->rescheduleMemTimer(settingVal);
+                                        model.clearMemStats();
+                                        model.notify("settings mem_stats_interval=" + std::to_string(settingVal));
+                                    }
+                                }
+                                else if (setting[0] == "cpu_stats_size")
+                                {
+                                    if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
+                                    {
+                                        model.setCpuStatsSize(settingVal);
+                                    }
+                                }
+                                else if (setting[0] == "cpu_stats_interval")
+                                {
+                                    if (settingVal != _admin->getCpuStatsInterval())
+                                    {
+                                        _admin->rescheduleCpuTimer(settingVal);
+                                        model.clearCpuStats();
+                                        model.notify("settings cpu_stats_interval=" + std::to_string(settingVal));
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
             }
@@ -282,9 +370,65 @@ Admin::~Admin()
 
 void Admin::handleInput(std::string& message)
 {
+    std::lock_guard<std::mutex> modelLock(modelMutex);
     _model.update(message);
 }
 
+void MemoryStats::run()
+{
+    std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+    AdminModel& model = _admin->getModel();
+    unsigned totalMem = _admin->getTotalMemoryUsage(model);
+
+    Log::trace() << "Total memory used: " << std::to_string(totalMem);
+    model.addMemStats(totalMem);
+}
+
+void CpuStats::run()
+{
+    //TODO: Implement me
+    //std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+    //model.addCpuStats(totalMem);
+}
+
+void Admin::rescheduleMemTimer(unsigned interval)
+{
+    _memStatsTask->cancel();
+    _memStatsTaskInterval = interval;
+    _memStatsTask = new MemoryStats(this);
+    _memStatsTimer.schedule(_memStatsTask, _memStatsTaskInterval, _memStatsTaskInterval);
+    Log::info("Memory stats interval changed - New interval: " + std::to_string(interval));
+}
+
+void Admin::rescheduleCpuTimer(unsigned interval)
+{
+    _cpuStatsTask->cancel();
+    _cpuStatsTaskInterval = interval;
+    _cpuStatsTask = new CpuStats(this);
+    _cpuStatsTimer.schedule(_cpuStatsTask, _cpuStatsTaskInterval, _cpuStatsTaskInterval);
+    Log::info("CPU stats interval changed - New interval: " + std::to_string(interval));
+}
+
+unsigned Admin::getTotalMemoryUsage(AdminModel& model)
+{
+    Poco::Process::PID nBrokerPid = getBrokerPid();
+    unsigned totalMem = Util::getMemoryUsage(nBrokerPid);
+    totalMem += model.getTotalMemoryUsage();
+    totalMem += Util::getMemoryUsage(Poco::Process::id());
+
+    return totalMem;
+}
+
+unsigned Admin::getMemStatsInterval()
+{
+    return _memStatsTaskInterval;
+}
+
+unsigned Admin::getCpuStatsInterval()
+{
+    return _cpuStatsTaskInterval;
+}
+
 void Admin::run()
 {
     Log::info("Listening on Admin port " + std::to_string(ADMIN_PORT_NUMBER));
@@ -292,6 +436,12 @@ void Admin::run()
     // Start a server listening on the admin port.
     _srv.start();
 
+    _memStatsTask = new MemoryStats(this);
+    _memStatsTimer.schedule(_memStatsTask, _memStatsTaskInterval, _memStatsTaskInterval);
+
+    _cpuStatsTask = new CpuStats(this);
+    _cpuStatsTimer.schedule(_cpuStatsTask, _cpuStatsTaskInterval, _cpuStatsTaskInterval);
+
     // Start listening for data changes
     struct pollfd pollPipeNotify;
     pollPipeNotify.fd = NotifyPipe;
diff --git a/loolwsd/Admin.hpp b/loolwsd/Admin.hpp
index c02f824..3b98b9b 100644
--- a/loolwsd/Admin.hpp
+++ b/loolwsd/Admin.hpp
@@ -13,6 +13,8 @@
 #include <Poco/Net/HTTPServer.h>
 #include <Poco/Runnable.h>
 #include <Poco/Types.h>
+#include <Poco/Util/Timer.h>
+#include <Poco/Util/TimerTask.h>
 
 #include "AdminModel.hpp"
 
@@ -30,10 +32,24 @@ public:
 
     static int getBrokerPipe() { return Admin::BrokerPipe; }
 
+    unsigned getTotalMemoryUsage(AdminModel&);
+
     void run() override;
 
+    /// Callers must ensure that modelMutex is acquired
     AdminModel& getModel();
 
+    unsigned getMemStatsInterval();
+
+    unsigned getCpuStatsInterval();
+
+    void rescheduleMemTimer(unsigned interval);
+
+    void rescheduleCpuTimer(unsigned interval);
+
+public:
+    std::mutex modelMutex;
+
 private:
     void handleInput(std::string& message);
 
@@ -41,11 +57,59 @@ private:
     Poco::Net::HTTPServer _srv;
     AdminModel _model;
 
+    Poco::Util::Timer _memStatsTimer;
+    Poco::Util::TimerTask::Ptr _memStatsTask;
+    unsigned _memStatsTaskInterval = 5000;
+
+    Poco::Util::Timer _cpuStatsTimer;
+    Poco::Util::TimerTask::Ptr _cpuStatsTask;
+    unsigned _cpuStatsTaskInterval = 5000;
+
     static Poco::Process::PID BrokerPid;
     static int BrokerPipe;
     static int NotifyPipe;
 };
 
+class MemoryStats : public Poco::Util::TimerTask
+{
+public:
+    MemoryStats(Admin* admin)
+        : _admin(admin)
+    {
+        Log::info("Memory stat ctor");
+    }
+
+    ~MemoryStats()
+    {
+        Log::info("Memory stat dtor");
+    }
+
+    void run() override;
+
+private:
+    Admin* _admin;
+};
+
+class CpuStats : public Poco::Util::TimerTask
+{
+public:
+    CpuStats(Admin* admin)
+        : _admin(admin)
+    {
+        Log::info("Cpu stat ctor");
+    }
+
+    ~CpuStats()
+    {
+        Log::info("Cpu stat dtor");
+    }
+
+    void run() override;
+
+private:
+    Admin* _admin;
+};
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/AdminModel.hpp b/loolwsd/AdminModel.hpp
index 0d9f21c..1ab242d 100644
--- a/loolwsd/AdminModel.hpp
+++ b/loolwsd/AdminModel.hpp
@@ -14,6 +14,7 @@
 #include <set>
 #include <sstream>
 #include <string>
+#include <queue>
 
 #include <Poco/Net/WebSocket.h>
 #include <Poco/Process.h>
@@ -265,6 +266,22 @@ public:
         {
             return std::to_string(_nActiveDocuments);
         }
+        else if (tokens[0] == "mem_stats")
+        {
+            return getMemStats();
+        }
+        else if (tokens[0] == "mem_stats_size")
+        {
+            return std::to_string(_memStatsSize);
+        }
+        else if (tokens[0] == "cpu_stats")
+        {
+            return getCpuStats();
+        }
+        else if (tokens[0] == "cpu_stats_size")
+        {
+            return std::to_string(_cpuStatsSize);
+        }
 
         return std::string("");
     }
@@ -311,6 +328,92 @@ public:
         subscriber->second.unsubscribe(command);
     }
 
+    void clearMemStats()
+    {
+        _memStats.clear();
+    }
+
+    void clearCpuStats()
+    {
+        _cpuStats.clear();
+    }
+
+    void addMemStats(unsigned memUsage)
+    {
+        _memStats.push_back(memUsage);
+        if (_memStats.size() > _memStatsSize)
+        {
+            _memStats.pop_front();
+        }
+
+        std::ostringstream oss;
+        oss << "mem_stats "
+            << std::to_string(memUsage);
+        notify(oss.str());
+    }
+
+    void addCpuStats(unsigned cpuUsage)
+    {
+        _cpuStats.push_back(cpuUsage);
+        if (_cpuStats.size() > _cpuStatsSize)
+        {
+            _cpuStats.pop_front();
+        }
+
+        std::ostringstream oss;
+        oss << "cpu_stats "
+            << std::to_string(cpuUsage);
+        notify(oss.str());
+    }
+
+    void setCpuStatsSize(unsigned size)
+    {
+        int wasteValuesLen = _cpuStats.size() - size;
+        while (wasteValuesLen-- > 0)
+        {
+            _cpuStats.pop_front();
+        }
+        _cpuStatsSize = size;
+
+        std::ostringstream oss;
+        oss << "settings "
+            << "cpu_stats_size="
+            << std::to_string(_cpuStatsSize);
+        notify(oss.str());
+    }
+
+    void setMemStatsSize(unsigned size)
+    {
+        int wasteValuesLen = _memStats.size() - size;
+        while (wasteValuesLen-- > 0)
+        {
+            _memStats.pop_front();
+        }
+        _memStatsSize = size;
+
+        std::ostringstream oss;
+        oss << "settings "
+            << "mem_stats_size="
+            << std::to_string(_memStatsSize);
+        notify(oss.str());
+    }
+
+    void notify(const std::string& message)
+    {
+        auto it = std::begin(_subscribers);
+        while (it != std::end(_subscribers))
+        {
+            if (!it->second.notify(message))
+            {
+                it = _subscribers.erase(it);
+            }
+            else
+            {
+                it++;
+            }
+        }
+    }
+
 private:
     // FIXME: we have a problem if new document to be added has PID = expired document in the map
     // Prolly, *move* expired documents to another container (?)
@@ -337,20 +440,26 @@ private:
         }
     }
 
-    void notify(const std::string& message)
+    std::string getMemStats()
     {
-        auto it = std::begin(_subscribers);
-        while (it != std::end(_subscribers))
+        std::string response;
+        for (auto& i: _memStats)
         {
-            if (!it->second.notify(message))
-            {
-                it = _subscribers.erase(it);
-            }
-            else
-            {
-                it++;
-            }
+            response += std::to_string(i) + ",";
         }
+
+        return response;
+    }
+
+    std::string getCpuStats()
+    {
+        std::string response;
+        for (auto& i: _cpuStats)
+        {
+            response += std::to_string(i) + ",";
+        }
+
+        return response;
     }
 
     unsigned getTotalActiveViews()
@@ -393,6 +502,12 @@ private:
     std::map<int, Subscriber> _subscribers;
     std::map<Poco::Process::PID, Document> _documents;
 
+    std::list<unsigned> _memStats;
+    unsigned _memStatsSize = 100;
+
+    std::list<unsigned> _cpuStats;
+    unsigned _cpuStatsSize = 100;
+
     /// Number of active documents
     unsigned _nActiveDocuments = 0;
 };


More information about the Libreoffice-commits mailing list