[Модификация] Строим графики Highstock

Вносятся изменения в файлы или устанавливаются доп программы

Модераторы: immortal, newz20

Urbas81
Сообщения: 289
Зарегистрирован: Вс сен 16, 2012 9:39 am
Благодарил (а): 0
Поблагодарили: 1 раз

Re: Строим графики Highstock

Сообщение Urbas81 » Пн авг 03, 2015 6:04 pm

xxc писал(а): У меня работает автоматическое обновление графиков - правда, пришлось менять код выдачи JSON.
Смысл в чем: в выдаче JSON добавлен последний ID для отобранных данных. А в скрипте на странице после получения данных мы этот ID запоминаем. После чего по таймеру периодически проверяем - не появились ли данные для графика с ID большим, чем мы уже получили. Если данные есть - добавляем их к графику.
На код бы взглянуть :? :mrgreen:
xxc
Сообщения: 4
Зарегистрирован: Пн авг 03, 2015 3:53 pm
Благодарил (а): 0
Поблагодарили: 0

Re: Строим графики Highstock

Сообщение xxc » Пн авг 03, 2015 9:07 pm

Urbas81 писал(а): На код бы взглянуть :? :mrgreen:
На "чистоту" кода не претендую - писался в торопях.
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>

Обновление работает. Но вот с разрывами в графике - например датчик завис или нет связи - пока не разобрался - не хватает времени.
olehs
Сообщения: 1115
Зарегистрирован: Вс июн 14, 2015 11:08 am
Благодарил (а): 85 раз
Поблагодарили: 342 раза

Re: Строим графики Highstock

Сообщение olehs » Пн авг 03, 2015 9:38 pm

Вижу, что у всех код работает, но не понимаю как.
У меня $.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]....
Кто-то сталкивался с таким? Может есть модифицированный сценарий?
xxc
Сообщения: 4
Зарегистрирован: Пн авг 03, 2015 3:53 pm
Благодарил (а): 0
Поблагодарили: 0

Re: Строим графики Highstock

Сообщение xxc » Пн авг 03, 2015 9:50 pm

olehs писал(а): У меня $.getJSON уходит в ошибку parseerror, что и логично, т.к. то, что возвращает сценарий, не совсем валидный JSON
Выдавало такую ж ошибку, пока я не стал забирать JSON не через $.getJSON, а так:

Код: Выделить всё

$.ajax({
            dataType: 'json',
            method: 'POST',
            url: url,
            success: function (data) {
                ......
                })
            }
        });
Т.е. с явным указанием типа данных - dataType: 'json'
olehs
Сообщения: 1115
Зарегистрирован: Вс июн 14, 2015 11:08 am
Благодарил (а): 85 раз
Поблагодарили: 342 раза

Re: Строим графики Highstock

Сообщение olehs » Пн авг 03, 2015 9:58 pm

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].... 
Кто-то сталкивался с таким? Может есть модифицированный сценарий?
Простите, вопрос снимается. Все-таки это была ошибка в данных истории.
Urbas81
Сообщения: 289
Зарегистрирован: Вс сен 16, 2012 9:39 am
Благодарил (а): 0
Поблагодарили: 1 раз

Re: Строим графики Highstock

Сообщение Urbas81 » Вт авг 04, 2015 9:19 am

xxc писал(а):
Urbas81 писал(а): На код бы взглянуть :? :mrgreen:
На "чистоту" кода не претендую - писался в торопях.
Обновление работает. Но вот с разрывами в графике - например датчик завис или нет связи - пока не разобрался - не хватает времени.
Спасибо. Сначала подумал что речь идет про автоматическое обновление там, где выведено несколько графиков на одном поле.
Fav0rit
Сообщения: 60
Зарегистрирован: Чт мар 19, 2015 10:27 pm
Благодарил (а): 8 раз
Поблагодарили: 22 раза

Re: Строим графики Highstock

Сообщение Fav0rit » Пт сен 25, 2015 9:11 am

Только недавно натыкался на эту библиотеку с графиками и хотел их прикрутить к умному дому, а оказалось все уже давно прикручено,, работает и есть готовые примеры, это просто замечательно.
Огромнейшее спасибо за проделанную работу, все очень круто.

И, пожалуй, внесу свои 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>
За это сообщение автора Fav0rit поблагодарил:
Ko/|xo3HUk (Пн ноя 28, 2016 10:12 pm)
Рейтинг: 1.16%
Fav0rit
Сообщения: 60
Зарегистрирован: Чт мар 19, 2015 10:27 pm
Благодарил (а): 8 раз
Поблагодарили: 22 раза

Re: Строим графики Highstock

Сообщение Fav0rit » Пн сен 28, 2015 12:18 am

Вот проблемка есть, которую я все никак не могу победить изменением параметров отрисовки, видимо как-то связано именно с JSON, с которым я работать не умею. Данные с датчиков приходят с различными интервалами, использую MySensor, система часто опрашивает гейт, поэтому повторные сведения не пишет, поэтому на графиках видны только параметры в момент изменения значения, из-за чего график представляет из себя гармошку, а ось времени, как её ни настраивай, нифига не линейная, что жутко раздражает и убивает всю идею. Примеры графиков с сайта с конфигами для непериодических данных работать не хотят, в отрисовке ничего не меняется, как я ни пытаюсь...
Fav0rit
Сообщения: 60
Зарегистрирован: Чт мар 19, 2015 10:27 pm
Благодарил (а): 8 раз
Поблагодарили: 22 раза

Re: Строим графики Highstock

Сообщение Fav0rit » Вт сен 29, 2015 1:13 pm

Более менее разобрался я с этими графиками, однако не уверен, что сказанное мною ниже на 100% верно, если кто-то исправит, я буду только рад.
В библиотеке есть несколько типов графиков, а именно Highcharts, Highstock, Highmaps. Последний тип нас не интересует, там только карты, а вот первые два очень даже ничего, как функционально, так и внешне.
Самый удобный и симпатичный из этих трех Highstock, однако с его использованием есть некоторые заморочки.
Первая и самая главная из них - кривое представление не периодичных данных. Например, данные с температурного графика в комнате пишутся в лог каждую минуту, а веб-переменная с температурой на улице обновляется раз в час. В итоге может получиться такая неприятность, что при частом изменении температуры в комнате час на графике будет сильно растянут, а в случае отсутствия изменений в параметрах длинный отрезок времени может отображаться как очень короткий. При отрисовке нескольких графиков на одном поле это особенно заметно. Какими-то параметрами и функциями самой библиотеки лично мне решить эту проблему не удалось.
Стоит заметить, что в модуле Highcrarts те же самые данные отображаются абсолютно правильно, шкала времени строго линейна, но в нем не так удобно реализовано масштабирование и выборка данных за отдельный период времени.
Для использования модуля Highstock предлагаю следующий костыль, как одно из возможных решений.
Чтобы не периодичные данные данные стали периодичными, можно создать отдельный класс, например, Graphics создать в нем объекты с названиями вида GraphsLivingroom, GraphsKitchen, GraphsPowermeter1 и, тому подобными. Для класса создать полный набор всевозможных логгируемых свойств. В списке сценариев создать сценарий заполнения соответствующих свойств объектов класса Graphs из свойств реальных объектов и ежеминутно запускать этот скрипт. В итоге, все свойства объектов нового класса будут заполняться одновременно с равными интервалами, без разрывов и.т.п. График Highstock будет строиться правильно. Минусом являются дополнительные телодвижения и увеличение базы данных, потому что в ней будут храниться ежеминутные (хотя можно обновлять значения и каждые 5, 10 минут и.т.д.) записи о данных, которые могут обновляться раз в час, плюс само по себе такое дублирование не самый красивый выход.
Если кто-то покажет нормально работающий Highstock с правильной отрисовкой данных различной периодичности, я буду просто счастлив.
Пока выход либо использование Highcharts, либо костыль в Highstock.

UPD: оказывается, костыль не работает. Ни в какую не хочет писать в историю одинаковые значения каждую минуту. Я в полной растерянности...
zelevova
Сообщения: 291
Зарегистрирован: Вт ноя 18, 2014 11:43 pm
Откуда: Краснодарский край
Благодарил (а): 32 раза
Поблагодарили: 68 раз

Re: Строим графики Highstock

Сообщение zelevova » Чт окт 08, 2015 12:36 pm

Fav0rit писал(а):UPD: оказывается, костыль не работает. Ни в какую не хочет писать в историю одинаковые значения каждую минуту. Я в полной растерянности...
Это не баг а фитча. ;)
Свойство записывает первое и последнее значение уникального значения. Больше 2 подряд одинаковых значений быть не может. Это сделано для оптимизации хранения данных в БД.
Majordomo (GitHub) на cubietruck + MegaD + 1-wire
CONNECT: http://connect.smartliving.ru/profile/311
Ответить