在衆多的工業控制系統領域經常會實時採集現場的溫度、壓力、扭矩等數據,這些數據對於監控人員進行現場態勢感知、進行將來趨勢預測具備重大指導價值。工程控制人員若是隻是閱讀海量的數據報表,對於現場整個態勢的掌控會十分困難,所以每每但願藉助一些圖表進行展現,其中趨勢圖是經常使用的實時數據展現方式之一。目前實現趨勢圖、曲線圖的工具不少也很成熟,一些是經過CS模式開發的,須要在工程控制人員操做的計算機上安裝相應軟件,這種方式有其特有的優點,可是有時也有不方便的地方。因而一些基於BS模式的展示方式就天然而然的被普遍應用起來。採用BS模式的展示方式工程控制人員能夠經過任何一臺能夠鏈接Web服務器的PC,經過經常使用的瀏覽器就能夠實時的查看當前環境現場的各類指標參數,其便利性是顯而易見的。下面我就介紹一下這種BS模式的實時數據展示曲線圖的方法。因爲本文的目的不是去實現一個現場環境可用的應用產品,所以只是針對涉及的技術進行講解,起一個指導做用,所以參考者請依據本身項目的實際須要對本文章涉及的代碼進行優化使用。
在經過BS系統實現趨勢圖、曲線圖的可選方案不少,本文主要經過Highcharts.com旗下的Highcharts API包來實現。因爲此包是經過JS腳本實現的,所以我的認爲相對來講適應面可能更好,固然經過諸如jFreeChart這類工具也是能夠實現的。
言歸正傳,咱們來說解如何經過Highcharts API實現趨勢圖在頁面上的呈現。
首先咱們須要簡單規劃一下咱們系統的架構。因爲實現實時數據的趨勢圖呈現,所以系統大體咱們能夠設計成三層:顯示層、邏輯層、數據源。以下圖所示:javascript
顯示層就是將數據呈現給工程控制人員的的展現頁面,這層主要由JSP、JS、CSS等文件構成,工程控制人員經過瀏覽器(諸如IE、火狐等)就能直接看到但願的曲線圖。顯示層只負責顯示,而顯示須要的數據是通過必定清洗、規格化的,顯示層拿到符合規格化要求的數據後,就能夠直接進行顯示,並響應和人的交互。數據的清洗、規格化工做都是在邏輯層中實現,邏輯層經過獲取的數據源信息,進行必要的數據邏輯轉換、數據清洗、數據規格化處理。數據源是一個複雜的重要的,它能夠是直接來自下位機的數據通信,也能夠是下位機將數據存儲在中間數據庫中,也能夠是一系列的數據文件。
在本文中只是模擬數據源,並非實現數據源的讀取。而且對應邏輯層的處理,也進行了忽略,這部份內容由於涉及具體的數據獲取、清洗、轉換、規格化,和具體工程項目的需求有較大關係,加之也不是本文規劃的中心,所以此部分代碼設計實現本文也不涉及。
顯示層的實現涉及到JSON、JQuery、Highcharts,咱們首先創建一個標準的Web應用(其實若是做爲例子,使用一個html文件也行,此處本人計劃後期會擴展本案例,實現後續一些諸如邏輯層的功能,所以創建了一個Web應用工程,有點多此一舉,還請見諒)。本人採用Netbeans IDE 7.2版本開發(如何用Netbeans IDE開發Web應用請參考本人其它文章),所以創建完成後的工程結構以下:html
要使用Highcharts,咱們須要導入highcharts.js文件。highcharts.js文件能夠從Highcharts官網得到(官網地址:http://www.highcharts.com/),從官網下載的壓縮文件中包含有咱們開發須要的highcharts.js文件外,還包含一些其它的文件,諸如例子文件等。
Highcharts是使用js來實現的,同時應用到了JQuery技術,所以還須要去得到最新版的JQuery包(官網地址:http://jquery.com/)。
以上兩塊準備好後,咱們將其加入新建的工程中,在工程文件中咱們規劃了一個放置全部js文件的地方,有一個js目錄,將highcharts.js、jquery-1.8.3.min.js文件都放置到此目錄下。你們看到個人工程中在js目錄下有一個modules子目錄,此子目錄下放置的exporting.js文件是無關緊要的,若是你們要使用將圖表導出等功能就須要使用此文件,所以須要導入工程中,不然徹底能夠不須要。
工程環境準備停當,咱們就能夠打開本工程的默認,也是惟一的一個jsp頁面,對此頁面作一個編寫修改。主要修改有以下幾個地方:
第一,在jsp頁面頭部引入highcharts.js、jquery-1.8.3.min.js文件,應用代碼參考以下:java
<scripttype="text/javascript"src="http://zhaowenbinmail.blog.163.com/blog/js/jquery-1.8.3.min.js"></script>jquery
<scriptsrc="http://zhaowenbinmail.blog.163.com/blog/js/highcharts.js"></script>數據庫
第二,在jsp頁面的Body體中加入一個div元素,highcharts將在這個div元素中繪製曲線圖。數組
<divid="container"style="min-width:100px; height:400px; margin:0auto"></div>瀏覽器
注意:咱們爲這個div指定了一個id值,這個id值未來對咱們頗有用,它是使highcharts知道在何處繪製圖表的根源。
第三,咱們須要在jsp頁面中加入咱們本身的js文件。這個文件用來實現特定的業務呈現邏輯。服務器
<scripttype="text/javascript"src="http://zhaowenbinmail.blog.163.com/blog/js/chart.js"></script>
如今我來說解一下我須要實現的業務的大致需求。我須要在頁面上顯示大橋表面一天24小時的溫度變化狀況(固然這些溫度值的變化本案例中都是經過隨機數來產生的)。在咱們的很軸方向上須要顯示從0點開始到晚上23點的時間刻度,而且要求固定就顯示0到23這24個刻度,在縱軸方向顯示橋面傳感器檢測到的本小時內溫度最大值,而後模擬時間推移顯示每小時的溫度變化曲線圖。
依據以上需求分析,在頁面呈現時,咱們就要去讀取本天從0點開始到當前時刻的數據,並將數據繪製顯示成曲線圖。所以咱們接下來就要編輯咱們本身的js文件——chart.js。在此文件中建立Highcharts對象,經過設置相關的屬性來影響曲線圖的呈現,使其知足咱們的需求要求。
咱們首先定義一個全局的圖表對象,咱們命名爲chart,同時示例化Highcharts對象,具體見下面代碼:架構
var chart;dom
$(function(){
$(document).ready(function(){
chart =newHighcharts.Chart({
chart:{
renderTo:'container',
type:'line',
marginRight:130,
marginBottom:80,
events:{
load:loadTime
}
},
title:{
text:'大橋採集數據',
x:-20
},
subtitle:{
text:'傳感器編號: 傳感器1',
x:-20
},
xAxis:{
title:{
enabled:true,
text:'時間(小時)'
},
max:23,
min:0,
tickPixelInterval:50
},
yAxis:{
title:{
text:'壓力 (℃)'
},
plotLines:[{
value:0,
width:1,
color:'#808080'
}]
},
tooltip:{
formatter:function(){
return'<b>'+this.series.name +'</b><br/>'+
this.x +': '+this.y +'℃';
}
},
legend:{
x:-50,
y:10,
enabled:true
},
exporting:{
enabled:false
},
plotOptions:{
line:{
gapSize:100
}
},
series:[{
name:'最大值',
data: getFirstData()
}]
});
});
});
在上面代碼中咱們實例化了一個Highcharts對象,並指定了此對象一些屬性。在定義中咱們能夠看到以下代碼:
chart:{
renderTo:'container',
type:'line',
marginRight:130,
marginBottom:80,
events:{
load:loadTime
}
}
在這段代碼中就知道了圖表須要繪製到的div元素,注意看上面紅色字體部分。這個地方的container就是jsp頁面上div的id值(你們能夠回過去查看一下我前面的代碼)。
其後指定了這個圖表的類型(見上面代碼中藍色字體部分)。這裏指定的Line類型,這就會繪製成曲線。
代碼中還指定了圖表的事件,目前只指定了一個load事件,在圖表裝載時將會調用執行load時間對應的函數。代碼後面的loadTime是我編寫的一個js函數,這個函數後面介紹。
經過title、subtitle指定圖表的標題、子標題,見下面代碼:
title:{
text:'大橋採集數據',
x:-20
},
subtitle:{
text:'傳感器編號: 傳感器1',
x:-20
},
經過改變這裏的設置能夠影響圖表上顯示的效果,顯示效果以下圖紅框選中部份內容。
經過xAxis、yAxis設定圖表中橫座標、縱座標的屬性。
xAxis:{
title:{
enabled:true,
text:'時間(小時)'
},
max:23,
min:0,
tickPixelInterval:50
},
yAxis:{
title:{
text:'壓力 (℃)'
},
plotLines:[{
value:0,
width:1,
color:'#808080'
}]
},
經過在xAxis、yAxis中設置title屬性控制橫座標、縱座標上的文字描述顯示。
在橫座標中有時咱們是須要指定座標上每一個座標點須要顯示的文字內容的,這時咱們須要使用到一個categories屬性,經過將一個數組值傳給categories屬性,這樣在橫座標上的每一個座標點就會按照指定的屬性內容顯示座標點信息。爲了實現如上目的,咱們須要再設置一個變量,並將這個變量賦予categories屬性。詳見下:
var x_arr=['0:00','1:00','2:00','3:00','4:00','5:00',
'6:00','7:00','8:00','9:00','10:00','11:00',
'12:00','13:00','14:00','15:00','16:00','17:00',
'18:00','19:00','20:00','21:00','22:00','23:00'];
經過上面代碼段咱們定義了一個變量,而後看看如何指定到chart對象中,請注意以下代碼端中紅色部門,就是較前面代碼端增長的地方:
xAxis:{
title:{
enabled:true,
text:'時間(小時)'
},
categories:x_arr,
max:23,
min:0,
tickPixelInterval:50
},
如今頁面上顯示的圖表效果以下圖所示:
橫座標上就是按照咱們在x_arr變量中設定的內容顯示的。若是設定的內容少於橫座標的座標點,則前面的座標點將按x_arr變量中設定內容顯示,後面的座標點將按默認的座標點信息顯示。
工程控制人員經過此曲線圖能夠知道每一個時間點的最高溫度狀況,可是具體某個點溫度是多少,工程人員但願經過將鼠標指向圖表中採集點就能夠展示出來。要實現這個功能,咱們須要在chart對象中指定tooltip屬性,見下面代碼:
tooltip:{
formatter:function(){
return'<b>'+this.series.name +'</b><br/>'+
this.x +': '+this.y +'℃';
}
},
配置上了這個屬性就能獲得下圖顯示的效果:
當鼠標移動到10點中的採集點時,屏幕上會當即顯示當時的溫度值。
針對有些應用場合,咱們在一個圖表中將展現多個線條表明不一樣的含義,這時就須要經過圖例來講明什麼顏色線條表明什麼含義,這種狀況下就須要啓用圖例說明,要啓用圖例說明就必須經過以下屬性進行配置:
egend:{
x:-50,
y:10,
enabled:true
},
只要使egend對象中的enabled屬性設置爲true就能夠。設置爲false,將不顯示圖例。
上面介紹的是圖例基本的屬性設置,如今咱們須要模擬一些大橋傳感器得到的數據,依據這些數據來顯示曲線圖。在這裏咱們首先要定義兩個js函數,這三個函數就是模擬獲取數據的,請看下面:
function loadTime(){
window.setTimeout(getData,TIMEOUT);
}
上面這個函數就是咱們在chart對象中設置events中的load事件指定的函數。在本函數中主要啓動了一個定時器,在超過指定時間計秒後,將調用函數getData。
function getFirstData(){
var data =[];
var y_mx=Math.round(Math.random()*10);
var i;
for(i =0; i <=0; i++){
data.push({
x: current_time,
y: y_mx
});
if(current_time<=23){
current_time++;
}
}
return data;
}
這個函數是用來模擬初始化chart對象時,得到的大橋橋面溫度值。其返回的是一個數組對象,數組中的每一個對象包含x、y屬性,這兩個屬性用來告訴chart對象x軸的某個座標上面的y值是多少,並在此處顯示一個座標點,同時將鏈接相鄰兩個座標點造成曲線。代碼中的current_time是一個全局變量,用來對當前採集顯示次數進行計數。
function getData(){
var current_x=x_arr[current_time];
//獲取最大值
var series_mx = chart.series[0];
var y_mx=Math.round(Math.random()*10);
series_mx.addPoint([current_time, y_mx],true,false);
current_time++;
if(current_time<=23){
window.setTimeout(getData,TIMEOUT);
}
}
上面這個函數其它部分都很簡單,關鍵的一個重點代碼是
series_mx.addPoint([current_time, y_mx],true,false);
這一段,這段代碼是在chart圖表中加入一個新的座標點。
截至到目前,所有的代碼就編寫完成,如今運行它,就能夠模擬出大橋橋面穩定24小時檢測狀況的實時趨勢圖。
其實使用highcharts來作實時趨勢圖簡單的處理仍是比較簡單的,更多的應用還能夠在這些基礎應用上進行拓展
<script type="text/javascript">
var x_arr = ['0:00', '1:00', '2:00', '3:00', '4:00', '5:00','6:00', '7:00', '8:00', '9:00', '10:00', '11:00','12:00', '13:00', '14:00', '15:00', '16:00', '17:00','18:00', '19:00', '20:00', '21:00', '22:00', '23:00'];
var chart;
var current_time = 0;
var TIMEOUT = 1000;
$(function () {
$(document).ready(function () {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'line',
marginRight: 130,
marginBottom: 80,
events: {
load: loadTime
}
},
title: {
text: '大橋採集數據',
x: -20
},
subtitle: {
text: '傳感器編號: 傳感器',
x: -20
},
xAxis: {
title: {
enabled: true,
text: '時間(小時)'
},
categories: x_arr,
max: 23,
min: 0,
tickPixelInterval: 50
},
yAxis: {
title: {
text: '壓力 (℃)'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
this.x + ': ' + this.y + '℃';
}
},
legend: {
x: -50,
y: 10,
enabled: true
},
exporting: {
enabled: false
},
plotOptions: {
line: {
gapSize: 100
}
},
series: [{
name: '最大值',
data: getFirstData()
}]
});
});
});
function loadTime() {
window.setTimeout(getData, TIMEOUT);
}
function getFirstData() {
var data = [];
var y_mx = Math.round(Math.random() * 10);
var i;
for (i = 0; i <= 3; i++) {
data.push({
x: current_time,
y: y_mx
});
if (current_time <= 23) {
current_time++;
}
}
return data;
}
function getData() {
var current_x = x_arr[current_time];
//獲取最大值
var series_mx = chart.series[0];
var y_mx = Math.round(Math.random() * 10);
series_mx.addPoint([current_time, y_mx], true, false);
current_time++;
if (current_time <= 23) {
window.setTimeout(getData, TIMEOUT);
}
}
</script>