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

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

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

Ruslan
Сообщения: 37
Зарегистрирован: Чт апр 09, 2015 7:11 pm
Благодарил (а): 7 раз
Поблагодарили: 1 раз

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

Сообщение Ruslan » Пт мар 18, 2016 5:29 pm

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

И, пожалуй, внесу свои 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>


Этот код у меня не хочет работать, а очень хочется :D

Простые графики строит, пробовал по отдельности для каждого значения. :?:
RaspBerry Pi 2 | MajorDoMo lastUpdate:27/04/2017
AlexK-71
Сообщения: 28
Зарегистрирован: Сб ноя 29, 2014 12:20 am
Благодарил (а): 7 раз
Поблагодарили: 0

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

Сообщение AlexK-71 » Чт апр 14, 2016 12:28 pm

После апрельских обновлений перестал работать.
У всех так? Или, что то только у меня сломалось?
-[a.v.p]-
Сообщения: 2
Зарегистрирован: Пн окт 24, 2016 1:49 pm
Откуда: Брест, Беларусь
Благодарил (а): 0
Поблагодарили: 1 раз

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

Сообщение -[a.v.p]- » Пн окт 24, 2016 2:01 pm

Если еще актуально, то сделал автообновление. Вроде работает. Строит и обновляет для любого количества датчиков.

код скрипта jconhs-new:
СпойлерПоказать

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

// Получить имя и проверить
 if ( isset($params['name'])  ) { $name = $params['name']; } else { returm; }
 $last_id = isset($params['lastid']) ? (int)$params['lastid'] : 0;
// Разбить на объект и свойство
 $name = explode('.', $name);

 // Получить объект по имени
 $obj=getObject($name[0]); 
 // Получить id свойства
 $prop_id=$obj->getPropertyByName($name[1], $obj->class_id, $obj->id);
 // Получаем VALUE_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 ($last_id>0){
    $sql .= " and id>$last_id";
  }
 $sql .= "  ORDER BY ADDED ";
 $arr_s = SQLSelect($sql);

  $data = [];

  header('Content-Type: application/json');

  foreach ($arr_s as $row){
    $added = $row['ADDED'];
    $t = (float)($row['VALUE']);
    $data[] = [$added*1000, $t];
    $last_id = $row['ID'];
  }

  echo(json_encode([
    'last_id'=>$last_id,
    'data'=>$data
  ]));
highstock последний, брал с родного сайта.
код страницы:
СпойлерПоказать

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

<script type="text/javascript" src="../../highcharts_new/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="../../highcharts-new/js/highstock.js"></script>
<script type="text/javascript">

$(function () {
    var seriesOptions = [],
        last_id = [],
        serData = [],
        now = 0,
         chart,
        seriesCounter = 0,
        names = ['улица','зал','спальня','столовая'], 
        sensornames = ['ESP00F8E1FC.dhtt1', 
                       'ESP00F8E1FC.bmpt',
                       '18:fe:34:9e:25:8e.temp',
                       '18:fe:34:a4:e1:a5.tmp1'];

    Highcharts.setOptions({
        lang: {
            months: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль', 
                     'Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],

            shortMonths: ['Янв','Фев','Мар','Апр','Май','Июн','Июл', 
                          'Авг','Сен','Окт','Ноя','Дек'],

            weekdays: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],

            rangeSelectorZoom: 'Маcштаб',
            rangeSelectorFrom: 'От',
            rangeSelectorTo: 'До',
            thousandsSep: ' '
        },
        global: {
            useUTC: false
        }
    });

    function createChart() {
        chart = new Highcharts.StockChart({
            chart: {
                renderTo: 'container',
                 zoomType: 'x',
                events: {
                    load: function () {
                        setInterval(function () {
                            updateChart();
                        }, 60000);
                    }
                 }
            },
            rangeSelector: {
                buttons: [
                    { type: 'hour',  count: 1,  text: '1h'  },
                    { type: 'hour',  count: 6,  text: '6h'  },
                    { type: 'hour',  count: 12, text: '12h' },
                    { type: 'day',   count: 1,  text: '1d'  },
                    { type: 'day',   count: 3,  text: '3d'  },
                    { type: 'week',  count: 1,  text: '1w'  },
                    { type: 'month', count: 1,  text: '1m'  },
                    { type: 'all', text: 'All' }],
                    selected: 1,
                    inputEnabled: false
            },
            title: { text : 'График температур в доме' },
            legend: { enabled : true,
                layout : 'horizontal',
                align : 'center',
                verticalAlign : 'top',
                borderWidth: 0,
                x : 0,
                y : 20 },
             xAxis : {
                ordinal: false,
                minRange: 3600 * 1000 // one hour
            },
            yAxis: {
                title: { text : 'Температура (°C)' }
            },
            series: seriesOptions
        });
        var time = new Date().getTime();
        time -= time % 1000;
        for (i = 0; i < names.length; i++){
            len = chart.series[i].processedYData.length - 1;
            last = chart.series[i].processedYData[len];
            chart.series[i].addPoint([time,last], true, true);
//            alert(time + ' ' + last);
        };
    };

    $.each(names, function (i, name) {
        $.getJSON('/objects/?script=jconhs-new&name='+sensornames[i], function (data) {
            last_id[i] = data.last_id;
            seriesOptions[i] = {
                name: name,
                data: data.data,
                type: 'spline'
            };
            seriesCounter += 1;
            if (seriesCounter === names.length) {
                createChart();
            };
        });
    });

    function updateChart() {
         now = 0;
         $.each(names, function (i) {
            $.getJSON('/objects/?script=jconhs-new&name=' + sensornames[i] + '&lastid=' + last_id[i], function (data_) {
                if (data_.data.length > 0){
                    last_id[i] = data_.last_id;
                        $.each(data_.data, function (idx, e) {
//                            chart.series[i].addPoint(e, true, true);
                            now = e[0];
                            serData[i] = e[1];
//                            alert('Date='+now+' value='+e[1]);
                        })
                } else {
                    len = chart.series[i].processedYData.length - 1;
                    serData[i] = chart.series[i].processedYData[len];
                }
                if (i == (names.length - 1)) {
                    updateChart_();
                }
            });
        });
    };

    function updateChart_() {
//        alert(now + ' ' + serData);
        if (now > 0){
            for (i = 0; i < serData.length; i++){
                chart.series[i].addPoint([now,serData[i]], true, true);
            }
        }
    }
});

</script>
<div id="container" style="height: 500px; min-width: 500px; margin: 0 auto"></div>
*** Сообщение запрещено. Сообщение похоже на спам. ***
Ko/|xo3HUk
Сообщения: 160
Зарегистрирован: Ср окт 07, 2015 9:36 am
Благодарил (а): 51 раз
Поблагодарили: 27 раз

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

Сообщение Ko/|xo3HUk » Пн ноя 28, 2016 10:22 pm

Мои 5 копеек по поводу графиков с двумя осями Y. Спасибо Fav0rit за пинок в нужном направлении и за пример. Я его немного переработал под себя, т.к. не совсем понял как в том примере индексы оси Y присваивались. Я для себя сделал так, как мне понятней - присвоил последнему графику (у меня всего их три) id=2 (т.к. айдишники присваиваются начиная с нулевого). И в конце, где формируются seriesOptions, сделал сравнение по ID. Если ID=2, то указываем ему что нужно строить эту серию по оси с ID=2
СпойлерПоказать

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

<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 = [            'гараж',
                     'гараж АМ2320',
                     'влажность'],
          sensornames = ['ESP8266_Garage.Temper_1',
                                'ESP8266_Garage.am2320_temper',
                 'ESP8266_Garage.am2320_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', {
            chart: {
                zoomType: 'x',
//                 resetZoomButton: {
//                    position: {
//                        //align: 'middle', // by default
//                        //verticalAlign: 'bottom' // by default
//                        x: 0,
//                        y: -10
//                        }
//                   },
//                relativeTo: 'chart'
             },
             rangeSelector: {
                buttons: [    { type: 'hour',    count: 1, text: '1h' },
                             { type: 'hour',    count: 6, text: '6h' },
                             { type: 'hour',    count: 12, text: '12h' },
                            { 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: 2  // Какая кнопка выбрана по умолчанию
            },
  
            title: { text : 'График температур в доме'},
            legend: { enabled : true,
                           layout : 'horizontal',
                           align : 'center',
                           verticalAlign : 'top',
                           borderWidth: 0,
                           x : 0,
                           y : 20 },

            xAxis : {
                minRange: 60 * 1000, // одна минута - минимальный диапазон всего графика
                ordinal: false
            },

            yAxis: [{ // Первая ось Y
                 //floor: 0,        //The lowest allowed value for automatically computed axis extremes. Defaults to null.
                 ceiling: 30,    //The highest allowed value for automatically computed axis extremes.
                 //softMax: 30, //A soft maximum for the axis. If the series data maximum is greater than this, the axis will stay at this maximum,
                              //but if the series data maximum is higher, the axis will flex to show all data.
                 //max: 30, //если включить эту опцию, то ось Y всегда будет рисоваться до этого значения!
                title: {
                    text: 'Температура (°C)'
                },
                 opposite: false
            },{ // Вторая ось Y
                 id:'2',
                 ceiling: 100,    //The highest allowed value for automatically computed axis extremes.
                 title: {
                    text: 'Влажность',
                    //style: {
                        //color: Highcharts.getOptions().colors[0]
                    //}
                },
                labels: {
                    format: '{value} %',
                    //style: {
                        //color: Highcharts.getOptions().colors[0]
                    //}
                },
                opposite: true
            }],
 
 
 
 
            tooltip: {
                valueDecimals: 1,
//                valuePrefix: 't = ',
//                valueSuffix: ' °C'
            },

             plotOptions: {
                series: {
                    lineWidth: 1,
                    point: {
                        events: {
                            'click': function () {
                                if (this.series.data.length > 1) {
                                    this.remove();
                                }
                            }
                        }
                    },
                    marker : {
                         enabled : true,
                        radius    : 2
                     }
                 }
            },

            exporting: {
                enabled: false
            },

            series: seriesOptions
        });
    };

    $.each(names, function (i, name) {
        $.getJSON('/objects/?script=jsonhs&name='+sensornames[i]+'&callback=?', function (data) {

            if (i < 2) {
                seriesOptions[i] = {
                    name: name,
                    data: data,
                     //yAxis: 'Temper',
                    type: 'spline'
                 };
             };

             if (i==2) {
                 //add 3th 
                seriesOptions[i] = {
                    name: name,
                    data: data,
                    yAxis: '2'
                 };
             };

 // 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: 500px; min-width: 100%"></div>

В итоге получился у меня такой график:
Безымянный.png
Безымянный.png (60.07 КБ) 9783 просмотра
P.S. Прошу обратить внимание что у меня сценарий называется jsonhs, а не как у всех - jconhs, поэтому если нужно - правьте название в следующей строке:

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

$.getJSON('/objects/?script=jsonhs&name='+sensornames[i]+'&callback=?', function (data) {
Текущий сервер: Ноутбук: HP Probook 4515s (без монитора). ОС: Debian GNU/Linux 8.6 (jessie)
Предыдущий сервер: Raspberry Pi 2B. ОС: Raspbian (jessie)
tsember
Сообщения: 52
Зарегистрирован: Ср фев 04, 2015 12:28 am
Благодарил (а): 54 раза
Поблагодарили: 6 раз

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

Сообщение tsember » Пт дек 30, 2016 5:51 pm

Не знаю, куда правильнее отнести данное сообщение: к графикам или обработке температуры (любых датчиков).

Обновляю свойство temp датчика температуры при его изменении на 0.1 (знаю что это изменение не почувствовать, но люблю точность).
В связи с этим, за час истории может набраться 200-300 записей в таблице phistory. При отрисовке графиков, например, за сутки, идет ожидание достаточное время (10-20 сек). В связи с этим, написал обработку всех датчиков температуры (можно легко переделать и на другие датчики (вместо класса Temperature ставим ваш класс, вместо свойства temp ставим ваше свойство (например status)). Метод этот вызывается раз в час из таймера (у меня из cron`a). Вытаскивает все значения с момента прошлого усреднения, записывает их в отдельное свойство объекта, а всю историю по главному свойству с историей - удаляет.

За код сильно не ругайте, мучал его три вечера (ночи):

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

// получаем все объекты класса Temperature
$objects = getObjectsByClass("Temperature");
// перебираем все полученные объекты (цикл)
foreach($objects as $object)
{
// получаем название объекта
$obj_title = $object['TITLE'];
// получаем ID объекта
$obj_id = $object['ID'];
// получаем все свойства объекта по его названию
$obj=getObject($obj_title); 
// получаем ID свойства temp 
$prop_id = $obj->getPropertyByName('temp', $obj->class_id, $obj->obj_id);
// по известным данным вытаскиваем с базы ID свойства
$pvalue = SQLSelectOne("SELECT ID FROM pvalues WHERE PROPERTY_ID='".$prop_id."' AND OBJECT_ID='".$obj->id."' LIMIT 0,1");
foreach ($pvalue as $pvalue_id)
// средствами mysql сразу получаем среднее значение температур
$query = SQLSelectOne("SELECT AVG(VALUE) as AVG_temp FROM phistory WHERE VALUE_ID = $pvalue_id");
// округляем значение до сотых
$avg_t = round($query[AVG_temp], 2);
// проверяем значение на ноль (если датчик в течение часа не обновлялся)
if(!$avg_t) { return; } else
// записываем новое значение
sg("$obj_title.tempAvg",$avg_t);
// удаляем обработанные значения (чистим историю по данному свойству
SQLExec("DELETE FROM phistory WHERE  VALUE_ID = $pvalue_id");
// выводим информацию на экран (работает при прямом вызове)
echo"<br>Средняя температура датчика $obj_title за последние 60 минут составила: ".$avg_t." градусов<br>";
}
 
Надеюсь, кому нибудь пригодится.
EvgenyG
Сообщения: 217
Зарегистрирован: Вт июл 21, 2015 4:59 pm
Откуда: Москва
Благодарил (а): 56 раз
Поблагодарили: 1 раз

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

Сообщение EvgenyG » Вт янв 31, 2017 1:34 am

В маркет планируется добавить этот модуль?

Можно ли задавать диапазон значений по оси ординат?
MDM Raspberry Pi 3b, esp32, ModBUS, esp8266, NooLite(MTRF-64-USB, SU-1-***, SLF-1-300, PM112, радиопульты PU)
Ko/|xo3HUk
Сообщения: 160
Зарегистрирован: Ср окт 07, 2015 9:36 am
Благодарил (а): 51 раз
Поблагодарили: 27 раз

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

Сообщение Ko/|xo3HUk » Вт янв 31, 2017 6:49 am

Посмотрите в Маркете дополнений модуль "Charts (by Serge J.) Создание графиков"

P.S. Вот, кстати, ссылка на обсуждение этого модуля: viewtopic.php?f=5&t=2810
Текущий сервер: Ноутбук: HP Probook 4515s (без монитора). ОС: Debian GNU/Linux 8.6 (jessie)
Предыдущий сервер: Raspberry Pi 2B. ОС: Raspbian (jessie)
EvgenyG
Сообщения: 217
Зарегистрирован: Вт июл 21, 2015 4:59 pm
Откуда: Москва
Благодарил (а): 56 раз
Поблагодарили: 1 раз

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

Сообщение EvgenyG » Вт янв 31, 2017 11:41 pm

Ko/|xo3HUk писал(а):Посмотрите в Маркете дополнений модуль "Charts (by Serge J.) Создание графиков"

P.S. Вот, кстати, ссылка на обсуждение этого модуля: viewtopic.php?f=5&t=2810
использую модуль Charts, но там нельзя изменять диапазон по ординате и нет возможности анализировать широкий диапазон дат

ветка на форуме по модулю Charts полумёртвая
автор не отвечает на вопросы и в личке не отвечает по модулю Charts
понимаю, что система бесплатная, но он бы хоть цену обозначил за техническую поддержу
кучу времени потратил на задачи, которые специалист решил бы за несколько минут
уже зла не хватает
и не знаю на какую систему перейти. уже всё на MDM подвязано
MDM Raspberry Pi 3b, esp32, ModBUS, esp8266, NooLite(MTRF-64-USB, SU-1-***, SLF-1-300, PM112, радиопульты PU)
Ko/|xo3HUk
Сообщения: 160
Зарегистрирован: Ср окт 07, 2015 9:36 am
Благодарил (а): 51 раз
Поблагодарили: 27 раз

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

Сообщение Ko/|xo3HUk » Ср фев 01, 2017 6:04 am

Я не использую данный модуль - см. выше мой пример графика с двумя осями. Там в коде можно использовать все настройки графиков Highstock.
Текущий сервер: Ноутбук: HP Probook 4515s (без монитора). ОС: Debian GNU/Linux 8.6 (jessie)
Предыдущий сервер: Raspberry Pi 2B. ОС: Raspbian (jessie)
EvgenyG
Сообщения: 217
Зарегистрирован: Вт июл 21, 2015 4:59 pm
Откуда: Москва
Благодарил (а): 56 раз
Поблагодарили: 1 раз

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

Сообщение EvgenyG » Ср фев 01, 2017 9:48 am

Ko/|xo3HUk писал(а):Я не использую данный модуль - см. выше мой пример графика с двумя осями. Там в коде можно использовать все настройки графиков Highstock.
Спасибо
Как Ваш код использовать, куда его записать и как запустить?
MDM Raspberry Pi 3b, esp32, ModBUS, esp8266, NooLite(MTRF-64-USB, SU-1-***, SLF-1-300, PM112, радиопульты PU)
Ответить