Помогите с кодом (с логикой) пожалуйста

Использование системы в различных ситуациях, вопросы программирования сценариев.

Модератор: immortal

Logrus
Сообщения: 1778
Зарегистрирован: Пт апр 07, 2017 12:20 pm
Благодарил (а): 296 раз
Поблагодарили: 369 раз

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение Logrus » Пн май 10, 2021 9:15 pm

можете на гите посмотреть, хотя все это у вас локально есть
посмотрите в пу счетчика в инжекциях
Telegram | Блог
Raspberry Pi3, с образа от Сергея 3.31, PHP 7
Почти всё время уходит на исправление ошибок, оставшееся - на их повторение. (с) ))) Спасибо
Chainik
Сообщения: 1387
Зарегистрирован: Вс янв 10, 2016 11:05 am
Благодарил (а): 230 раз
Поблагодарили: 433 раза

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение Chainik » Пн май 10, 2021 11:55 pm

Приятно иметь дело с человеком, который знает, чего хочет, и может это четко сформулировать.

Начну опять же с выражения благодарности ув.xor за его замечательную функцию (https://connect.smartliving.ru/profile/ ... omili.html). Также поблагодарю ув.SmoKE_xDDD за вебинар от 04.04.2021, из которого в числе прочего я узнал, каким образом можно собственные функции легко "прикрутить" к системе.

Перед тем, как приступать к счетчику, потренируемся на более простом объекте. Соберем статистику работы насосов. Узнаем, как долго они работали в течение прошлого дня и как часто включались.

Прикручиваем функцию getHistoryPart от ув.xor к системе. В директории \htdocs\lib\ создаем файл с именем, к примеру, "my.class.php" и вписываем в него код функции:

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

<?php

//возвращает долю времени на интервале, где истинно сравнение $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;
}

?>
После этого можем пользоваться данной функцией в коде наравне со штатными функциями системы.

В класс с объектами насосов добавляем свойства, куда будем сохранять статистику по их использованию за истекший день:
UsagePart -- доля дня, когда насос был включен;
UsagePercent -- процент времени в течение дня, когда насос был включен (то же что и доля, только переведенная в проценты);
UsagePeriod -- период времени (текстом), когда насос был включен (например, "11 ч. 17 мин. 38 сек.");
UsageАctivationQuantity -- количество включений насоса.
У свойств включаем историю.

Добавляем в класс насосов метод "getUsageStatistics".
Код запишем в методы объектов и поправим (при необходимости) имя объекта в функциях getHistoryPart и getHistorySum:

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

//Получаем статистику работы насоса за прошлый день

//Рассчитываем долю времени работы насоса в течение прошлого дня
$StartLastDayTime = strtotime(date('Y-m-d 0:0:0',time()-60*60*24));
$EndLastDayTime = strtotime(date('0:0:0',time()-1));
$Part = getHistoryPart('PumpHeat_01.Status',$StartLastDayTime,$EndLastDayTime,'=',1);
$this->setProperty('UsagePart', $Part);

//Процент пребывания во включенном состоянии в течение анализируемого периода
$UsagePercent = round($Part*100,2);
$this->setProperty('UsagePercent', $UsagePercent);

//Продолжительность пребывания во включенном состоянии в течение дня
$UsagePeriod = round($Part*60*60*24,0);
$UsagePeriod_H = floor($UsagePeriod/60/60);
$UsagePeriod_M = floor($UsagePeriod/60 - $UsagePeriod_H*60);
$UsagePeriod_S = floor($UsagePeriod - $UsagePeriod_H*60*60 - $UsagePeriod_M*60);
$UsagePeriodLastDayText = $UsagePeriod_H." ч. ".$UsagePeriod_M." мин. ".$UsagePeriod_S." сек.";
$this->setProperty('UsagePeriod', $UsagePeriodLastDayText);

//Считаем количество включений насоса в течение прошлого дня
$UsageАctivationQuantity = getHistorySum('PumpHeat_01.Status',$StartLastDayTime,$EndLastDayTime);
$this->setProperty('UsageАctivationQuantity', $UsageАctivationQuantity);
Хотя метод и под классовый несложно переписать:

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

//Получаем статистику работы насоса за прошлый день


//Рассчитываем долю времени работы насоса в течение прошлого дня
$ot = $this->object_title;
$StartLastDayTime = strtotime(date('Y-m-d 0:0:0',time()-60*60*24));
$EndLastDayTime = strtotime(date('0:0:0',time()-1));
$Part = getHistoryPart($ot.'.Status',$StartLastDayTime,$EndLastDayTime,'=',1);
$this->setProperty('UsagePart', $Part);

//Процент пребывания во включенном состоянии в течение анализируемого периода
$UsagePercent = round($Part*100,2);
$this->setProperty('UsagePercent', $UsagePercent);

//Продолжительность пребывания во включенном состоянии в течение дня
$UsagePeriod = round($Part*60*60*24,0);
$UsagePeriod_H = floor($UsagePeriod/60/60);
$UsagePeriod_M = floor($UsagePeriod/60 - $UsagePeriod_H*60);
$UsagePeriod_S = floor($UsagePeriod - $UsagePeriod_H*60*60 - $UsagePeriod_M*60);
$UsagePeriodLastDayText = $UsagePeriod_H." ч. ".$UsagePeriod_M." мин. ".$UsagePeriod_S." сек.";
$this->setProperty('UsagePeriod', $UsagePeriodLastDayText);

//Считаем количество включений насоса в течение прошлого дня
$UsageАctivationQuantity = getHistorySum($ot.'.Status',$StartLastDayTime,$EndLastDayTime);
$this->setProperty('UsageАctivationQuantity', $UsageАctivationQuantity);
Запускать метод надо раз в сутки. Можно из OnNewMinute, а лучше даже из onNewHour:

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

if (timeIs('00:01')) callMethodSafe('PumpHeat_01.getUsageStatistics');
if (timeIs('00:02')) callMethodSafe('PumpHeat_02.getUsageStatistics');
if (timeIs('00:03')) callMethodSafe('PumpHeat_03.getUsageStatistics');
Можно и одновременно все запускать, но это я уж перестраховываюсь, и распределяю нагрузку на систему по времени.
А самый лучший вариант -- запускать код из onNewDay. Если такого объекта в таймерах нет, надо его просто добавить. А оттуда уже запускать через небольшие промежутки времени:

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

callMethodSafe('PumpHeat_01.getUsageStatistics');
SetTimeOut("PumpHeat_02_getUsageStatistics","callMethodSafe('PumpHeat_02.getUsageStatistics');",5);
SetTimeOut("PumpHeat_03_getUsageStatistics","callMethodSafe('PumpHeat_03.getUsageStatistics');",10);
Как использовать полученную статистику в интерфейсе (в информерах, в текстовых блоках с переменными) писать не буду. Это, думаю, вопрос вторичный.

А счетчиком, как более сложным объектом, займемся завтра-послезавтра.
Хотя это и не особо принципиально, но из исходных данных нет только дневного и ночного тарифов.
Да,... и имя Объект.Свойства надо знать, куда сохраняется данные счетчика об израсходованных киловаттах.
Последний раз редактировалось Chainik Вт май 11, 2021 11:56 am, всего редактировалось 1 раз.
За это сообщение автора Chainik поблагодарил:
John (Вт май 11, 2021 1:42 am)
Рейтинг: 1.18%
John
Сообщения: 38
Зарегистрирован: Пн ноя 10, 2014 1:52 pm
Благодарил (а): 12 раз
Поблагодарили: 0

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение John » Вт май 11, 2021 1:48 am

Примите мою искреннюю благодарность!!!
Вот уже задумался над интерфейсом, сделаю отдельную сцену с отоплением и всей статистикой.
Тарифы постараюсь узнать, но их всегда вписать можно.
Сейчас счетчик все передает в Counter02.valueWork, но можно назначить любой другой объект и свойство
Аватара пользователя
xor
Сообщения: 1753
Зарегистрирован: Сб ноя 22, 2014 8:45 pm
Благодарил (а): 216 раз
Поблагодарили: 528 раз

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение xor » Вт май 11, 2021 2:16 am

Chainik писал(а):
Пн май 10, 2021 11:55 pm
Запускать метод надо раз в сутки. Можно из OnNewMinute, а лучше даже из onNewHour:
Фундаментальный разбор! Единственно добавлю, что, если это нужно делать в полночь, я использую onNewDay
Этот метод запускается с началом нового дня как раз. Тогда разнесение запусков на неск. секунд и в разные потоки можно так записать, попроще:

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

 SetTimeOut("sun_t","RunScript('sun');",2);
 SetTimeOut("moon_t","RunScript('moon');",5);
 SetTimeOut("rates","RunScript('Курсы');",7);
 setTimeOut("holidays","RunScript('holidays');",10);
Chainik
Сообщения: 1387
Зарегистрирован: Вс янв 10, 2016 11:05 am
Благодарил (а): 230 раз
Поблагодарили: 433 раза

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение Chainik » Вт май 11, 2021 8:49 am

Xor, моё почтение!
Я бы тоже в OnNewDay запихнул, но такого объекта нет в "нулёвой" системе. Понятно, что нетрудно добавить, но вопрос в том, каким образом вы инициируете запуск OnNewDay?
Аватара пользователя
xor
Сообщения: 1753
Зарегистрирован: Сб ноя 22, 2014 8:45 pm
Благодарил (а): 216 раз
Поблагодарили: 528 раз

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение xor » Вт май 11, 2021 10:37 am

Chainik писал(а):
Вт май 11, 2021 8:49 am
Xor, моё почтение!
Я бы тоже в OnNewDay запихнул, но такого объекта нет в "нулёвой" системе. Понятно, что нетрудно добавить, но вопрос в том, каким образом вы инициируете запуск OnNewDay?
Этот метод нужно просто создать в классе, а в коде системы его обновление уже прописано так же, как и для минут и часов
За это сообщение автора xor поблагодарил:
Chainik (Вт май 11, 2021 11:44 am)
Рейтинг: 1.18%
Chainik
Сообщения: 1387
Зарегистрирован: Вс янв 10, 2016 11:05 am
Благодарил (а): 230 раз
Поблагодарили: 433 раза

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение Chainik » Вт май 11, 2021 11:58 am

Важно! Код добавленной функции должен быть обрамлен в теги PHP, иначе будет выдавать ошибку. Поправил в тексте поста.
John
Сообщения: 38
Зарегистрирован: Пн ноя 10, 2014 1:52 pm
Благодарил (а): 12 раз
Поблагодарили: 0

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение John » Вт май 11, 2021 3:09 pm

Функцию вставил, все прекрасно работает, спасибо огромное !!!
Chainik
Сообщения: 1387
Зарегистрирован: Вс янв 10, 2016 11:05 am
Благодарил (а): 230 раз
Поблагодарили: 433 раза

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение Chainik » Вт май 11, 2021 11:56 pm

Ну прекрасно. Единственное, порекомендую еще залезть в модуль Оптимайзер и отключить всякую оптимизацию у наших свойств с историей, особенно у свойства Status. А то модуль может начать усреднять и "прореживать" нули с единицами. Он на это способен, если "пустить дело на самотек". ))

Со счетчиком принципиальных проблем не вижу, но не могу гарантировать, что завтра все закончу (в связи с появившимися неотложными делами). Но то что в итоге сделаем, не сомневайтесь.
Chainik
Сообщения: 1387
Зарегистрирован: Вс янв 10, 2016 11:05 am
Благодарил (а): 230 раз
Поблагодарили: 433 раза

Re: Помогите с кодом (с логикой) пожалуйста

Сообщение Chainik » Чт май 13, 2021 12:06 am

Друзья! Я тут по наличию времени "сочиняю", чтобы долго не рыться в поисках ответа, может подскажите, как проще извлечь значение из строки? В общем, получаю нужную мне строчку из истории в виде:
{"VALUE":"555.25","ADDED":"2021-05-12 18:20:14"}
Каким образом проще извлечь из строки значение 555.25?
Ответить