На код бы взглянутьxxc писал(а): У меня работает автоматическое обновление графиков - правда, пришлось менять код выдачи JSON.
Смысл в чем: в выдаче JSON добавлен последний ID для отобранных данных. А в скрипте на странице после получения данных мы этот ID запоминаем. После чего по таймеру периодически проверяем - не появились ли данные для графика с ID большим, чем мы уже получили. Если данные есть - добавляем их к графику.
[Модификация] Строим графики Highstock
-
- Сообщения: 289
- Зарегистрирован: Вс сен 16, 2012 9:39 am
- Благодарил (а): 0
- Поблагодарили: 1 раз
Re: Строим графики Highstock
Re: Строим графики Highstock
На "чистоту" кода не претендую - писался в торопях.Urbas81 писал(а): На код бы взглянуть
jsonp.phpПоказать
Код: Выделить всё
<?
/*
* Автор Казаков Сергей
* на форуме под ником Bagir
* благодарность denis на примеры
* ver 1.3
*/
include_once("./config.php");
include_once("./lib/loader.php");
$session = new session("prj");
$db = new mysql(DB_HOST, '', DB_USER, DB_PASSWORD, DB_NAME); // connecting to database
include_once("./load_settings.php");
if (!headers_sent()) {
header("HTTP/1.0: 200 OK\n");
header('Content-Type: text/html; charset=utf-8');
}
// Получить имя и проверить
if (isset($params['name'])) {
$name = $params['name'];
} else
returm;
$date_start = isset($params['start']) ? $params['start'] : null;
$date_end = isset($params['end']) ? $params['end'] : null;
$last_id = isset($params['lastid']) ? (int)$params['lastid'] : 0;
// Разбить на объект и свойство (возможен вариант поиска в pvalues сразу по PROPERTY_NAME=$name не разбирая на объект и свойство)
$name = explode('.', $name);
// Получить объект по имени
$obj = getObject($name[0]);
// Получить id свойства
$prop_id = $obj->getPropertyByName($name[1], $obj->class_id, $obj->id);
$pvalue = SQLSelectOne("SELECT * FROM pvalues WHERE PROPERTY_ID='" . $prop_id . "' AND OBJECT_ID='" . $obj->id . "'");
$sql = "SELECT ID, UNIX_TIMESTAMP(ADDED) as ADDED, VALUE FROM phistory WHERE VALUE_ID='$pvalue[ID] '";
if (!is_null($date_start)){
$sql .= " and ADDED >= '$date_start'";
}
if (!is_null($date_end)){
$sql .= " and cast(ADDED as DATE) <= '$date_end'";
}
if ($last_id>0){
$sql .= " and id>$last_id";
}
$sql .= " ORDER BY ADDED ";
$arr_s = SQLSelect($sql);
$data = [];
$date_prev = null;
header('Content-Type: application/json');
$breaks = [];
$DTPrev = new DateTime();
$DTCurrent = new DateTime();
$prev = null;
foreach ($arr_s as $row){
$added = $row['ADDED'];
$t = (float)($row['VALUE']);
if (!is_null($prev)){
$diff = $added - $prev;
if ($diff > 60*60) { // 1 hour
$breaks[] = ["start"=>$prev, "end"=>$row['ADDED'], 'size'=>$diff];
}
$data[] = [$added*1000, $t];
}
$prev = $row['ADDED'];
$last_id = $row['ID'];
}
echo(json_encode([
'last_id'=>$last_id,
'data'=>$data,
'breaks'=>$breaks,
'description'=>$obj->description
]));
$session->save();
$db->Disconnect(); // closing database connection
?>
Код страницы с графикомПоказать
Код: Выделить всё
<script type="text/javascript" src="/js/jquery.js"></script>
<script src="/highcharts/js/highstock.js"></script>
<script src="/highcharts/js/modules/broken-axis.js"></script>
<script type="text/javascript" language="javascript">
var url = '/jsonp.php/?name=Sensor1.temp';
var last_id = 0;
$(function () {
$.getJSON(url, function (data) {
var breaks = [];
$.each(data.breaks, function (idx, e) {
breaks.push({
from: e.start,
to: e.end,
breakSize: e.size
});
});
// console.log(breaks);
var chart = $("#chart-container").highcharts('StockChart', {
chart: {
zoomtype: 'x',
events: {
selection: function (e) {
},
load: function (e) {
}
}
},
title: {text: data.description},
xAxis: {
type: 'datetime',
},
yAxis: {title: {text: 'Температура, °C'}},
rangeSelector: {
buttons: [
{type: 'all', count: 1, text: 'Все'},
{type: 'hour', count: 1, text: 'Час'},
{type: 'day', count: 1, text: 'День'},
{type: 'month', count: 1, text: 'Мес'},
{type: 'year', count: 1, text: 'Год'}],
selected: 0,
inputEnabled: false
},
series: [{
type: 'area',
name: 'Temperature',
tooltip: {
valueDecimals: 1
},
gapSize: 0,
data: data.data
}
]
});
last_id = data.last_id;
setInterval(function () {
updateChart();
}, 30000);
});
});
function updateChart() {
$.ajax({
dataType: 'json',
method: 'POST',
url: url + '&lastid=' + last_id,
success: function (data) {
var ser = $("#chart-container").highcharts().series[0];
last_id = data.last_id;
$.each(data.data, function (idx, e) {
ser.addPoint(e);
})
}
});
}
</script>
<div id="chart-container" style="height: 500px; min-width: 500px"></div>
-
- Сообщения: 1115
- Зарегистрирован: Вс июн 14, 2015 11:08 am
- Благодарил (а): 85 раз
- Поблагодарили: 342 раза
Re: Строим графики Highstock
Вижу, что у всех код работает, но не понимаю как.
У меня $.getJSON уходит в ошибку parseerror, что и логично, т.к. то, что возвращает сценарий, не совсем валидный JSON
Кто-то сталкивался с таким? Может есть модифицированный сценарий?
У меня $.getJSON уходит в ошибку parseerror, что и логично, т.к. то, что возвращает сценарий, не совсем валидный JSON
Код: Выделить всё
jQuery182043176942504942417_1438627650102([[1437944711000,33.40],[1437979275000,30.79],[1437979879000,30.70],[1437980485000,30.79],[1437981089000,31],[1437981696000,31],[1437982300000,30.90],[1437982904000,30.79],[1437983511000,30.70],[1437984115000,30.60],[1437984719000,30.60],[1438013955000,32.90],[1438014560000,32.59],[1438015165000,32.09],[1438015770000,31.90],[1438016375000,31.70],[1438016981000,31.50],[1438017586000,31.29],[1438018191000,31.29],[1438018795000,31.20],[1438020611000,31.20],[1438021219000,31.10]....
Re: Строим графики Highstock
Выдавало такую ж ошибку, пока я не стал забирать JSON не через $.getJSON, а так:olehs писал(а): У меня $.getJSON уходит в ошибку parseerror, что и логично, т.к. то, что возвращает сценарий, не совсем валидный JSON
Код: Выделить всё
$.ajax({
dataType: 'json',
method: 'POST',
url: url,
success: function (data) {
......
})
}
});
-
- Сообщения: 1115
- Зарегистрирован: Вс июн 14, 2015 11:08 am
- Благодарил (а): 85 раз
- Поблагодарили: 342 раза
Re: Строим графики Highstock
Простите, вопрос снимается. Все-таки это была ошибка в данных истории.olehs писал(а):Вижу, что у всех код работает, но не понимаю как.
У меня $.getJSON уходит в ошибку parseerror, что и логично, т.к. то, что возвращает сценарий, не совсем валидный JSONКто-то сталкивался с таким? Может есть модифицированный сценарий?Код: Выделить всё
jQuery182043176942504942417_1438627650102([[1437944711000,33.40],[1437979275000,30.79],[1437979879000,30.70],[1437980485000,30.79],[1437981089000,31],[1437981696000,31],[1437982300000,30.90],[1437982904000,30.79],[1437983511000,30.70],[1437984115000,30.60],[1437984719000,30.60],[1438013955000,32.90],[1438014560000,32.59],[1438015165000,32.09],[1438015770000,31.90],[1438016375000,31.70],[1438016981000,31.50],[1438017586000,31.29],[1438018191000,31.29],[1438018795000,31.20],[1438020611000,31.20],[1438021219000,31.10]....
-
- Сообщения: 289
- Зарегистрирован: Вс сен 16, 2012 9:39 am
- Благодарил (а): 0
- Поблагодарили: 1 раз
Re: Строим графики Highstock
Спасибо. Сначала подумал что речь идет про автоматическое обновление там, где выведено несколько графиков на одном поле.xxc писал(а):На "чистоту" кода не претендую - писался в торопях.Urbas81 писал(а): На код бы взглянуть
Обновление работает. Но вот с разрывами в графике - например датчик завис или нет связи - пока не разобрался - не хватает времени.
-
- Сообщения: 60
- Зарегистрирован: Чт мар 19, 2015 10:27 pm
- Благодарил (а): 8 раз
- Поблагодарили: 22 раза
Re: Строим графики Highstock
Только недавно натыкался на эту библиотеку с графиками и хотел их прикрутить к умному дому, а оказалось все уже давно прикручено,, работает и есть готовые примеры, это просто замечательно.
Огромнейшее спасибо за проделанную работу, все очень круто.
И, пожалуй, внесу свои 5 копеек.
Мне очень понравилась возможность в некоторых графиках отображать сразу две и более осей, это может быть полезно, когда мы хотим отобразить на одном графике, например, температуру и влажность в помещении. Приведу готовый пример своего графика, уверен, желающие разберутся.
В этом примере отображены четыре графика и две оси, причем графики температур на нечетных позициях, а графики влажности на четных в описании, это сделано для того, чтобы не мудрить в SeriesOptions с индивидуальным заданием оси yaxis, там просто вычисляется остаток от деления.
В классе осей y есть параметр "opposite: false/true" он как раз и показывает с какой стороны отображать ось. По умолчанию в библиотеке он false, он в MD true, поэтому сразу пример не заработал. opposite: true можно не писать, а false обязательно, но лучше писать и там и там, тогда при обновлениях точно ничего не слетит.
Огромнейшее спасибо за проделанную работу, все очень круто.
И, пожалуй, внесу свои 5 копеек.
Мне очень понравилась возможность в некоторых графиках отображать сразу две и более осей, это может быть полезно, когда мы хотим отобразить на одном графике, например, температуру и влажность в помещении. Приведу готовый пример своего графика, уверен, желающие разберутся.
В этом примере отображены четыре графика и две оси, причем графики температур на нечетных позициях, а графики влажности на четных в описании, это сделано для того, чтобы не мудрить в SeriesOptions с индивидуальным заданием оси yaxis, там просто вычисляется остаток от деления.
В классе осей y есть параметр "opposite: false/true" он как раз и показывает с какой стороны отображать ось. По умолчанию в библиотеке он false, он в MD true, поэтому сразу пример не заработал. opposite: true можно не писать, а false обязательно, но лучше писать и там и там, тогда при обновлениях точно ничего не слетит.
СпойлерПоказать
<script type="text/javascript" src="../../highcharts/js/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="../../highcharts/js/highstock.js"></script>
<script type="text/javascript">
$(function () {
var seriesOptions = [],
obsss = 0,
seriesCounter = 0,
timeback = 0,
names = ['температура в зале','влажность в зале','температура на улице','влажность на улице'],
sensornames = ['LivingRoom.Temperature',
'LivingRoom.Humidity',
'Weather.Temperature',
'Weather.Humidity'
];
Highcharts.setOptions({
lang: {
months: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль',
'Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
shortMonths: ['Янв','Фев','Мар','Апр','Май','Июн','Июл',
'Авг','Сен','Окт','Ноя','Дек'],
weekdays: ['Вск','Пнд','Втр','Срд','Чтв','Птн','Сбт'],
rangeSelectorZoom: 'Маcштаб',
rangeSelectorFrom: 'От',
rangeSelectorTo: 'До',
thousandsSep: ' '
},
global: {
useUTC: false
}
});
// create the chart when all data is loaded
createChart = function () {
$('#container').highcharts('StockChart', {
rangeSelector: {
buttons: [{ type: 'hour', count: 1, text: '1h' },
{ type: 'day', count: 1, text: '1d' },
{ type: 'day', count: 2, text: '2d' },
{ type: 'week', count: 1, text: '1w' },
{ type: 'month', count: 1, text: '1m' },
{ type: 'month', count: 6, text: '6m' },
{ type: 'year', count: 1, text: '1y' },
{ type: 'all', text: 'All' }],
selected: 1 // Какая кнопка выбрана по умолчанию
},
title: { text : 'График температуры и влажности'},
legend: { enabled : true,
layout : 'horizontal',
align : 'center',
verticalAlign : 'top',
borderWidth: 0,
x : 0,
y : 20 },
xAxis : {
minRange: 3600 * 1000, // one hour
crosshair: true
},
yAxis: [{ // Primary yAxis
labels: {
format: '{value}°C',
style: {
color: Highcharts.getOptions().colors[1]
}
},
title: {
text: 'Temperature',
style: {
color: Highcharts.getOptions().colors[1]
}
},
opposite: false
}, { // Secondary yAxis
title: {
text: 'Humidity',
style: {
color: Highcharts.getOptions().colors[0]
}
},
labels: {
format: '{value} %',
style: {
color: Highcharts.getOptions().colors[0]
}
},
opposite: true
}],
$.each(names, function (i, name) {
$.getJSON('/objects/?script=jconhs&name='+sensornames+'&callback=?', function (data) {
seriesOptions = {
name: name,
data: data,
yAxis: (i%2),
type: 'spline'
};
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
});
</script>
<div id="container" style="height: 600px; min-width: 500px"></div>
<script type="text/javascript" src="../../highcharts/js/highstock.js"></script>
<script type="text/javascript">
$(function () {
var seriesOptions = [],
obsss = 0,
seriesCounter = 0,
timeback = 0,
names = ['температура в зале','влажность в зале','температура на улице','влажность на улице'],
sensornames = ['LivingRoom.Temperature',
'LivingRoom.Humidity',
'Weather.Temperature',
'Weather.Humidity'
];
Highcharts.setOptions({
lang: {
months: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль',
'Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
shortMonths: ['Янв','Фев','Мар','Апр','Май','Июн','Июл',
'Авг','Сен','Окт','Ноя','Дек'],
weekdays: ['Вск','Пнд','Втр','Срд','Чтв','Птн','Сбт'],
rangeSelectorZoom: 'Маcштаб',
rangeSelectorFrom: 'От',
rangeSelectorTo: 'До',
thousandsSep: ' '
},
global: {
useUTC: false
}
});
// create the chart when all data is loaded
createChart = function () {
$('#container').highcharts('StockChart', {
rangeSelector: {
buttons: [{ type: 'hour', count: 1, text: '1h' },
{ type: 'day', count: 1, text: '1d' },
{ type: 'day', count: 2, text: '2d' },
{ type: 'week', count: 1, text: '1w' },
{ type: 'month', count: 1, text: '1m' },
{ type: 'month', count: 6, text: '6m' },
{ type: 'year', count: 1, text: '1y' },
{ type: 'all', text: 'All' }],
selected: 1 // Какая кнопка выбрана по умолчанию
},
title: { text : 'График температуры и влажности'},
legend: { enabled : true,
layout : 'horizontal',
align : 'center',
verticalAlign : 'top',
borderWidth: 0,
x : 0,
y : 20 },
xAxis : {
minRange: 3600 * 1000, // one hour
crosshair: true
},
yAxis: [{ // Primary yAxis
labels: {
format: '{value}°C',
style: {
color: Highcharts.getOptions().colors[1]
}
},
title: {
text: 'Temperature',
style: {
color: Highcharts.getOptions().colors[1]
}
},
opposite: false
}, { // Secondary yAxis
title: {
text: 'Humidity',
style: {
color: Highcharts.getOptions().colors[0]
}
},
labels: {
format: '{value} %',
style: {
color: Highcharts.getOptions().colors[0]
}
},
opposite: true
}],
$.each(names, function (i, name) {
$.getJSON('/objects/?script=jconhs&name='+sensornames+'&callback=?', function (data) {
seriesOptions = {
name: name,
data: data,
yAxis: (i%2),
type: 'spline'
};
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
});
</script>
<div id="container" style="height: 600px; min-width: 500px"></div>
- За это сообщение автора Fav0rit поблагодарил:
- Ko/|xo3HUk (Пн ноя 28, 2016 10:12 pm)
- Рейтинг: 1.16%
-
- Сообщения: 60
- Зарегистрирован: Чт мар 19, 2015 10:27 pm
- Благодарил (а): 8 раз
- Поблагодарили: 22 раза
Re: Строим графики Highstock
Вот проблемка есть, которую я все никак не могу победить изменением параметров отрисовки, видимо как-то связано именно с JSON, с которым я работать не умею. Данные с датчиков приходят с различными интервалами, использую MySensor, система часто опрашивает гейт, поэтому повторные сведения не пишет, поэтому на графиках видны только параметры в момент изменения значения, из-за чего график представляет из себя гармошку, а ось времени, как её ни настраивай, нифига не линейная, что жутко раздражает и убивает всю идею. Примеры графиков с сайта с конфигами для непериодических данных работать не хотят, в отрисовке ничего не меняется, как я ни пытаюсь...
-
- Сообщения: 60
- Зарегистрирован: Чт мар 19, 2015 10:27 pm
- Благодарил (а): 8 раз
- Поблагодарили: 22 раза
Re: Строим графики Highstock
Более менее разобрался я с этими графиками, однако не уверен, что сказанное мною ниже на 100% верно, если кто-то исправит, я буду только рад.
В библиотеке есть несколько типов графиков, а именно Highcharts, Highstock, Highmaps. Последний тип нас не интересует, там только карты, а вот первые два очень даже ничего, как функционально, так и внешне.
Самый удобный и симпатичный из этих трех Highstock, однако с его использованием есть некоторые заморочки.
Первая и самая главная из них - кривое представление не периодичных данных. Например, данные с температурного графика в комнате пишутся в лог каждую минуту, а веб-переменная с температурой на улице обновляется раз в час. В итоге может получиться такая неприятность, что при частом изменении температуры в комнате час на графике будет сильно растянут, а в случае отсутствия изменений в параметрах длинный отрезок времени может отображаться как очень короткий. При отрисовке нескольких графиков на одном поле это особенно заметно. Какими-то параметрами и функциями самой библиотеки лично мне решить эту проблему не удалось.
Стоит заметить, что в модуле Highcrarts те же самые данные отображаются абсолютно правильно, шкала времени строго линейна, но в нем не так удобно реализовано масштабирование и выборка данных за отдельный период времени.
Для использования модуля Highstock предлагаю следующий костыль, как одно из возможных решений.
Чтобы не периодичные данные данные стали периодичными, можно создать отдельный класс, например, Graphics создать в нем объекты с названиями вида GraphsLivingroom, GraphsKitchen, GraphsPowermeter1 и, тому подобными. Для класса создать полный набор всевозможных логгируемых свойств. В списке сценариев создать сценарий заполнения соответствующих свойств объектов класса Graphs из свойств реальных объектов и ежеминутно запускать этот скрипт. В итоге, все свойства объектов нового класса будут заполняться одновременно с равными интервалами, без разрывов и.т.п. График Highstock будет строиться правильно. Минусом являются дополнительные телодвижения и увеличение базы данных, потому что в ней будут храниться ежеминутные (хотя можно обновлять значения и каждые 5, 10 минут и.т.д.) записи о данных, которые могут обновляться раз в час, плюс само по себе такое дублирование не самый красивый выход.
Если кто-то покажет нормально работающий Highstock с правильной отрисовкой данных различной периодичности, я буду просто счастлив.
Пока выход либо использование Highcharts, либо костыль в Highstock.
UPD: оказывается, костыль не работает. Ни в какую не хочет писать в историю одинаковые значения каждую минуту. Я в полной растерянности...
В библиотеке есть несколько типов графиков, а именно Highcharts, Highstock, Highmaps. Последний тип нас не интересует, там только карты, а вот первые два очень даже ничего, как функционально, так и внешне.
Самый удобный и симпатичный из этих трех Highstock, однако с его использованием есть некоторые заморочки.
Первая и самая главная из них - кривое представление не периодичных данных. Например, данные с температурного графика в комнате пишутся в лог каждую минуту, а веб-переменная с температурой на улице обновляется раз в час. В итоге может получиться такая неприятность, что при частом изменении температуры в комнате час на графике будет сильно растянут, а в случае отсутствия изменений в параметрах длинный отрезок времени может отображаться как очень короткий. При отрисовке нескольких графиков на одном поле это особенно заметно. Какими-то параметрами и функциями самой библиотеки лично мне решить эту проблему не удалось.
Стоит заметить, что в модуле Highcrarts те же самые данные отображаются абсолютно правильно, шкала времени строго линейна, но в нем не так удобно реализовано масштабирование и выборка данных за отдельный период времени.
Для использования модуля Highstock предлагаю следующий костыль, как одно из возможных решений.
Чтобы не периодичные данные данные стали периодичными, можно создать отдельный класс, например, Graphics создать в нем объекты с названиями вида GraphsLivingroom, GraphsKitchen, GraphsPowermeter1 и, тому подобными. Для класса создать полный набор всевозможных логгируемых свойств. В списке сценариев создать сценарий заполнения соответствующих свойств объектов класса Graphs из свойств реальных объектов и ежеминутно запускать этот скрипт. В итоге, все свойства объектов нового класса будут заполняться одновременно с равными интервалами, без разрывов и.т.п. График Highstock будет строиться правильно. Минусом являются дополнительные телодвижения и увеличение базы данных, потому что в ней будут храниться ежеминутные (хотя можно обновлять значения и каждые 5, 10 минут и.т.д.) записи о данных, которые могут обновляться раз в час, плюс само по себе такое дублирование не самый красивый выход.
Если кто-то покажет нормально работающий Highstock с правильной отрисовкой данных различной периодичности, я буду просто счастлив.
Пока выход либо использование Highcharts, либо костыль в Highstock.
UPD: оказывается, костыль не работает. Ни в какую не хочет писать в историю одинаковые значения каждую минуту. Я в полной растерянности...
-
- Сообщения: 291
- Зарегистрирован: Вт ноя 18, 2014 11:43 pm
- Откуда: Краснодарский край
- Благодарил (а): 32 раза
- Поблагодарили: 68 раз
Re: Строим графики Highstock
Это не баг а фитча.Fav0rit писал(а):UPD: оказывается, костыль не работает. Ни в какую не хочет писать в историю одинаковые значения каждую минуту. Я в полной растерянности...
Свойство записывает первое и последнее значение уникального значения. Больше 2 подряд одинаковых значений быть не может. Это сделано для оптимизации хранения данных в БД.
Majordomo (GitHub) на cubietruck + MegaD + 1-wire
CONNECT: http://connect.smartliving.ru/profile/311
CONNECT: http://connect.smartliving.ru/profile/311