module.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. define([
  2. 'angular',
  3. 'app',
  4. 'jquery',
  5. 'lodash',
  6. 'kbn',
  7. 'moment',
  8. 'components/timeSeries',
  9. 'components/panelmeta',
  10. './seriesOverridesCtrl',
  11. './legend',
  12. 'services/panelSrv',
  13. 'services/annotationsSrv',
  14. 'services/datasourceSrv',
  15. 'jquery.flot',
  16. 'jquery.flot.events',
  17. 'jquery.flot.selection',
  18. 'jquery.flot.time',
  19. 'jquery.flot.stack',
  20. 'jquery.flot.stackpercent',
  21. 'jquery.flot.fillbelow',
  22. 'jquery.flot.crosshair'
  23. ],
  24. function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
  25. 'use strict';
  26. var module = angular.module('grafana.panels.graph');
  27. module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv) {
  28. $scope.panelMeta = new PanelMeta({
  29. description: 'Graph panel',
  30. fullscreen: true,
  31. metricsEditor: true
  32. });
  33. $scope.panelMeta.addEditorTab('Axes & Grid', 'app/panels/graph/axisEditor.html');
  34. $scope.panelMeta.addEditorTab('Display Styles', 'app/panels/graph/styleEditor.html');
  35. $scope.panelMeta.addExtendedMenuItem('Toggle legend', '', 'toggleLegend()');
  36. // Set and populate defaults
  37. var _d = {
  38. // datasource name, null = default datasource
  39. datasource: null,
  40. // sets client side (flot) or native graphite png renderer (png)
  41. renderer: 'flot',
  42. // Show/hide the x-axis
  43. 'x-axis' : true,
  44. // Show/hide y-axis
  45. 'y-axis' : true,
  46. // y axis formats, [left axis,right axis]
  47. y_formats : ['short', 'short'],
  48. // grid options
  49. grid : {
  50. leftMax: null,
  51. rightMax: null,
  52. leftMin: null,
  53. rightMin: null,
  54. threshold1: null,
  55. threshold2: null,
  56. threshold1Color: 'rgba(216, 200, 27, 0.27)',
  57. threshold2Color: 'rgba(234, 112, 112, 0.22)'
  58. },
  59. // show/hide lines
  60. lines : true,
  61. // fill factor
  62. fill : 0,
  63. // line width in pixels
  64. linewidth : 1,
  65. // show hide points
  66. points : false,
  67. // point radius in pixels
  68. pointradius : 5,
  69. // show hide bars
  70. bars : false,
  71. // enable/disable stacking
  72. stack : false,
  73. // stack percentage mode
  74. percentage : false,
  75. // legend options
  76. legend: {
  77. show: true, // disable/enable legend
  78. values: false, // disable/enable legend values
  79. min: false,
  80. max: false,
  81. current: false,
  82. total: false,
  83. avg: false
  84. },
  85. // how null points should be handled
  86. nullPointMode : 'connected',
  87. // staircase line mode
  88. steppedLine: false,
  89. // tooltip options
  90. tooltip : {
  91. value_type: 'cumulative',
  92. shared: false,
  93. },
  94. // metric queries
  95. targets: [{}],
  96. // series color overrides
  97. aliasColors: {},
  98. // other style overrides
  99. seriesOverrides: [],
  100. };
  101. _.defaults($scope.panel,_d);
  102. _.defaults($scope.panel.tooltip, _d.tooltip);
  103. _.defaults($scope.panel.annotate, _d.annotate);
  104. _.defaults($scope.panel.grid, _d.grid);
  105. _.defaults($scope.panel.legend, _d.legend);
  106. $scope.hiddenSeries = {};
  107. $scope.seriesList = [];
  108. $scope.updateTimeRange = function () {
  109. $scope.range = timeSrv.timeRange();
  110. $scope.rangeUnparsed = timeSrv.timeRange(false);
  111. if ($scope.panel.maxDataPoints) {
  112. $scope.resolution = $scope.panel.maxDataPoints;
  113. }
  114. else {
  115. $scope.resolution = Math.ceil($(window).width() * ($scope.panel.span / 12));
  116. }
  117. $scope.interval = kbn.calculateInterval($scope.range, $scope.resolution, $scope.panel.interval);
  118. };
  119. $scope.get_data = function() {
  120. $scope.updateTimeRange();
  121. var metricsQuery = {
  122. range: $scope.rangeUnparsed,
  123. interval: $scope.interval,
  124. targets: $scope.panel.targets,
  125. format: $scope.panel.renderer === 'png' ? 'png' : 'json',
  126. maxDataPoints: $scope.resolution,
  127. cacheTimeout: $scope.panel.cacheTimeout
  128. };
  129. $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed, $scope.dashboard);
  130. return $scope.datasource.query(metricsQuery)
  131. .then($scope.dataHandler)
  132. .then(null, function(err) {
  133. $scope.panelMeta.loading = false;
  134. $scope.panelMeta.error = err.message || "Timeseries data request error";
  135. $scope.inspector.error = err;
  136. $scope.seriesList = [];
  137. $scope.render([]);
  138. });
  139. };
  140. $scope.dataHandler = function(results) {
  141. $scope.panelMeta.loading = false;
  142. // png renderer returns just a url
  143. if (_.isString(results)) {
  144. $scope.render(results);
  145. return;
  146. }
  147. $scope.datapointsWarning = false;
  148. $scope.datapointsCount = 0;
  149. $scope.datapointsOutside = false;
  150. $scope.seriesList = _.map(results.data, $scope.seriesHandler);
  151. $scope.datapointsWarning = $scope.datapointsCount === 0 || $scope.datapointsOutside;
  152. $scope.annotationsPromise
  153. .then(function(annotations) {
  154. $scope.seriesList.annotations = annotations;
  155. $scope.render($scope.seriesList);
  156. }, function() {
  157. $scope.render($scope.seriesList);
  158. });
  159. };
  160. $scope.seriesHandler = function(seriesData, index) {
  161. var datapoints = seriesData.datapoints;
  162. var alias = seriesData.target;
  163. var color = $scope.panel.aliasColors[alias] || $rootScope.colors[index];
  164. var series = new TimeSeries({
  165. datapoints: datapoints,
  166. alias: alias,
  167. color: color,
  168. });
  169. if (datapoints && datapoints.length > 0) {
  170. var last = moment.utc(datapoints[datapoints.length - 1][1]);
  171. var from = moment.utc($scope.range.from);
  172. if (last - from < -10000) {
  173. $scope.datapointsOutside = true;
  174. }
  175. $scope.datapointsCount += datapoints.length;
  176. }
  177. return series;
  178. };
  179. $scope.render = function(data) {
  180. $scope.$broadcast('render', data);
  181. };
  182. $scope.changeSeriesColor = function(series, color) {
  183. series.color = color;
  184. $scope.panel.aliasColors[series.alias] = series.color;
  185. $scope.render();
  186. };
  187. $scope.toggleSeries = function(serie, event) {
  188. if (event.ctrlKey || event.metaKey || event.shiftKey) {
  189. if ($scope.hiddenSeries[serie.alias]) {
  190. delete $scope.hiddenSeries[serie.alias];
  191. }
  192. else {
  193. $scope.hiddenSeries[serie.alias] = true;
  194. }
  195. } else {
  196. $scope.toggleSeriesExclusiveMode(serie);
  197. }
  198. $scope.render();
  199. };
  200. $scope.toggleSeriesExclusiveMode = function(serie) {
  201. var hidden = $scope.hiddenSeries;
  202. if (hidden[serie.alias]) {
  203. delete hidden[serie.alias];
  204. }
  205. // check if every other series is hidden
  206. var alreadyExclusive = _.every($scope.seriesList, function(value) {
  207. if (value.alias === serie.alias) {
  208. return true;
  209. }
  210. return hidden[value.alias];
  211. });
  212. if (alreadyExclusive) {
  213. // remove all hidden series
  214. _.each($scope.seriesList, function(value) {
  215. delete $scope.hiddenSeries[value.alias];
  216. });
  217. }
  218. else {
  219. // hide all but this serie
  220. _.each($scope.seriesList, function(value) {
  221. if (value.alias === serie.alias) {
  222. return;
  223. }
  224. $scope.hiddenSeries[value.alias] = true;
  225. });
  226. }
  227. };
  228. $scope.toggleYAxis = function(info) {
  229. var override = _.findWhere($scope.panel.seriesOverrides, { alias: info.alias });
  230. if (!override) {
  231. override = { alias: info.alias };
  232. $scope.panel.seriesOverrides.push(override);
  233. }
  234. override.yaxis = info.yaxis === 2 ? 1 : 2;
  235. $scope.render();
  236. };
  237. $scope.toggleGridMinMax = function(key) {
  238. $scope.panel.grid[key] = _.toggle($scope.panel.grid[key], null, 0);
  239. $scope.render();
  240. };
  241. $scope.addSeriesOverride = function(override) {
  242. $scope.panel.seriesOverrides.push(override || {});
  243. };
  244. $scope.removeSeriesOverride = function(override) {
  245. $scope.panel.seriesOverrides = _.without($scope.panel.seriesOverrides, override);
  246. $scope.render();
  247. };
  248. // Called from panel menu
  249. $scope.toggleLegend = function() {
  250. $scope.panel.legend.show = !$scope.panel.legend.show;
  251. $scope.get_data();
  252. };
  253. panelSrv.init($scope);
  254. });
  255. });