AngularJS集成HighCharts动态绘制CPU和内存变化曲线

来自:陈文管的博客 2020-06-22

调研了下动态曲线绘制的开源项目, HighCharts 应用在AngularJS上相对容易使用和集成,来看下在AngularJS上集成HighCharts的StockChart图表,实现动态绘制Android内存和CPU的变化曲线。

一、实现效果

先看下HightCharts CPU和内存图表显示实现效果,图表左上角可以选择显示的区间范围,1分钟、5分钟或显示全部,截图显示的是全部数据,底部栏可以拖动查看不同区间数据。

二、HighCharts集成

先到 HighCharts下载中心 下载对应的资源,这边下载的是Highcharts-Stock-8.1.2,Highcharts-Stock下载之后就不用下载Highcharts资源,Highcharts-Stock里面已经包含,也可以直接使用CDN 文件而不用下载:

// Highcharts
<script src="//cdn.highcharts.com.cn/highcharts/8.1.2/highcharts.js"></script>
// Highcharts Stock
<script src="//cdn.highcharts.com.cn/highstock/8.1.2/highstock.js"></script>
// Highcharts Maps
<script src="//cdn.highcharts.com.cn/highmaps/8.1.2/highmaps.js"></script>
// Highcharts Gantt
<script src="//cdn.highcharts.com.cn/gantt/8.1.2/highcharts-gantt.js"></script>

还可以通过 NPM 安装:

npm install highcharts --save

或者通过 Bower 安装:

bower install highcharts --save

在Highcharts-Stock-8.1.2下载完成之后,把Highcharts-Stock-8.1.2/code/highstock.js文件拷贝放到工程目录下,添加highstock.js引用声明,之后就可以在工程里面使用Highcharts:

script(src='*****/*****/highstock.js')

三、Highcharts-Stock绘制CPU曲线

直接上代码,先看下CPU图表的参数初始化:

前端界面:

div(id='cpu')

JS文件,renderTo参数就是指定前端界面展示的控件id :

var chartCpuOptions = {
  chart: {
    renderTo: 'cpu'
  },
  xAxis: [{
    // currentDateIndicator: true,
    min: new Date().getTime(),
    max: new Date().getTime() + 24 * 60 * 60 * 1000
  }],
  rangeSelector: {
    buttons: [{
      count: 1,
      type: 'minute',
      text: '1M'
    }, {
      count: 5,
      type: 'minute',
      text: '5M'
    }, {
      type: 'all',
      text: 'All'
    }],
    inputEnabled: false,
    selected: 0
  },
  exporting: {
    enabled: true
  },
  title: {
    text: 'CPU'
  },
  series: [
    {
      name: 'Process CPU',
      data: []
    },
    {
      name: 'System CPU',
      data: []
    }
  ]
};

var chartCpu = new Highcharts.StockChart(chartCpuOptions);

xAxis图表横坐标轴参数一定要初始化,否则会报如下异常,min和max就是指定横坐标轴的最小值和最大值,xAxis的参数说明可以参考 HighCharts API xAxis参数文档 :

Error: <rect> attribute x: Expected length, "NaN".

rangeSelector 就是图表范围选择器,这边配置1分钟、5分钟和全部显示三个类型,buttons的type参数取值可以是:”millisecond”, “second”, “minute”, “hour”, “day”, “week”, “month”, “ytd”, “all”。

exporting.enabled 是否启用导出图表按钮,默认是true,但配置了之后竟然不会显示。

title.text 配置图表显示的标题,也可以调整显示的位置。

series 配置图表显示的数据列,name参数就是鼠标移动到图表曲线的时候显示的数据列悬浮提示内容。

四、Highcharts-Stock绘制内存曲线

内存曲线图表参数的初始化也类似,前端界面:

div(id='memory')

JS文件:

var chartMemoryOptions = {
  chart: {
    renderTo: 'memory'
  },
  xAxis: {
    // currentDateIndicator: true,
    min: new Date().getTime(),
    max: new Date().getTime() + 24 * 60 * 60 * 1000
  },
  rangeSelector: {
    buttons: [{
      count: 1,
      type: 'minute',
      text: '1M'
    }, {
      count: 5,
      type: 'minute',
      text: '5M'
    }, {
      type: 'all',
      text: 'All'
    }],
    inputEnabled: false,
    selected: 0
  },
  exporting: {
    enabled: true
  },
  title: {
    text: 'Memory'
  },
  series: [
    {
      name: 'Total',
      data: []
    },
    {
      name: 'Native',
      data: []
    },
    {
      name: 'Dalvik',
      data: []
    },
    {
      name: 'Sommap',
      data: []
    }
  ]
};

var chartMemory = new Highcharts.StockChart(chartMemoryOptions);

五、 Highcharts 图表数据刷新

1. CPU数据的刷新显示

cpulist和memorylist都是JSON结构的数据源:

//每次都设置最新的数据来更新图表
var cpuSize = cpulist.length;
var cpuTimeStamp = new Date(parseFloat(cpulist[cpuSize - 1].time)).getTime();
chartCpu.series[0].addPoint([cpuTimeStamp, parseFloat(cpulist[cpuSize - 1].processRate)]);
chartCpu.series[1].addPoint([cpuTimeStamp, parseFloat(cpulist[cpuSize - 1].systemRate)]);
//更新一次X轴的最小值和最大值,避免和初始化数据设置不一致导致图表刷新显示问题。
var hasInitCPUxAxis = false;
if(!hasInitCPUxAxis){
  hasInitCPUxAxis = true;
  chartCpu.xAxis[0].setExtremes(cpuTimeStamp - 60 * 1000, cpuTimeStamp);
}

2. Memory数据的刷新显示

var memSize = memorylist.length;
var memTimeStamp = new Date(parseFloat(memorylist[memSize - 1].time)).getTime();
chartMemory.series[0].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].totalPss) / 1024).toFixed(2))]);
chartMemory.series[1].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].nativePss) / 1024).toFixed(2))]);
chartMemory.series[2].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].dalvikPss) / 1024).toFixed(2))]);
chartMemory.series[3].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].soMmapPss) / 1024).toFixed(2))]);
var hasInitMemoryXAxis = false;
if(!hasInitMemoryXAxis){
  hasInitMemoryXAxis = true;
  chartMemory.xAxis[0].setExtremes(memTimeStamp - 60 * 1000, memTimeStamp);
}

3. 遇到的问题

这边数据更新要注意下, 不能用 chartCpuOptions.series [ 0 ] .data.push ()的方式 ,而要用 chartMemory.series [0 ] .addPoint ([ x , y ]) 才有效。 使用不当会输出如下异常:

Uncaught TypeError: d[w].destroyElements is not a function
    at n.generatePoints (highstock.js:5899)
    at n.e.generatePoints (highstock.js:9605)
    at n.translate (highstock.js:5939)
    at n.redraw (highstock.js:6243)
    at highstock.js:4731
    at Array.forEach (<anonymous>)
    at k.Chart.redraw (highstock.js:4730)
    at k.Chart.setSize (highstock.js:4937)
    at highstock.js:4904

另外注意添加的数据点参数不能是String类型的数据,最好加下parseInt或parseFloat的转化,控制台输出的异常提示如下:

Uncaught Error: Highcharts error #14: www.highcharts.com/errors/14/
    at k.Chart.e (highstock.js:98)
    at k.fireEvent (highstock.js:487)
    at k.error (highstock.js:110)
    at n.setData (highstock.js:5796)
    at n.g.updatedDataHandler (highstock.js:8923)
    at highstock.js:485
    at k.fireEvent (highstock.js:486)
    at highstock.js:4709
    at Array.forEach (<anonymous>)
    at k.Chart.redraw (highstock.js:4707)
    at n.addPoint (highstock.js:6658)
    at Socket.dataReceiveListener (performance-monitor.js:286)
    at Socket.Emitter.emit (index.js:131)
    at Socket.onevent (socket.js:263)
    at Socket.onpacket (socket.js:221)
    at Manager.eval (index.js:21)

关于error #14的说明如下:

String value sent to series.data, expected Number

This happens if using a string as a data point, for example in a setup like this:
series: [{
    data: ["3", "5", "1", "6"]
}]
Highcharts expects numerical data values.
The most common reason for this error this is that data is parsed from CSV or from a XML source, and the implementer forgot to run parseFloat on the parsed value.
Note: For performance reasons internal type casting is not performed, and only the first value is checked (since 2.3).

如果遇到其他问题可以到 HighCharts BBS论坛 上找答案。

六、参考资料

21个实用的Javascript数据图表插件

动态折线图

Highcharts 演示:实时刷新的曲线图

Angular Highcharts 教程

AntV 实时更新数据的折线图

highcharts + angularjs + live random data example

扩展阅读:

Android 性能监控之内存监控

Android 性能监控之CPU监控

Python 绘制Android CPU和内存增长曲线

转载请注明出处:陈文管的博客–  AngularJS集成HighCharts动态绘制CPU和内存变化曲线

扫码或搜索: 文呓

微信公众号  扫一扫关注