Xor

<<< Назад

По следам наших выступлений - сколько мы наэкономили?

Предлагается функция, считающая долю времени исторического свойства в зависимости от его величины

Навеяно блогом о среднем тык(1) и обсуждением на форуме тык

Народ озабачивается вопросом - сколько сегодня работал режим Экономный? или Какая у меня сеть 220? или Сколько времени работали мои нагреватели, управляемые термостатом?
Оказалось, что если свойство является бинарным (0/1), то долю времени работы/активности (т.е., когда значение свойства = 1) можно получить с помощью функции из блога (1).
Пример - есть реле конвектора, управляемое термостатом с пид-регулятором, как узнать сколько времени за день реле включено?
Возьмём функцию getHistoryAvgMy($obj_prop,$start_time,$stop_time) и запустим ее с параметрами:

console>getHistoryAvgMy( 'Noorelay_1.status',-60 * 60 * 24 + 1,0)
console>0.39366196368014

Тут мы смотрели на интервал времени в 24 часа от текущего и узнали, что 39% от суток реле Noorelay_1 было включено. Можно узнать (если история позволяет) время активности объекта на любом интервале, задавая время старта/окончания расчета - за позавчера,с начала суток - strtotime(date("Y-m-d").' 00:00:00'), за час, месяц и т.д.
Но что делать, если свойство не бинарное? Типа, как посчитать долю суток с отрицательной температурой или как долго моя сеть работает в ненормальном режиме - вне диапазона -- пусть от 209 до 231 в?

Родил функцию:

//возвращает долю времени на интервале, где истинно сравнение $objprop со значением $value по правилу $oper
//$oper{'=','<>','>','>=','<','<=','betw','!betw'};
//ex gg('obj.prop') == 1 / gg('obj.prop') betw -1,1/ gg('obj.prop') !betw 10,15
//return t(true part)/t(whole interval)
function getHistoryPart($objprop,  $start_time, $stop_time = 0, $oper = "=", $value = '1', $value2 = '0') { 

        if($start_time <= 0) $start_time = (time() + $start_time);
        if($stop_time  <= 0) $stop_time  = (time() + $stop_time);

    $pvalue = getHistoryValueId($objprop);
    if(defined('SEPARATE_HISTORY_STORAGE') && SEPARATE_HISTORY_STORAGE == 1) {
        $table_name = createHistoryTable($pvalue);
    } else {
        $table_name = 'phistory';
    }

         // Получить количество записей за нужный период времени
         $arr_s = SQLSelectOne("SELECT COUNT(ID) as COUNT_ID FROM $table_name ".
                  "WHERE VALUE_ID=".$pvalue." and not value is null ".
                  "AND ADDED between '".date('Y-m-d H:i:s', $start_time)."' AND '".date('Y-m-d H:i:s', $stop_time)."'");
         // Взять это количество записей +1
         $arr_s = SQLSelect("SELECT * FROM $table_name WHERE VALUE_ID=".$pvalue." and not value is null ".
                  " AND ADDED<='".date('Y-m-d H:i:s', $stop_time)."' ORDER BY ADDED DESC LIMIT 0 , ".(1+$arr_s['COUNT_ID']));
         $tmr2 = $stop_time;

         // Переберем весь массив
         foreach($arr_s as $s) {
          $tmr1 = $tmr2;
          $tmr2 = strtotime($s['ADDED']);

          // Ограничить началом 
          if($tmr2<$start_time) $tmr2 = $start_time; 
            // Двигаясь вниз по массиву времени складывать отрезки 
            //echo(date('H:i:s', $tmr1).' - '.date('H:i:s', $tmr2).' = '.($tmr1 - $tmr2).'c  t='.$s['VALUE'].'<br>');
            if($oper == '=' || $oper == '=='){
              if($s['VALUE']==$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '>'){
              if($s['VALUE']>$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '<'){
              if($s['VALUE']<$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '>='){
              if($s['VALUE']>=$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '<='){
              if($s['VALUE']<=$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
             }elseif($oper == '<>' || $oper == '!='){
              if($s['VALUE']<>$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == 'betw'){
              if($s['VALUE']>=$value && $s['VALUE']<=$value2) $tmr_mem = $tmr_mem + $tmr1 - $tmr2; 
            }elseif($oper == '!betw'){
              if($s['VALUE']<$value || $s['VALUE']>$value2) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }
         }//foreach

$all = $stop_time - $start_time; //всего времени с начала интервала до конца
return $tmr_mem/$all;
}

если мы запустим ее с такими параметрами

console>getHistoryPart('Noorelay_1.status',-60*60*24+1,0,'=',1)
console>0.39366196368014

то она отработает, как функция выше. Но эта функция позволяет задавать свои операции сравнения значения свойства
это может быть проверка на равенство ('=','=='), неравенство ('!=','<>'), больше/меньше ('>','>=','<','<='), вхождение/невхождение в интервал ('betw','!betw')

примеры
Наш, c электричеством - как долго моя сеть работает в ненормальном режиме - вне диапазона -- пусть от 209 до 231 в --с начала дня?

console>getHistoryPart('ippon.inVoltage',strtotime(date("Y-m-d").' 00:00:00'),0,'!betw',209,231)
console>0.012494285267855
(ого, больше процента!)
Доля отрицательной наружной температуры за последние сутки:
console>getHistoryPart('Outside.Temperature',-60*60*24+1,0,'<',0)
Доля отрицательной наружной температуры за предпоследние сутки:
console>getHistoryPart('Outside.Temperature',-60*60*24*2+1,-60*60*24,'<',0)

ps Если нужно узнать время вместо доли :
для примера getHistoryPart('Outside.Temperature',-60*60*24*2+1,-60*60*24,'<',0)

//приведём время от смещения к абсолютному для того, чтобы узнать длительность всего интервала
//т.к. у нас в примере указано отрицательное смещение
$start_time = time() - 60*60*24*2+1;
$stop_time  = time() - 60*60*24;
$t_sec = ($stop_time-$start_time)*getHistoryPart('Outside.Temperature',$start_time,$stop_time,'<',0);

Для примера getHistoryPart('Outside.Temperature',-60*60*24+1,0,'<',0)

//приведём время от смещения к абсолютному для того, чтобы узнать длительность всего интервала
$start_time = time() - 60*60*24+1;
$stop_time  = time();
$t_sec = ($stop_time-$start_time)*getHistoryPart('Outside.Temperature',$start_time,$stop_time,'<',0);

pps По просьбе публики (Логрусу привет!) вариант функции, возвращающий всё массивом:

function getHistoryPart2($objprop,  $start_time, $stop_time = 0, $oper = "=", $value = '1', $value2 = '0') { 

         if($start_time <= 0) $start_time = (time() + $start_time);
         if($stop_time  <= 0) $stop_time  = (time() + $stop_time);

         $pvalue = getHistoryValueId($objprop);
         if(defined('SEPARATE_HISTORY_STORAGE') && SEPARATE_HISTORY_STORAGE == 1) {
             $table_name = createHistoryTable($pvalue);
         } else {
             $table_name = 'phistory';
         }

         // Получить количество записей за нужный период времени
         $arr_s = SQLSelectOne("SELECT COUNT(ID) as COUNT_ID FROM $table_name ".
                  "WHERE VALUE_ID=".$pvalue." and not value is null ".
                  "AND ADDED between '".date('Y-m-d H:i:s', $start_time)."' AND '".date('Y-m-d H:i:s', $stop_time)."'");
         // Взять это количество записей +1
         $arr_s = SQLSelect("SELECT * FROM $table_name WHERE VALUE_ID=".$pvalue." and not value is null ".
                  " AND ADDED<='".date('Y-m-d H:i:s', $stop_time)."' ORDER BY ADDED DESC LIMIT 0 , ".(1+$arr_s['COUNT_ID']));
         $tmr2 = $stop_time;

         // Переберем весь массив
         foreach($arr_s as $s) {
          $tmr1 = $tmr2;
          $tmr2 = strtotime($s['ADDED']);

          // Ограничить началом 
          if($tmr2<$start_time) $tmr2 = $start_time; 
            // Двигаясь вниз по массиву времени складывать отрезки 
            //echo(date('H:i:s', $tmr1).' - '.date('H:i:s', $tmr2).' = '.($tmr1 - $tmr2).'c  t='.$s['VALUE'].'<br>');
            if($oper == '=' || $oper == '=='){
              if($s['VALUE']==$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '>'){
              if($s['VALUE']>$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '<'){
              if($s['VALUE']<$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '>='){
              if($s['VALUE']>=$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == '<='){
              if($s['VALUE']<=$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
             }elseif($oper == '<>' || $oper == '!='){
              if($s['VALUE']<>$value) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }elseif($oper == 'betw'){
              if($s['VALUE']>=$value && $s['VALUE']<=$value2) $tmr_mem = $tmr_mem + $tmr1 - $tmr2; 
            }elseif($oper == '!betw'){
              if($s['VALUE']<$value || $s['VALUE']>$value2) $tmr_mem = $tmr_mem + $tmr1 - $tmr2;
            }
         }//foreach

$all = $stop_time - $start_time; //всего времени с начала интервала до конца
$ret = array();
$ret['PART'] = $tmr_mem/$all; //тут доля
$ret['PART_TIME'] = $tmr_mem; // тут время в секундах, где условие истинно
$ret['ALL_TIME'] = $all;      //всего времени с начала интервала до конца
return $ret;
}

Вызов в консоли

console>print_r(getHistoryPart2('Noorelay_1.status',-60*60*24))

возвращает массив:

Array
(
    [PART] => 0.54081018518519
    [PART_TIME] => 46726
    [ALL_TIME] => 86400
)
1
  • поправил умолчания - если время конца опущено, берём текущее, операция по умолчанию =1

Обсуждение (4) (9)

Смотрите так же:
04.11.2019 Группа как объект - экономим на коде - 2
26.10.2019 Прикручиваем Grafana к Мажордому
11.10.2019 Группа как объект - экономим на коде
25.09.2019 Если у вас нет телеги...
11.09.2019 Датчик присутствия из ип камеры
30.07.2019 Win-платформа-замена ffmpeg для получения скриншотов из rstp - потока
24.06.2019 Форматированный отчет в телеграм - имитация таблицы
30.04.2019 Используем зомбо-ящик в Мажордомо
24.03.2019 О среднем в Мажордомо
31.10.2018 Об "обделённых" пользователях Win-систем -- с точки зрения кэширования winTTS сообщений
16.09.2018 Перезагрузка Мажордомо в Win-системах

Домодедово, Россия

На форуме: xor