[Настройка Linux, PHP, Oracle] Как настроить мониторинг любых бизнес-процессов, в БД Oracle + построение графиков, используя бесплатную версию Grafana
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Вводные. Зачем мне это было нужноЛично мне нужно было организовать мониторинг домашней солнечной электростанции.Кратко о матчасти (хотя этот пост не про неё):
- Инвертор МАП Энергия и 3 солнечных контроллера того же производителя.
- Внутри инвертора установлен микрокомпьютер (производитель его называет "Малина"), который кое-что умеет в плане мониторинга, но не всё что мне нужно, и не очень удобно. Ценность микрокомпьютера в том, что он снимает данные с com-портов инвертора и контроллеров и публикует их насвоём http-сервере в виде Json. Данные веб-сервисов обновляются примерно каждую секунду. Также есть веб-сервисы для управления встроенными в контроллеры и инвертор реле
- Парочка Ethernet-устройств SR-201 это такие платы с релюхами, используются для управления нагрузкой и кое-чем еще, управляются по протоколу tcp и udp.
- Домашний сервер под управлением Centos-8, на нём установлен Oracle (разумеется Express Edition со всеми своими ограничениями, но для домашнего сервера достаточно)
- В оракле крутятся 2 JOBa (на самом деле это persistent процессы, которые крутят бесконечный цикл и перезапускаются примерно раз в полчаса):
- Раз в секуну снимает данные с вебсервисов "Малины", текущее состояние реле устройств SR-201 и пишет это всё в БД Oracle. С Малины снимает с помощью несложных функций на основе utl_http, с реюх - через utl_tcp. Собственно это и есть статистика, которую будем мониторить
- Постоянно пересчитывает статистику за некоторый промежуток времени, и на основе полученных результатов, управляет нагрузкой и еще кое-чем через SR-201 и встроенные реле инвертора и контроллеров.
Вот это всё хозяйство мне нужно мониторить. Причем мониторить не события (событиями занимаетс Job2), а строить графики на основе накопленной статистической информации, визуализировать их на компе и мобилке. Сама "Малина" кое-что умеет, но во-первых не всё (про мои SR-201 она точно ничего не знает), во-вторых неудобный интерфейс - нельзя всё посмотреть на одном экране в удомном мне виде, а в третьих - в некоторых местах кривовато.Вопросы: Почему Oracle а не Postgres например? Ну просто лень, хотелось сделать из того что умею... :-)Выбор пал на Grafana https://grafana.com - довольно мощное средство визуализации статистики и прочей ерунды. Легко настраивается, удобно использовать. Работает с многими БД...Собственно описание проектаДанный пост ни разу не претендует на качественный пошаговый tutorial, я лишь хочу указать путь, по которому Вам будет легче идти.Итак: Устанавливаем grafana
$ sudo nano /etc/yum.repos.d/grafana.repo
[grafana]
name=grafana
baseurl=https://packages.grafana.com/oss/rpm
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packages.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
dnf update
dnf install grafana
systemctl daemon-reload
systemctl enable --now grafana-server
systemctl status grafana-server
Selinux у меня отключен, файрвол тоже, так что в эти нюансы вдаваться не будуДалее одна проблемка: Grafana конечно с Oracle работать умеет, но данная опция (плагин) предоставляется только в Enterprise версии, которая начинается от 24к$ и это в мои планы не входит. Устанавливаем плагин grafana-simple-json-datasource
grafana-cli plugins install grafana-simple-json-datasource
systemctl restart grafana-server
То есть графана у нас в оракл ходить не будет. Она будет брать данные из вебсервиса, теперь дело за малым - вебсервис написать.Вебсервис будем делать на apache + phpДля этого потребуется установить и настроить:httpd, php и php-fpm (у меня php 7.2) установлен и сконфигрирован вместе с freepbx которая живёт на том же сервере :-)Для php нужно подключить библиотеку oci8 - тут есть сложность в том, что для php 7.2 не получится поставить oci8 командой pecl.В общем путь такой:Подключаем репозиторий remi, и оттуда:
dnf install php-pecl-oci8
Подключаем oci8 к php/etc/hp.d/20-oci8.iniВ принципе достаточно раскомментировать 1 строку
extension=oci8.so
Далее этот oci8 не очень хочет запускаться, тут помогут примерно такие строки в /etc/php-fpm.d/www.conf
env[ORACLE_HOSTNAME] = myserver.localdomain
env[ORACLE_UNQNAME] = mydb
env[ORACLE_BASE] = /u01/app/oracle
env[ORACLE_HOME] = /u01/app/oracle/product/18.4.0/dbhome_1
env[ORA_INVENTORY] = /u01/app/oraInventory
env[ORACLE_SID] = mydb
env[LD_LIBRARY_PATH] = /u01/app/oracle/product/18.4.0/dbhome_1/lib:/lib:/usr/lib
env[NLS_LANG] = AMERICAN_CIS.UTF8
Теперь при исполнении php-скрипта на вебсервере, oci8 прекрасно запускаетсяВыкладываем скрипт на вебсервер/var/www/html/gr/gr.php
<?php
header("Content-Type: application/json;");
$conn = oci_pconnect('www', 'www$password', 'mydb', 'AL32UTF8');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// Подготовка выражения
$stid = oci_parse($conn, 'begin LGRAFANA.GetJson(:vPath, :vInp, :vOut); end;');
if (!$stid) {
$e = oci_error($conn);
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// Создадим дескрипторы
$vInp = oci_new_descriptor($conn, OCI_DTYPE_LOB);
$vOut = oci_new_descriptor($conn, OCI_DTYPE_LOB);
// Привяжем переменные
$vPath = $_SERVER["PATH_INFO"];
$postdata = file_get_contents("php://input");
$vInp->writeTemporary($postdata, OCI_TEMP_BLOB);
oci_bind_by_name($stid, ":vPath", $vPath);
oci_bind_by_name($stid, ":vInp", $vInp, -1, OCI_B_BLOB);
oci_bind_by_name($stid, ":vOut", $vOut, -1, OCI_B_BLOB);
// Выполним логику запроса
$r = oci_execute($stid);
if (!$r) {
$e = oci_error($stid);
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
echo $vOut->load();
$vInp ->close();
$vOut ->close();
oci_free_statement($stid);
oci_commit($conn);
oci_close($conn);
?>
Вебсервис готов.В нашей БД есть пакет LGRAFANA, из которого наружу торчит только одна процедура
procedure GetJson(pPathInfo in varchar2, pInpPost in blob, pOutPost out blob);
она и будет обрабатывать Json - запросы от графаны. Эту процедуру целиком приведу чуть ниже, Вы понимаете что это лишь пример для моей электростанции, а в целом формат Json запросов-ответов описан тутhttps://grafana.com/grafana/plugins/grafana-simple-json-datasourceТеперь настройка в самой графане:Configuration - Data Sources - Add DataSource - Simple JSON
Дальше можно идти добавлять DashBoard и накидывать туда панели с нужными графиками... Если у Вас уже есть реализация пакета LGRAFANA разумеется.Да кстати про пакет. Он у меня написан не совсем на PL/SQL, но в целом Вы сможете это использовать для того чтобы понять, что надо написать в своём пакете. Это легко переводится на pl/sql.Вкратце так:
- Реализуем метод, который реагирует на pahinfo=/search и отдаёт массив имён метрик которые мы умеем считать
- Реализуем метод /query который формирует массив данных по нужным метрикам
Полный текст пакета
pragma include([DEBUG_TRIGGER]::[MACRO_LIB]);
CPALL const varchar2(30) := 'Мощность нагр.';
CPNET const varchar2(30) := 'Мощность сеть';
CPACB const varchar2(30) := 'Мощность АКБ';
CPI2C const varchar2(30) := 'Мощность I2C';
CPADD const varchar2(30) := 'Доп. Нагрузка';
CPMP1 const varchar2(30) := 'Мощность MPPT1';
CPMP2 const varchar2(30) := 'Мощность MPPT2';
CPMP3 const varchar2(30) := 'Мощность MPPT3';
CEDAY const varchar2(30) := 'Выработка за день';
CEMP1 const varchar2(30) := 'Выработка MPPT1';
CEMP2 const varchar2(30) := 'Выработка MPPT2';
CEMP3 const varchar2(30) := 'Выработка MPPT3';
CETOB const varchar2(30) := 'На заряд батареи';
CEFRB const varchar2(30) := 'Взято от батареи';
CEFRN const varchar2(30) := 'Взято от сети';
CUNET const varchar2(30) := 'Напряжение сети';
CUOUT const varchar2(30) := 'Напряжение выход';
CUACB const varchar2(30) := 'Напряжение АКБ';
public function TsToUTs(v_Ts in timestamp) return number is
v_Dt date;
begin
v_Dt := v_ts;
return trunc((v_Dt - to_date('01.01.1970','DD.MM.YYYY')) -- Кол-во дней с 1 янв 1970
* (24 * 60 * 60)) -- Теперь это кол-во секунд
* 1000 -- Теперь миллисекунд
+ to_number(to_char(v_ts,'FF3')); -- Добавили миллисекунды
end;
procedure get_query(pInp in out nocopy JSON_OBJECT_T, pOut in out nocopy JSON_ARRAY_T) is
type rtflag is record (
fTp varchar2(30)
,fOb json_object_t
,fAr json_array_t
);
type ttflag is table of rtflag index by string;
tflag ttflag;
vTmpOb json_object_t;
vTmpAr json_array_t;
vTmpId varchar2(30);
vDBeg timestamp;
vDEnd timestamp;
vDDBeg date;
vDDEnd date;
num_tz number;
curts number;
function GetFlag(pFlagName in varchar2) return boolean is
begin
if tflag.exists(pFlagName) then
return true;
else
return false;
end if;
end;
--function GetFlagType(pFlagName in varchar2) return varchar2 is
--begin
-- if tflag.exists(pFlagName) then
-- return tflag(pFlagName).fTp;
-- else
-- pragma error('Нет значения ['||pFlagName||'] в мвссиве tflag');
-- end if;
--end;
procedure AddTrgData(pTrgName in varchar2, pStamp in number, pValue in number) is
begin
vTmpAr := Json_Array_t;
vTmpAr.append(pValue);
vTmpAr.append(pStamp);
tFlag(pTrgName).fAr.append(vTmpAr);
end;
begin
&debug('pInp='||pInp.to_string())
vTmpOb := pInp.get_Object('range');
num_tz := to_number(::[GA_MAP_STAT].[LIB].GetSetting('MALINA_TIME_ZONE'));
vDBeg := vTmpOb.get_Timestamp('from') + numtodsinterval(num_tz,'hour');
vDEnd := vTmpOb.get_Timestamp('to') + numtodsinterval(num_tz,'hour');
vDDBeg := to_date(to_char(vDBeg,'dd.mm.yyyy hh24:mi:ss'),'dd.mm.yyyy hh24:mi:ss');
vDDEnd := to_date(to_char(vDEnd,'dd.mm.yyyy hh24:mi:ss'),'dd.mm.yyyy hh24:mi:ss');
&debug('vDBeg='||to_char(vDBeg,'dd.mm.yyyy hh24:mi:ss:ff'))
&debug('vDEnd='||to_char(vDEnd,'dd.mm.yyyy hh24:mi:ss:ff'))
vTmpAr := pInp.get_Array('targets');
for i in 0 .. vTmpAr.get_size - 1 loop
vTmpOb := JSON_OBJECT_T(vTmpAr.get(i));
vTmpId := vTmpOb.get_string('target');
tflag(vTmpId).fTp := vTmpOb.get_string('type');
tflag(vTmpId).fOb := Json_object_t;
tflag(vTmpId).fAr := Json_array_t;
tflag(vTmpId).fOb.put('target',vTmpId);
end loop;
-- Взять значения мощностей из статистики МАП
if GetFlag(CPALL) or GetFlag(CPNET) or GetFlag(CPACB) or GetFlag(CPI2C) or GetFlag(CUNET) or GetFlag(CUOUT) or GetFlag(CUACB) then
for (select x(x.[QTIME]:qtime
, x.[F__PNET_CALC]:pnet -- Мощность сеть
, - x.[F__PLOAD_CALC] + x.[F__PNET_CALC]:pall -- Мощность нагр.
, - x.[F__PLOAD_CALC]:pacb -- Мощность АКБ
, x.[F__P_MPPT_AVG]:pi2c -- Мощность I2C
, x.[F__UNET]:unet
, x.[F__UOUTMED]:uout
, x.[F__UACC]:uacb
) in ::[GA_MAP_STAT] all
where x.[QTIME] >= vDBeg and x.[QTIME] <= vDEnd
order by x.[QTIME]
) loop
curts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour'));
vTmpId := tflag.first;
while vTmpId is not null loop
case vTmpId of
:CPALL: AddTrgData(vTmpId,curts,x.pall);
:CPNET: AddTrgData(vTmpId,curts,x.pnet);
:CPACB: AddTrgData(vTmpId,curts,x.pacb);
:CPI2C: AddTrgData(vTmpId,curts,x.pi2c);
:CUNET: AddTrgData(vTmpId,curts,x.unet);
:CUOUT: AddTrgData(vTmpId,curts,x.uout);
:CUACB: AddTrgData(vTmpId,curts,x.uacb);
end;
vTmpId := tflag.next(vTmpId);
end loop;
end loop;
end if;
-- Взять статистику панелей
if GetFlag(CPMP1) or GetFlag(CPMP2) or GetFlag(CPMP3) then
for (select x(x.[QTIME]:qtime
,x.[F_UID]:fuid
,x.[F_P_CURR]:fpower -- Мощность заряда
) in ::[GA_MPPT_STAT] all
where x.[QTIME] >= vDBeg and x.[QTIME] <= vDEnd
order by x.[QTIME], x.[F_UID]
) loop
curts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour'));
case x.fuid of
:1: if GetFlag(CPMP1) then AddTrgData(CPMP1,curts,x.fpower); end if;
:2: if GetFlag(CPMP2) then AddTrgData(CPMP2,curts,x.fpower); end if;
:3: if GetFlag(CPMP3) then AddTrgData(CPMP3,curts,x.fpower); end if;
end;
end loop;
end if;
-- Взять значения мощностей из статистики допнагрузки
if GetFlag(CPADD) then
declare
tqend timestamp;
paend number;
begin
for (select x(
x.[QTIME]:qtime
, x.[FPOWER]:padd -- Доп. Нагрузка
) in ::[GA_LOAD_H] all
where x.[QTIME] >= (
select x(nvl(max(x.[QTIME]),to_timestamp('01.01.1970','dd.mm.yyyy')))
in ::[GA_LOAD_H] all
where x.[QTIME] < vDBeg
)
and x.[QTIME] < vDEnd
order by x.[QTIME]
) loop
curts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour'));
tqend := x.qtime;
paend := x.padd;
AddTrgData(CPADD,curts,x.padd);
end loop;
curts := TsToUTs(vDEnd - numtodsinterval(num_tz,'hour'));
AddTrgData(CPADD,curts,paend);
end;
end if;
-- Взять значения выработки по датам
if GetFlag(CEDAY) or GetFlag(CEMP1) or GetFlag(CEMP2) or GetFlag(CEMP3) or GetFlag(CEFRN) then
declare
vDEBeg date;
vDEEnd date;
vDECur date;
curEn number;
prven number;
vTSCur timestamp;
curEnToBat number;
curEnFromBat number;
procedure GetCeMp(vCeMp in varchar2, vMpUID in number) is
begin
vDECur := vDEBeg;
while vDECur <= vDEEnd loop
vTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy');
select x(nvl(max(x.[F_PWR_KW]),0)*1000) in ::[GA_MPPT_STAT] all
where x.[qtime] >= vTSCur
and x.[qtime] < (vTSCur + numtodsinterval(1,'day'))
and x.[F_TIMESTAMP] >= vDECur
and x.[F_TIMESTAMP] < (vDECur+1)
and x.[F_UID] = vMpUID
into curEn;
curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour'));
AddTrgData(vCeMp,curts,curen);
vDECur := vDECur + 1;
end loop;
end;
begin
vDEBeg := trunc(vDDBeg);
vDEEnd := trunc(vDDEnd);
if GetFlag(CEDAY) or GetFlag(CETOB) or GetFlag(CEFRB) then
vDECur := vDEBeg;
while vDECur <= vDEEnd loop
vTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy');
select x(
nvl(max(x.[S1].[F_MPPT_DAY_E]),0)
,nvl(max(x.[S1].[F_ESUM_TO_BAT]),0)
,nvl(max(x.[S1].[F_ESUM_FROM_BAT]),0)
) in ::[GA_BAT_STAT] all
where x.[qtime] >= vTSCur
and x.[qtime] < (vTSCur + numtodsinterval(1,'day'))
and x.[S1].[F_TIMESTAMP] >= vDECur
and x.[S1].[F_TIMESTAMP] < (vDECur+1)
into curEn,curEnToBat,curEnFromBat;
curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour'));
if GetFlag(CEDAY) then AddTrgData(CEDAY,curts,curen); end if;
if GetFlag(CETOB) then AddTrgData(CETOB,curts,curenToBat); end if;
if GetFlag(CEFRB) then AddTrgData(CEFRB,curts,curenFromBat); end if;
vDECur := vDECur + 1;
end loop;
end if;
if GetFlag(CEMP1) then
GetCeMp(CEMP1,1);
end if;
if GetFlag(CEMP2) then
GetCeMp(CEMP2,2);
end if;
if GetFlag(CEMP3) then
GetCeMp(CEMP3,3);
end if;
-- Посчитать сколько взято от сети
if GetFlag(CEFRN) then
vDECur := vDEBeg-1;
prven := null;
while vDECur <= vDEEnd loop
vTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy');
curen := 0;
for (
select x(x.[F__E_NET_B]*10:enet)
in ::[GA_MAP_STAT] all
where x.[qtime] >= vTSCur
and x.[qtime] < (vTSCur + numtodsinterval(1,'day'))
and x.[F_TIMESTAMP] >= vDECur
and x.[F_TIMESTAMP] < (vDECur+1)
order by x.[qtime] desc
) loop
curen := x.enet;
exit;
end loop;
if curen = 0 and prven != 0 then
curen := prven;
end if;
if prven is null then
prven := curen;
else
if prven = 0 then
prven := curen;
end if;
curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour'));
&debug('1. dcur = '||to_char(vDECur,'dd.mm.yyyy')||' prven = '||prven||' curen ='||curen||' diff='||to_char(curen - prven) )
AddTrgData(CEFRN,curts,curen - prven);
prven := curen;
end if;
vDECur := vDECur + 1;
end loop;
end if;
end;
end if;
-- Выгрузить собранные массивы ответ
vTmpId := tflag.first;
while vTmpId is not null loop
tflag(vTmpId).fOb.put('datapoints',tflag(vTmpId).fAr);
tflag(vTmpId).fAr := null;
pOut.append(tflag(vTmpId).fOb);
tflag(vTmpId).fOb := null;
vTmpId := tflag.next(vTmpId);
end loop;
end;
procedure get_search(pInp in out nocopy JSON_OBJECT_T, pOut in out nocopy JSON_ARRAY_T) is
vTarget varchar2(100);
begin
&debug('pInp='||pInp.to_string())
vTarget := trim(pInp.get_String('target'));
if vTarget is null then
pOut.Append(CPALL);
pOut.Append(CPNET);
pOut.Append(CPACB);
pOut.Append(CPI2C);
pOut.Append(CPADD);
pOut.Append(CPMP1);
pOut.Append(CPMP2);
pOut.Append(CPMP3);
pOut.Append(CEDAY);
pOut.Append(CEMP1);
pOut.Append(CEMP2);
pOut.Append(CEMP3);
pOut.Append(CETOB);
pOut.Append(CEFRB);
pOut.Append(CEFRN);
pOut.Append(CUNET);
pOut.Append(CUOUT);
pOut.Append(CUACB);
end if;
end;
public procedure GetJson(pPathInfo in varchar2, pInpPost in blob, pOutPost out blob) is
vInp JSON_OBJECT_T;
vOut JSON_ARRAY_T;
begin
vInp := JSON_OBJECT_T(pInpPost);
vOut := JSON_ARRAY_T();
&debug('pPathInfo='||pPathInfo)
-- Маршрутизация запроса в зависимости от pPathInfo
if pPathInfo = '/search' then
get_search(vInp, vOut);
elsif pPathInfo = '/query' then
get_query(vInp, vOut);
end if;
pOutPost := vOut.to_Blob;
end;
Возможно это кому-то окажется полезным :-)Вот такие результаты:
===========
Источник:
habr.com
===========
Похожие новости:
- [Информационная безопасность, Тестирование веб-сервисов] Атаки на JSON Web Tokens
- [Настройка Linux, Разработка на Raspberry Pi, Смартфоны, Умный дом] Делаем из старого усилителя многофункциональный медиа сервер с помощью Raspberry pi
- [Настройка Linux, Системное администрирование, Сетевые технологии] Установка NTP сервера для включения его в pool.ntp.org
- [PHP, Программирование] Язык программирования PHP 8: новый JIT-компилятор нацелен на лучшую производительность (перевод)
- [Настройка Linux, Разработка под Windows] В тестовой сборке Windows 10 появилась возможность автоматически выполнять команды Linux при запуске дистрибутивов в WSL
- [Тестирование IT-систем, Тестирование веб-сервисов, Тестирование мобильных приложений, Тестирование игр, Статистика в IT] Обзор рынка труда QA/QC в Москве
- [Habr] Ежегодный отчетный хабрапост — 2021
- [PHP, Symfony, Управление разработкой, Управление проектами] У Вас проблемы с legacy — значит, Вам повезло! Распил монолита на PHP
- [Настройка Linux, Разработка на Raspberry Pi] Пишем драйвер фреймбуфера для Raspberry Pi с LCD
- [Настройка Linux, Информационная безопасность, Open source, Виртуализация] Альтернативы VirtualBox для любителей приватности и свободы. Гипервизоры и менеджеры виртуальных машин. Часть I
Теги для поиска: #_nastrojka_linux (Настройка Linux), #_php, #_oracle, #_oracle, #_grafana, #_php, #_phpfpm, #_json, #_simple, #_pl/sql, #_plugin, #_grafiki (графики), #_statistika (статистика), #_nastrojka_linux (
Настройка Linux
), #_php, #_oracle
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 21:07
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Вводные. Зачем мне это было нужноЛично мне нужно было организовать мониторинг домашней солнечной электростанции.Кратко о матчасти (хотя этот пост не про неё):
$ sudo nano /etc/yum.repos.d/grafana.repo
[grafana] name=grafana baseurl=https://packages.grafana.com/oss/rpm repo_gpgcheck=1 enabled=1 gpgcheck=1 gpgkey=https://packages.grafana.com/gpg.key sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt dnf update
dnf install grafana systemctl daemon-reload systemctl enable --now grafana-server systemctl status grafana-server grafana-cli plugins install grafana-simple-json-datasource
systemctl restart grafana-server dnf install php-pecl-oci8
extension=oci8.so
env[ORACLE_HOSTNAME] = myserver.localdomain
env[ORACLE_UNQNAME] = mydb env[ORACLE_BASE] = /u01/app/oracle env[ORACLE_HOME] = /u01/app/oracle/product/18.4.0/dbhome_1 env[ORA_INVENTORY] = /u01/app/oraInventory env[ORACLE_SID] = mydb env[LD_LIBRARY_PATH] = /u01/app/oracle/product/18.4.0/dbhome_1/lib:/lib:/usr/lib env[NLS_LANG] = AMERICAN_CIS.UTF8 <?php
header("Content-Type: application/json;"); $conn = oci_pconnect('www', 'www$password', 'mydb', 'AL32UTF8'); if (!$conn) { $e = oci_error(); trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR); } // Подготовка выражения $stid = oci_parse($conn, 'begin LGRAFANA.GetJson(:vPath, :vInp, :vOut); end;'); if (!$stid) { $e = oci_error($conn); trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR); } // Создадим дескрипторы $vInp = oci_new_descriptor($conn, OCI_DTYPE_LOB); $vOut = oci_new_descriptor($conn, OCI_DTYPE_LOB); // Привяжем переменные $vPath = $_SERVER["PATH_INFO"]; $postdata = file_get_contents("php://input"); $vInp->writeTemporary($postdata, OCI_TEMP_BLOB); oci_bind_by_name($stid, ":vPath", $vPath); oci_bind_by_name($stid, ":vInp", $vInp, -1, OCI_B_BLOB); oci_bind_by_name($stid, ":vOut", $vOut, -1, OCI_B_BLOB); // Выполним логику запроса $r = oci_execute($stid); if (!$r) { $e = oci_error($stid); trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR); } echo $vOut->load(); $vInp ->close(); $vOut ->close(); oci_free_statement($stid); oci_commit($conn); oci_close($conn); ?> procedure GetJson(pPathInfo in varchar2, pInpPost in blob, pOutPost out blob);
Дальше можно идти добавлять DashBoard и накидывать туда панели с нужными графиками... Если у Вас уже есть реализация пакета LGRAFANA разумеется.Да кстати про пакет. Он у меня написан не совсем на PL/SQL, но в целом Вы сможете это использовать для того чтобы понять, что надо написать в своём пакете. Это легко переводится на pl/sql.Вкратце так:
pragma include([DEBUG_TRIGGER]::[MACRO_LIB]);
CPALL const varchar2(30) := 'Мощность нагр.'; CPNET const varchar2(30) := 'Мощность сеть'; CPACB const varchar2(30) := 'Мощность АКБ'; CPI2C const varchar2(30) := 'Мощность I2C'; CPADD const varchar2(30) := 'Доп. Нагрузка'; CPMP1 const varchar2(30) := 'Мощность MPPT1'; CPMP2 const varchar2(30) := 'Мощность MPPT2'; CPMP3 const varchar2(30) := 'Мощность MPPT3'; CEDAY const varchar2(30) := 'Выработка за день'; CEMP1 const varchar2(30) := 'Выработка MPPT1'; CEMP2 const varchar2(30) := 'Выработка MPPT2'; CEMP3 const varchar2(30) := 'Выработка MPPT3'; CETOB const varchar2(30) := 'На заряд батареи'; CEFRB const varchar2(30) := 'Взято от батареи'; CEFRN const varchar2(30) := 'Взято от сети'; CUNET const varchar2(30) := 'Напряжение сети'; CUOUT const varchar2(30) := 'Напряжение выход'; CUACB const varchar2(30) := 'Напряжение АКБ'; public function TsToUTs(v_Ts in timestamp) return number is v_Dt date; begin v_Dt := v_ts; return trunc((v_Dt - to_date('01.01.1970','DD.MM.YYYY')) -- Кол-во дней с 1 янв 1970 * (24 * 60 * 60)) -- Теперь это кол-во секунд * 1000 -- Теперь миллисекунд + to_number(to_char(v_ts,'FF3')); -- Добавили миллисекунды end; procedure get_query(pInp in out nocopy JSON_OBJECT_T, pOut in out nocopy JSON_ARRAY_T) is type rtflag is record ( fTp varchar2(30) ,fOb json_object_t ,fAr json_array_t ); type ttflag is table of rtflag index by string; tflag ttflag; vTmpOb json_object_t; vTmpAr json_array_t; vTmpId varchar2(30); vDBeg timestamp; vDEnd timestamp; vDDBeg date; vDDEnd date; num_tz number; curts number; function GetFlag(pFlagName in varchar2) return boolean is begin if tflag.exists(pFlagName) then return true; else return false; end if; end; --function GetFlagType(pFlagName in varchar2) return varchar2 is --begin -- if tflag.exists(pFlagName) then -- return tflag(pFlagName).fTp; -- else -- pragma error('Нет значения ['||pFlagName||'] в мвссиве tflag'); -- end if; --end; procedure AddTrgData(pTrgName in varchar2, pStamp in number, pValue in number) is begin vTmpAr := Json_Array_t; vTmpAr.append(pValue); vTmpAr.append(pStamp); tFlag(pTrgName).fAr.append(vTmpAr); end; begin &debug('pInp='||pInp.to_string()) vTmpOb := pInp.get_Object('range'); num_tz := to_number(::[GA_MAP_STAT].[LIB].GetSetting('MALINA_TIME_ZONE')); vDBeg := vTmpOb.get_Timestamp('from') + numtodsinterval(num_tz,'hour'); vDEnd := vTmpOb.get_Timestamp('to') + numtodsinterval(num_tz,'hour'); vDDBeg := to_date(to_char(vDBeg,'dd.mm.yyyy hh24:mi:ss'),'dd.mm.yyyy hh24:mi:ss'); vDDEnd := to_date(to_char(vDEnd,'dd.mm.yyyy hh24:mi:ss'),'dd.mm.yyyy hh24:mi:ss'); &debug('vDBeg='||to_char(vDBeg,'dd.mm.yyyy hh24:mi:ss:ff')) &debug('vDEnd='||to_char(vDEnd,'dd.mm.yyyy hh24:mi:ss:ff')) vTmpAr := pInp.get_Array('targets'); for i in 0 .. vTmpAr.get_size - 1 loop vTmpOb := JSON_OBJECT_T(vTmpAr.get(i)); vTmpId := vTmpOb.get_string('target'); tflag(vTmpId).fTp := vTmpOb.get_string('type'); tflag(vTmpId).fOb := Json_object_t; tflag(vTmpId).fAr := Json_array_t; tflag(vTmpId).fOb.put('target',vTmpId); end loop; -- Взять значения мощностей из статистики МАП if GetFlag(CPALL) or GetFlag(CPNET) or GetFlag(CPACB) or GetFlag(CPI2C) or GetFlag(CUNET) or GetFlag(CUOUT) or GetFlag(CUACB) then for (select x(x.[QTIME]:qtime , x.[F__PNET_CALC]:pnet -- Мощность сеть , - x.[F__PLOAD_CALC] + x.[F__PNET_CALC]:pall -- Мощность нагр. , - x.[F__PLOAD_CALC]:pacb -- Мощность АКБ , x.[F__P_MPPT_AVG]:pi2c -- Мощность I2C , x.[F__UNET]:unet , x.[F__UOUTMED]:uout , x.[F__UACC]:uacb ) in ::[GA_MAP_STAT] all where x.[QTIME] >= vDBeg and x.[QTIME] <= vDEnd order by x.[QTIME] ) loop curts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour')); vTmpId := tflag.first; while vTmpId is not null loop case vTmpId of :CPALL: AddTrgData(vTmpId,curts,x.pall); :CPNET: AddTrgData(vTmpId,curts,x.pnet); :CPACB: AddTrgData(vTmpId,curts,x.pacb); :CPI2C: AddTrgData(vTmpId,curts,x.pi2c); :CUNET: AddTrgData(vTmpId,curts,x.unet); :CUOUT: AddTrgData(vTmpId,curts,x.uout); :CUACB: AddTrgData(vTmpId,curts,x.uacb); end; vTmpId := tflag.next(vTmpId); end loop; end loop; end if; -- Взять статистику панелей if GetFlag(CPMP1) or GetFlag(CPMP2) or GetFlag(CPMP3) then for (select x(x.[QTIME]:qtime ,x.[F_UID]:fuid ,x.[F_P_CURR]:fpower -- Мощность заряда ) in ::[GA_MPPT_STAT] all where x.[QTIME] >= vDBeg and x.[QTIME] <= vDEnd order by x.[QTIME], x.[F_UID] ) loop curts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour')); case x.fuid of :1: if GetFlag(CPMP1) then AddTrgData(CPMP1,curts,x.fpower); end if; :2: if GetFlag(CPMP2) then AddTrgData(CPMP2,curts,x.fpower); end if; :3: if GetFlag(CPMP3) then AddTrgData(CPMP3,curts,x.fpower); end if; end; end loop; end if; -- Взять значения мощностей из статистики допнагрузки if GetFlag(CPADD) then declare tqend timestamp; paend number; begin for (select x( x.[QTIME]:qtime , x.[FPOWER]:padd -- Доп. Нагрузка ) in ::[GA_LOAD_H] all where x.[QTIME] >= ( select x(nvl(max(x.[QTIME]),to_timestamp('01.01.1970','dd.mm.yyyy'))) in ::[GA_LOAD_H] all where x.[QTIME] < vDBeg ) and x.[QTIME] < vDEnd order by x.[QTIME] ) loop curts := TsToUTs(x.qtime - numtodsinterval(num_tz,'hour')); tqend := x.qtime; paend := x.padd; AddTrgData(CPADD,curts,x.padd); end loop; curts := TsToUTs(vDEnd - numtodsinterval(num_tz,'hour')); AddTrgData(CPADD,curts,paend); end; end if; -- Взять значения выработки по датам if GetFlag(CEDAY) or GetFlag(CEMP1) or GetFlag(CEMP2) or GetFlag(CEMP3) or GetFlag(CEFRN) then declare vDEBeg date; vDEEnd date; vDECur date; curEn number; prven number; vTSCur timestamp; curEnToBat number; curEnFromBat number; procedure GetCeMp(vCeMp in varchar2, vMpUID in number) is begin vDECur := vDEBeg; while vDECur <= vDEEnd loop vTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy'); select x(nvl(max(x.[F_PWR_KW]),0)*1000) in ::[GA_MPPT_STAT] all where x.[qtime] >= vTSCur and x.[qtime] < (vTSCur + numtodsinterval(1,'day')) and x.[F_TIMESTAMP] >= vDECur and x.[F_TIMESTAMP] < (vDECur+1) and x.[F_UID] = vMpUID into curEn; curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour')); AddTrgData(vCeMp,curts,curen); vDECur := vDECur + 1; end loop; end; begin vDEBeg := trunc(vDDBeg); vDEEnd := trunc(vDDEnd); if GetFlag(CEDAY) or GetFlag(CETOB) or GetFlag(CEFRB) then vDECur := vDEBeg; while vDECur <= vDEEnd loop vTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy'); select x( nvl(max(x.[S1].[F_MPPT_DAY_E]),0) ,nvl(max(x.[S1].[F_ESUM_TO_BAT]),0) ,nvl(max(x.[S1].[F_ESUM_FROM_BAT]),0) ) in ::[GA_BAT_STAT] all where x.[qtime] >= vTSCur and x.[qtime] < (vTSCur + numtodsinterval(1,'day')) and x.[S1].[F_TIMESTAMP] >= vDECur and x.[S1].[F_TIMESTAMP] < (vDECur+1) into curEn,curEnToBat,curEnFromBat; curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour')); if GetFlag(CEDAY) then AddTrgData(CEDAY,curts,curen); end if; if GetFlag(CETOB) then AddTrgData(CETOB,curts,curenToBat); end if; if GetFlag(CEFRB) then AddTrgData(CEFRB,curts,curenFromBat); end if; vDECur := vDECur + 1; end loop; end if; if GetFlag(CEMP1) then GetCeMp(CEMP1,1); end if; if GetFlag(CEMP2) then GetCeMp(CEMP2,2); end if; if GetFlag(CEMP3) then GetCeMp(CEMP3,3); end if; -- Посчитать сколько взято от сети if GetFlag(CEFRN) then vDECur := vDEBeg-1; prven := null; while vDECur <= vDEEnd loop vTSCur := to_timestamp(to_char(vDECur,'dd.mm.yyyy'),'dd.mm.yyyy'); curen := 0; for ( select x(x.[F__E_NET_B]*10:enet) in ::[GA_MAP_STAT] all where x.[qtime] >= vTSCur and x.[qtime] < (vTSCur + numtodsinterval(1,'day')) and x.[F_TIMESTAMP] >= vDECur and x.[F_TIMESTAMP] < (vDECur+1) order by x.[qtime] desc ) loop curen := x.enet; exit; end loop; if curen = 0 and prven != 0 then curen := prven; end if; if prven is null then prven := curen; else if prven = 0 then prven := curen; end if; curts := TsToUTs(vTsCur - numtodsinterval(num_tz,'hour')); &debug('1. dcur = '||to_char(vDECur,'dd.mm.yyyy')||' prven = '||prven||' curen ='||curen||' diff='||to_char(curen - prven) ) AddTrgData(CEFRN,curts,curen - prven); prven := curen; end if; vDECur := vDECur + 1; end loop; end if; end; end if; -- Выгрузить собранные массивы ответ vTmpId := tflag.first; while vTmpId is not null loop tflag(vTmpId).fOb.put('datapoints',tflag(vTmpId).fAr); tflag(vTmpId).fAr := null; pOut.append(tflag(vTmpId).fOb); tflag(vTmpId).fOb := null; vTmpId := tflag.next(vTmpId); end loop; end; procedure get_search(pInp in out nocopy JSON_OBJECT_T, pOut in out nocopy JSON_ARRAY_T) is vTarget varchar2(100); begin &debug('pInp='||pInp.to_string()) vTarget := trim(pInp.get_String('target')); if vTarget is null then pOut.Append(CPALL); pOut.Append(CPNET); pOut.Append(CPACB); pOut.Append(CPI2C); pOut.Append(CPADD); pOut.Append(CPMP1); pOut.Append(CPMP2); pOut.Append(CPMP3); pOut.Append(CEDAY); pOut.Append(CEMP1); pOut.Append(CEMP2); pOut.Append(CEMP3); pOut.Append(CETOB); pOut.Append(CEFRB); pOut.Append(CEFRN); pOut.Append(CUNET); pOut.Append(CUOUT); pOut.Append(CUACB); end if; end; public procedure GetJson(pPathInfo in varchar2, pInpPost in blob, pOutPost out blob) is vInp JSON_OBJECT_T; vOut JSON_ARRAY_T; begin vInp := JSON_OBJECT_T(pInpPost); vOut := JSON_ARRAY_T(); &debug('pPathInfo='||pPathInfo) -- Маршрутизация запроса в зависимости от pPathInfo if pPathInfo = '/search' then get_search(vInp, vOut); elsif pPathInfo = '/query' then get_query(vInp, vOut); end if; pOutPost := vOut.to_Blob; end; =========== Источник: habr.com =========== Похожие новости:
Настройка Linux ), #_php, #_oracle |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 21:07
Часовой пояс: UTC + 5