From 55519e072eceb21bd820ee56d1dad26e7fd11083 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Fri, 11 Apr 2025 15:58:40 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B0=D0=BB=D0=B3=D0=BE=D1=80=D0=B8=D1=82=D0=BC?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D1=87=D0=B5=D1=82=D0=B0=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BE=D1=8F=D1=82=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=B2?= =?UTF-8?q?=D1=8B=D1=85=D0=BE=D0=B4=D0=B0=20=D0=B8=D0=B7=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D1=8F,=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=82=D0=B5=D1=85=D0=BD=D0=B8=D1=87=D0=B5=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B0=20(=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BE=20=D0=BF=D1=80=D0=BE=D0=B3?= =?UTF-8?q?=D0=BD=D0=BE=D0=B7=D0=B5)=20=D0=B8=20=D0=BA=D0=B0=D1=80=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BA=D0=B0=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20(=D0=B4?= =?UTF-8?q?=D0=B0=D1=82=D1=8B=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=D0=B0=20?= =?UTF-8?q?=D0=B8=D0=B7=20=D1=81=D1=82=D1=80=D0=BE=D1=8F=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=81=D0=B5=D0=B3=D0=BE=D0=B4=D0=BD=D1=8F=D1=88?= =?UTF-8?q?=D0=BD=D0=B5=D0=B3=D0=BE=20=D0=B4=D0=BD=D1=8F,=20=D0=B1=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D0=B8=D0=B5=D0=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/UDO_PKG_EQUIPDS.pck | 96 ++++++++++++------- db/UDO_PKG_EQUIPDS_BASE.pck | 54 ++++++++--- db/UDO_PKG_EQUIPTCF.pck | 42 ++++---- panels/eqs_tech_cond_forecast/forecast_tab.js | 95 +++++++++--------- .../forecast_tab_layout.js | 64 +++++++++---- 5 files changed, 221 insertions(+), 130 deletions(-) diff --git a/db/UDO_PKG_EQUIPDS.pck b/db/UDO_PKG_EQUIPDS.pck index ecb4dee..ff636a9 100644 --- a/db/UDO_PKG_EQUIPDS.pck +++ b/db/UDO_PKG_EQUIPDS.pck @@ -205,9 +205,10 @@ create or replace package UDO_PKG_EQUIPDS as DFORECAST_DATE in date, -- Дата получения прогноза NFORECAST_DAYS in number, -- Прогнозное количество дней до выхода из строя (на дату прогноза) STASK in varchar2, -- Задача (см. константы UDO_PKG_EQUIPDS_BASE.CMML_TASK_*) - SFORECAST_DESC out varchar2, -- Краткое описание прогноза - SFORECAST_DESC_COLOR out varchar2, -- Цвет заливки прогноза - CFORECAST out clob -- Данные детальной карточки прогноза + SFORECAST out varchar2, -- Прогноз + SFORECAST_DESC out varchar2, -- Интерпретация прогноза + SFORECAST_DESC_COLOR out varchar2, -- Цвет заливки интерпретации прогноза + CFORECAST_CARD out clob -- Данные детальной карточки прогноза ); /* Список "Выборки данных оборудования (классы оборудования, модели, история запросов)" по единице оборудования */ @@ -1544,9 +1545,10 @@ create or replace package body UDO_PKG_EQUIPDS as DFORECAST_DATE in date, -- Дата получения прогноза NFORECAST_DAYS in number, -- Прогнозное количество дней до выхода из строя (на дату прогноза) STASK in varchar2, -- Задача (см. константы UDO_PKG_EQUIPDS_BASE.CMML_TASK_*) - SFORECAST_DESC out varchar2, -- Краткое описание прогноза - SFORECAST_DESC_COLOR out varchar2, -- Цвет заливки прогноза - CFORECAST out clob -- Данные детальной карточки прогноза + SFORECAST out varchar2, -- Прогноз + SFORECAST_DESC out varchar2, -- Интерпретация прогноза + SFORECAST_DESC_COLOR out varchar2, -- Цвет заливки интерпретации прогноза + CFORECAST_CARD out clob -- Данные детальной карточки прогноза ) is RCMMLH UDO_T_EQUIPDSCMMLH%rowtype; -- Запись истории запросов модели @@ -1560,16 +1562,21 @@ create or replace package body UDO_PKG_EQUIPDS as NBREAKDOWN_PROB_CUR PKG_STD.TNUMBER; -- Вероятность выхода из строя (для текущей позиции графика ТО и ремонтов/рем. ведомости) DNEXT_REPAIR PKG_STD.TLDATE; -- Дата ближайшего ТО и ремонта по графику/рем. ведомости NFORECAST_DAYS_NOW PKG_STD.TNUMBER; -- Количество дней до выхода из строя согласно прогнозу, но от текущей даты + STASK_NAME PKG_STD.TSTRING; -- Текстовая расшифровка задачи + SFORECAST_DATE_CALC PKG_STD.TSTRING; -- Текстовое представление плановой даты перехода в предельное состояние begin /* Считаем запись истории запросов к модели */ RCMMLH := UDO_PKG_EQUIPDS_BASE.CMMLH_GET(NFLAG_SMART => 0, NRN => NEQUIPDSCMMLH); /* Проноз есть */ if ((DFORECAST_DATE is not null) and (NFORECAST_DAYS is not null)) then + /* Получим текстовую расшифровку задачи */ + STASK_NAME := UDO_PKG_EQUIPDS_BASE.CMML_TASK_NAME(STASK => STASK); + SFORECAST_DATE_CALC := TO_CHAR(DFORECAST_DATE + NFORECAST_DAYS, 'dd.mm.yyyy'); /* Вычислим сколько осталось до выхода из строя от текущей даты */ NFORECAST_DAYS_NOW := UDO_PKG_EQUIPDS_BASE.CMMLH_FORECAST_DAYS_NOW(DFORECAST_DATE => DFORECAST_DATE, NFORECAST_DAYS => NFORECAST_DAYS); /* Вычислим вероятность выхода из строя до даты ближайшего ТО/ремонта по графику или рем. ведомости с учётом прогноза */ - DNEXT_REPAIR := UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_GEN_NXTRPR(NEQCONFIG => RCMMLH.EQCONFIG); + DNEXT_REPAIR := UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_GEN_NXTRPR(NEQCONFIG => RCMMLH.EQCONFIG); NBREAKDOWN_PROB := UDO_PKG_EQUIPDS_BASE.CMMLH_RUL_BREAKDOWN_PROB(DFORECAST_DATE => DFORECAST_DATE, NFORECAST_DAYS => NFORECAST_DAYS, DDATE => DNEXT_REPAIR); @@ -1596,9 +1603,7 @@ create or replace package body UDO_PKG_EQUIPDS as RNODE01 => PKG_XMAKE.ELEMENT(ICURSOR => NCUR, SNAME => 'SFORECAST_DATE_CALC', RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => NCUR, - SVALUE => TO_CHAR(DFORECAST_DATE + - NFORECAST_DAYS, - 'dd.mm.yyyy')))); + SVALUE => SFORECAST_DATE_CALC))); /* Заполним текущую дату */ XROOT := PKG_XMAKE.CONCAT(ICURSOR => NCUR, RNODE00 => XROOT, @@ -1626,7 +1631,7 @@ create or replace package body UDO_PKG_EQUIPDS as RNODE01 => PKG_XMAKE.ELEMENT(ICURSOR => NCUR, SNAME => 'STASK_NAME', RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => NCUR, - SVALUE => UDO_PKG_EQUIPDS_BASE.CMML_TASK_NAME(STASK => STASK)))); + SVALUE => STASK_NAME))); /* Заполним дату следующего ТО и ремонта */ XROOT := PKG_XMAKE.CONCAT(ICURSOR => NCUR, RNODE00 => XROOT, @@ -1675,6 +1680,9 @@ create or replace package body UDO_PKG_EQUIPDS as PKG_P8PANELS_VISUAL.TCHART_ADD_DATASET(RCHART => RCH, RDATASET => RCH_DS); /* Сериализуем график */ CCHART := PKG_P8PANELS_VISUAL.TCHART_TO_XML(RCHART => RCH, NINCLUDE_DEF => 1); + else + /* Запланированных ремонтов или ТО нет, но прогноз есть - значит точно выйдет из строя */ + NBREAKDOWN_PROB := 100; end if; /* Заполним вероятность выхода из стороя до ближайшего ТО с учётом прогноза */ XROOT := PKG_XMAKE.CONCAT(ICURSOR => NCUR, @@ -1699,19 +1707,24 @@ create or replace package body UDO_PKG_EQUIPDS as RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => NCUR, LCVALUE => CCHART))); /* Формируем XML-представление ответа */ XDOC := PKG_XMAKE.ELEMENT(ICURSOR => NCUR, SNAME => 'XDATA', RNODE00 => XROOT); - /* Конвертируем в CLOB */ - CFORECAST := PKG_XMAKE.SERIALIZE_TO_CLOB(ICURSOR => NCUR, - ITYPE => PKG_XMAKE.CONTENT_, - RNODE => XDOC, - RHEADER => PKG_XHEADER.WRAP_ALL(SVERSION => PKG_XHEADER.VERSION_1_0_, - SENCODING => PKG_XHEADER.ENCODING_UTF_, - SSTANDALONE => PKG_XHEADER.STANDALONE_YES_)); + /* Возвращаем полученные данные карточки - конвертируем XML-представление ответа в CLOB */ + CFORECAST_CARD := PKG_XMAKE.SERIALIZE_TO_CLOB(ICURSOR => NCUR, + ITYPE => PKG_XMAKE.CONTENT_, + RNODE => XDOC, + RHEADER => PKG_XHEADER.WRAP_ALL(SVERSION => PKG_XHEADER.VERSION_1_0_, + SENCODING => PKG_XHEADER.ENCODING_UTF_, + SSTANDALONE => PKG_XHEADER.STANDALONE_YES_)); /* Закрываем документ */ PKG_XMAKE.CLOSE_CURSOR(ICURSOR => NCUR); - /* Возвращаем значение краткого описания прогноза */ + /* Возвращаем текстовое описание прогноза */ + SFORECAST := STASK_NAME || ' на дату выборки (' || TO_CHAR(DFORECAST_DATE, 'dd.mm.yyyy') || '): ' || + TO_CHAR(NFORECAST_DAYS) || 'д (до ' || SFORECAST_DATE_CALC || ')'; + /* Возвращаем интерпретацию прогноза */ SFORECAST_DESC := TO_CHAR(NFORECAST_DAYS_NOW) || 'Д / ' || TO_CHAR(ROUND(NBREAKDOWN_PROB)) || '%'; - /* Возвращаем значение цвета заливки описания прогноза */ + /* Возвращаем цвета заливки интерпретации прогноза */ SFORECAST_DESC_COLOR := CMMLH_FORECAST_COLOR(NMODE => 1, NVALUE => NBREAKDOWN_PROB); + else + SFORECAST := 'Не удалось разобрать ответ модели'; end if; end CMMLH_FORECAST_CARD; @@ -1744,7 +1757,8 @@ create or replace package body UDO_PKG_EQUIPDS as PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, SNAME => 'SEQUIPDS_CODE', SCAPTION => 'Выборка', - SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, SNAME => 'SEQUIPDSCMML_TASK', SCAPTION => 'Задача', @@ -1753,22 +1767,27 @@ create or replace package body UDO_PKG_EQUIPDS as PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, SNAME => 'NEQUIPDSCMML_PRECISION', SCAPTION => 'Точность', - SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); - PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, - SNAME => 'STO_FORECAST_DESC', - SCAPTION => 'Прогноз', - SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, - SHINT => 'Формат прогноза: "XXД / YY%", где
' || - 'XXД - время (в днях, от текущей даты) до перехода в критическое состояние
' || - 'YY% - вероятность перехода в критическое состояние до следующего ТО и ремонта

' || - 'Цвет прогноза:
' || CMMLH_FORECAST_COLOR(NMODE => 0)); - PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, - SNAME => 'STO_FORECAST_DESC_COLOR', - SCAPTION => 'Цвет прогноза', - SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, BVISIBLE => false); PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, SNAME => 'STO_FORECAST', + SCAPTION => 'Ответ модели', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STO_FORECAST_DESC', + SCAPTION => 'Интерпретация', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + SHINT => 'Формат: "XXД / YY%", где
' || + 'XXД - время (в днях, от текущей даты) до перехода в критическое состояние
' || + 'YY% - вероятность перехода в критическое состояние до следующего ТО и ремонта

' || + 'Цвет:
' || CMMLH_FORECAST_COLOR(NMODE => 0)); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STO_FORECAST_DESC_COLOR', + SCAPTION => 'Цвет интерпретации', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDG_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STO_FORECAST_CARD', SCAPTION => 'Данные карточки прогноза для конкретного технического объекта', SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, BVISIBLE => false); @@ -1789,9 +1808,10 @@ create or replace package body UDO_PKG_EQUIPDS as T.FORECAST BFORECAST, T.FORECAST_DATE DFORECAST_DATE, T.FORECAST_DAYS NFORECAST_DAYS, + null STO_FORECAST, null STO_FORECAST_DESC, null STO_FORECAST_DESC_COLOR, - null STO_FORECAST + null STO_FORECAST_CARD from UDO_T_EQUIPDSCMMLH T, UDO_T_EQUIPDSCMML ML, UDO_T_EQUIPDSCM CM, @@ -1809,9 +1829,10 @@ create or replace package body UDO_PKG_EQUIPDS as DFORECAST_DATE => C.DFORECAST_DATE, NFORECAST_DAYS => C.NFORECAST_DAYS, STASK => C.SEQUIPDSCMML_TASK, + SFORECAST => C.STO_FORECAST, SFORECAST_DESC => C.STO_FORECAST_DESC, SFORECAST_DESC_COLOR => C.STO_FORECAST_DESC_COLOR, - CFORECAST => C.STO_FORECAST); + CFORECAST_CARD => C.STO_FORECAST_CARD); /* Добавляем колонки с данными */ PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NRN', NVALUE => C.NRN, BCLEAR => true); PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SRQ_AUTHID', SVALUE => C.SRQ_AUTHID); @@ -1821,11 +1842,12 @@ create or replace package body UDO_PKG_EQUIPDS as PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NEQUIPDSCMML_PRECISION', NVALUE => C.NEQUIPDSCMML_PRECISION); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STO_FORECAST', SVALUE => C.STO_FORECAST); PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STO_FORECAST_DESC', SVALUE => C.STO_FORECAST_DESC); PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STO_FORECAST_DESC_COLOR', SVALUE => C.STO_FORECAST_DESC_COLOR); - PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STO_FORECAST', SVALUE => C.STO_FORECAST); + PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STO_FORECAST_CARD', SVALUE => C.STO_FORECAST_CARD); PKG_P8PANELS_VISUAL.TDG_ROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SDICMUNTS', SVALUE => C.SDICMUNTS); /* Добавляем строку в таблицу */ PKG_P8PANELS_VISUAL.TDG_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); diff --git a/db/UDO_PKG_EQUIPDS_BASE.pck b/db/UDO_PKG_EQUIPDS_BASE.pck index d650bdf..c539ad7 100644 --- a/db/UDO_PKG_EQUIPDS_BASE.pck +++ b/db/UDO_PKG_EQUIPDS_BASE.pck @@ -1428,7 +1428,26 @@ create or replace package body UDO_PKG_EQUIPDS_BASE as return NRES; end CMMLH_FORECAST_DAYS_NOW; - /* Вычисление вероятности выхода из строя на дату по RUL-прогнозу */ + /* Вычисление вероятности выхода из строя на дату по RUL-прогнозу */ + /* + Описание алгоритма: + + Считаем, что остаточный ресурс "k" - случайная величина, имеющая равномерную функцию распределения на интервале [a, b] + Необходимо определить вероятность того, что остаточный ресурс составит не менее "m" дней (P(k>=m)). + + Здесь: + "k" - остаточный ресурс + "m" - количество дней, от текущего, до проверяемой даты (DDATE) + "a" - текущий день, 0 + "b" - количество дней от текущего, до даты выхода из строя (DFORECAST_DATE + NFORECAST_DAYS), спрогнозированной фреймворком + + Таким образом: + - вычисления невозможны, если хоть один из входных параметров неопределён, будем возвращать null + - если дата выхода из строя (DFORECAST_DATE + NFORECAST_DAYS) меньше текущей (т.е. b < a и интервал распределения неопределён), то считаем, что ресурс гарантировано меньше (P(k>=m) = 1) + - если проверяемая дата (DDATE) меньше текущей (т.е. не левее [a, b]) - вероятность 0, ресурс гарантировано больше (P(k>=m) = 0) + - если проверяемая дата (DDATE) больше даты выхода из строя (т.е. правее [a, b]) - вероятность 1, ресурс гарантировано меньше (P(k>=m) = 1) + - если проверяемая дата (DDATE) в диапазоне от текущей, до предсказанной даты выхода из строя (в [a, b]), то P(k>=m) = 1 - ((b - m)/(b - a)) + */ function CMMLH_RUL_BREAKDOWN_PROB ( DFORECAST_DATE in date, -- Дата прогноза @@ -1436,26 +1455,33 @@ create or replace package body UDO_PKG_EQUIPDS_BASE as DDATE in date -- Дата на ) return number -- Значение вероятности is + DFORECAST_BREAKDOWN PKG_STD.TLDATE; -- Прогнозная дата выхода из строя + NB PKG_STD.TNUMBER; -- Правая граница диапазона распределения вероятности (количество дней от текущего до прогнозной даты выхода из строя) + NM PKG_STD.TNUMBER; -- Проверяемый аргумент (количество дней от текущего до проверяемой даты) NRES PKG_STD.TNUMBER; -- Буфер для результата begin - /* Проверим параметры */ - if ((NFORECAST_DAYS is null) or (DFORECAST_DATE is null) or (DDATE is null) or (DDATE < DFORECAST_DATE) or - (DFORECAST_DATE > sysdate) or (DDATE < sysdate)) then + /* Проверим параметры - должны быть заданы все параметры */ + if ((NFORECAST_DAYS is null) or (DFORECAST_DATE is null) or (DDATE is null)) then return null; end if; - /* Проверяем пограничные значения */ - if ((NFORECAST_DAYS = 0) or ((DDATE - sysdate) = 0)) then + /* Вычислим дату выхода из строя */ + DFORECAST_BREAKDOWN := DFORECAST_DATE + NFORECAST_DAYS; + /* Если прогнозируемая дата выхода из строя меньше текущей - ресурс гарантировано ниже */ + if (DFORECAST_BREAKDOWN < sysdate) then + return 100; + end if; + /* Если проверяемя дата меньше текущей - ресурс гарантировано выше */ + if (DDATE < sysdate) then + return 0; + end if; + /* Если проверяемя дата больше прогнозной дты выхода из строя - ресурс гарантировано ниже */ + if (DDATE > DFORECAST_BREAKDOWN) then return 100; end if; /* Вычисляем */ - NRES := 100 - ROUND((NFORECAST_DAYS - (TRUNC(sysdate) - TRUNC(DFORECAST_DATE))) / (DDATE - sysdate) * 100); - /* Корректируем флуктуации */ - if (NRES > 100) then - NRES := 100; - end if; - if (NRES < 0) then - NRES := 0; - end if; + NB := DFORECAST_BREAKDOWN - sysdate; + NM := DDATE - sysdate; + NRES := 100 - ROUND(((NB - NM) / NB) * 100); /* Возвращаем результат */ return NRES; end CMMLH_RUL_BREAKDOWN_PROB; diff --git a/db/UDO_PKG_EQUIPTCF.pck b/db/UDO_PKG_EQUIPTCF.pck index 81685db..ec47029 100644 --- a/db/UDO_PKG_EQUIPTCF.pck +++ b/db/UDO_PKG_EQUIPTCF.pck @@ -962,7 +962,7 @@ text="Проверка прав доступа при формировании /* Читаем последнее значение из массива предсказаний (оно соответствует дате окончания из параметров прогноза) */ NLAST := PKG_XPATH.COUNT_NODES(RNODES => XFCTS); XLAST_FCT := PKG_XPATH.ITEM_NODE(RNODES => XFCTS, INUMBER => NLAST); - NLAST_INTERVALS := TO_CHAR(PKG_XPATH.VALUE(RNODE => XLAST_FCT, SPATTERN => 'SVALUE')); + NLAST_INTERVALS := PKG_XPATH.VALUE_NUM(RNODE => XLAST_FCT, SPATTERN => 'VALUE'); if (NLAST_INTERVALS < 0) then NLAST_INTERVALS := 0; end if; @@ -1007,27 +1007,31 @@ text="Проверка прав доступа при формировании begin /* Определим дату ближайшего ТО или ремонта */ DCALC_DATE := EQCONFIG_THOBJ_GEN_NXTRPR(NEQCONFIG => NEQCONFIG); - /* Если дата есть */ - if (DCALC_DATE is not null) then - /* Вынем самый свежий прогноз */ - for C in (select T.FORECAST_DATE DFORECAST_DATE, - T.FORECAST_DAYS NFORECAST_DAYS - from UDO_T_EQUIPDSCMMLH T, - UDO_T_EQUIPDSCMML CMML - where T.EQCONFIG = NEQCONFIG - and T.PRN = CMML.RN - and CMML.TASK = UDO_PKG_EQUIPDS_BASE.SCMML_TASK_RUL - order by T.RQ_DATE desc) - loop - DFORECAST_DATE := C.DFORECAST_DATE; - NFORECAST_DAYS := C.NFORECAST_DAYS; - exit; - end loop; - /* Если есть и прогноз */ - if (NFORECAST_DAYS is not null) then + /* Вынем самый свежий прогноз */ + for C in (select T.FORECAST_DATE DFORECAST_DATE, + T.FORECAST_DAYS NFORECAST_DAYS + from UDO_T_EQUIPDSCMMLH T, + UDO_T_EQUIPDSCMML CMML + where T.EQCONFIG = NEQCONFIG + and T.PRN = CMML.RN + and CMML.TASK = UDO_PKG_EQUIPDS_BASE.SCMML_TASK_RUL + order by T.RQ_DATE desc) + loop + DFORECAST_DATE := C.DFORECAST_DATE; + NFORECAST_DAYS := C.NFORECAST_DAYS; + exit; + end loop; + /* Если есть прогноз */ + if ((DFORECAST_DATE is not null) and (NFORECAST_DAYS is not null)) then + /* Если и дата ТО есть */ + if (DCALC_DATE is not null) then + /* Вычислим вероятность */ NRES := UDO_PKG_EQUIPDS_BASE.CMMLH_RUL_BREAKDOWN_PROB(DFORECAST_DATE => DFORECAST_DATE, NFORECAST_DAYS => NFORECAST_DAYS, DDATE => DCALC_DATE); + else + /* Запланированных ремонтов или ТО нет, но прогноз есть - значит точно выйдет из строя */ + NRES := 100; end if; end if; /* Вернём то, что собрали */ diff --git a/panels/eqs_tech_cond_forecast/forecast_tab.js b/panels/eqs_tech_cond_forecast/forecast_tab.js index cd52878..b02da01 100644 --- a/panels/eqs_tech_cond_forecast/forecast_tab.js +++ b/panels/eqs_tech_cond_forecast/forecast_tab.js @@ -58,7 +58,7 @@ const TECH_OBJ_SPEC_INITIAL = { }; //Начальное состояние выбранного технического объекта -const TECH_OBJ_INITIAL = { id: 20005565, objKind: null, measureUnit: null }; +const TECH_OBJ_INITIAL = { id: null, objKind: null, measureUnit: null }; //Стили const STYLES = { @@ -224,50 +224,57 @@ const ForecastTab = ({ onGoToAdmin }) => { }; //Выполняем запрос showLoader("Прогнозирование..."); - const response = await fetch(forecastReqData.SURL, { - method: "POST", - body: JSON.stringify(body), - headers: { - Authorization: `Basic ${forecastReqData.SAUTH_TOKEN}`, - Accept: "*/*", - "Content-Type": "application/json" - } - }); + let response = null; + try { + response = await fetch(forecastReqData.SURL, { + method: "POST", + body: JSON.stringify(body), + headers: { + Authorization: `Basic ${forecastReqData.SAUTH_TOKEN}`, + Accept: "*/*", + "Content-Type": "application/json" + } + }); + } catch (e) { + throw new Error("Сетевая ошибка или ошибка внешнего сервера."); + } //Обработаем результаты - if (response.status === 200) { - let responseJson; - try { - responseJson = await response.json(); - } catch (e) { - throw new Error("Неожиданный ответ системы прогнозирования: ответ неверного формата."); - } - if (responseJson) { - if (responseJson?.status && responseJson.status == "ERR") - throw new Error(`Ошибка формирования прогноза: ${responseJson.message}`); - if (responseJson?.forecast && Array.isArray(responseJson?.forecast)) { - await executeStored({ - stored: "UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_FORECAST_EPLG", - args: { - NDATASET_IDENT: res.outParameters.NDATASET_IDENT, - NDATASET_CONFIG_IDENT: res.outParameters.NDATASET_CONFIG_IDENT, - NREQUEST_CONFIG_IDENT: res.outParameters.NREQUEST_CONFIG_IDENT, - NEQCONFIG: techObj.id, - NEQUIPDSCMML: model.NRN, - CFORECAST: { - VALUE: object2Base64XML( - { XDATA: { XFORECAST: responseJson.forecast.map(f => ({ STIME: f.time, SVALUE: f.value })) } }, - { arrayNodeName: "XFORECAST" } - ), - SDATA_TYPE: SERV_DATA_TYPE_CLOB - } - }, - loader: false - }); - hideLoader(); - setRefresh(pv => ({ ...pv, techObjForecastHistList: pv.techObjForecastHistList + 1 })); - } else throw new Error("Неожиданный ответ системы прогнозирования: ответ не содержит данных прогноза."); - } else throw new Error("Неожиданный ответ системы прогнозирования: ответ не содержит данных."); - } else throw new Error(`Ошибка (${response.status}) при взаимодейтсвии с системой прогнозирования.`); + if (response) { + if (response?.status === 200) { + let responseJson; + try { + responseJson = await response.json(); + } catch (e) { + throw new Error("Неожиданный ответ системы прогнозирования: ответ неверного формата."); + } + if (responseJson) { + if (responseJson?.status && responseJson.status == "ERR") + throw new Error(`Ошибка формирования прогноза: ${responseJson.message}`); + if (responseJson?.forecast && Array.isArray(responseJson?.forecast)) { + await executeStored({ + stored: "UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_FORECAST_EPLG", + args: { + NDATASET_IDENT: res.outParameters.NDATASET_IDENT, + NDATASET_CONFIG_IDENT: res.outParameters.NDATASET_CONFIG_IDENT, + NREQUEST_CONFIG_IDENT: res.outParameters.NREQUEST_CONFIG_IDENT, + NEQCONFIG: techObj.id, + NEQUIPDSCMML: model.NRN, + CFORECAST: { + VALUE: object2Base64XML( + { XDATA: { XFORECAST: responseJson.forecast.map(f => ({ STIME: f.time, VALUE: f.value })) } }, + { arrayNodeName: "XFORECAST" } + ), + SDATA_TYPE: SERV_DATA_TYPE_CLOB + } + }, + loader: false + }); + hideLoader(); + setRefresh(pv => ({ ...pv, techObjForecastHistList: pv.techObjForecastHistList + 1 })); + } else throw new Error("Неожиданный ответ системы прогнозирования: ответ не содержит данных прогноза."); + } else throw new Error("Неожиданный ответ системы прогнозирования: ответ не содержит данных."); + } else throw new Error(`Ошибка (${response.status}) при взаимодейтсвии с системой прогнозирования.`); + } else throw new Error("Система прогнозирования не вернула ответ."); } catch (e) { hideLoader(); showMsgErr(e.message); diff --git a/panels/eqs_tech_cond_forecast/forecast_tab_layout.js b/panels/eqs_tech_cond_forecast/forecast_tab_layout.js index e167ce4..6717e13 100644 --- a/panels/eqs_tech_cond_forecast/forecast_tab_layout.js +++ b/panels/eqs_tech_cond_forecast/forecast_tab_layout.js @@ -63,7 +63,7 @@ const STYLES = { ...SCROLL_STYLES }, TECH_OBJ_FORECAST_DETAIL_DIALOG: { maxWidth: "600px" }, - TECH_OBJ_FORECAST_DETAIL_CHART: { width: "550px", display: "flex", justifyContent: "center", paddingTop: "20px" }, + TECH_OBJ_FORECAST_DETAIL_CHART: { display: "flex", alignItems: "center", justifyContent: "center", paddingTop: "20px" }, TECH_OBJ_MAKE_DATASET_DIALOG_CONTENT: { minHeight: "40vh", maxHeight: "40vh", ...SCROLL_STYLES }, TECH_OBJ_MAKE_DATASET_DIALOG_TABS_CONTAINER: { borderBottom: 1, borderColor: "divider" }, FORECAST_DETAIL_NOTE: { display: "block", marginTop: "-5px" } @@ -130,8 +130,6 @@ const techObjCardForecastListTableHeadCellRender = ({ columnDef }) => { //Форматирование колонок таблицы истории прогнозов класса оборудования выборки данных const techObjCardForecastListTableDataCellRender = ({ row, columnDef, onShowForecastDetail }) => { switch (columnDef.name) { - case "NEQUIPDSCMML_PRECISION": - return { data: `${row.NEQUIPDSCMML_PRECISION} ${row.SDICMUNTS}` }; case "STO_FORECAST_DESC": return { cellProps: { align: "right" }, @@ -147,7 +145,17 @@ const techObjCardForecastListTableDataCellRender = ({ row, columnDef, onShowFore //Детали прогноза const ForecastDetail = ({ date, forecast, onClose }) => { //Собственное состояние - сведения о прогнозе - const [datails, setDetails] = useState({ taskName: null, forecast: null, meas: null, nextRepair: null, breakDown: null, breakDownColor: null }); + const [details, setDetails] = useState({ + taskName: null, + forecastDate: null, + forecastDays: null, + forecastDateCalc: null, + forecastDateNow: null, + forecastDaysNow: null, + nextRepair: null, + breakDown: null, + breakDownColor: null + }); //Собственное состояние - график const [chart, setChart] = useState({ loaded: false, available: true, labels: [], datasets: [] }); @@ -158,8 +166,11 @@ const ForecastDetail = ({ date, forecast, onClose }) => { const data = await xml2JSON({ xmlDoc: forecast }); setDetails({ taskName: data.XDATA.STASK_NAME, - forecast: data.XDATA.NFORECAST, - meas: data.XDATA.SDICMUNTS, + forecastDate: data.XDATA.SFORECAST_DATE, + forecastDays: data.XDATA.NFORECAST_DAYS, + forecastDateCalc: data.XDATA.SFORECAST_DATE_CALC, + forecastDateNow: data.XDATA.SFORECAST_DATE_NOW, + forecastDaysNow: data.XDATA.NFORECAST_DAYS_NOW, nextRepair: data.XDATA.DNEXT_REPAIR, breakDown: data.XDATA.NBREAKDOWN_PROB, breakDownColor: data.XDATA.SBREAKDOWN_PROB_COLOR @@ -179,30 +190,51 @@ const ForecastDetail = ({ date, forecast, onClose }) => { {`Детали прогноза от ${date}`} - {datails.taskName}: {`${datails.forecast} ${datails.meas}`} + {details.taskName} на дату выборки ({`${details.forecastDate}`}):{" "} + {`${details.forecastDays} д (до ${details.forecastDateCalc})*`} + + + *по данным фреймворка прогнозирования + + + {details.taskName} на сегодня ({`${details.forecastDateNow}`}): {`${details.forecastDaysNow} д*`} + + + *по данным фреймворка прогнозирования Дата следующего ТО/ремонта:  - - {datails.nextRepair ? `${formatDateRF(datails.nextRepair)}*` : "Нет графиков ТО и ремонтов/ремонтых ведомостей"} + + {details.nextRepair ? `${formatDateRF(details.nextRepair)}*` : "Нет графиков ТО и ремонтов/ремонтых ведомостей"} - {datails.nextRepair ? ( + {details.nextRepair ? ( *по графику/ремонтной ведомости ) : null} Bероятность перехода в критическое состояние:  - - {`${datails.breakDown}%*`} + + {`${details.breakDown}%*`} *до даты следующего ТО/ремонта по графику/ремонтной ведомости - {chart.loaded ? : null} - {!chart.available ? ( + {chart.loaded && ( + <> + + + При расчёте вероятности принято, что она имеет равномерную функцию распределения на интервале [a, b], +
+ гда a = 0 (текущий момент времени), b = количество дней до выхода из строя от текущего момента времени, +
+ полученное от фреймворка прогнозирования +
+ + )} + {!chart.available && ( @@ -213,7 +245,7 @@ const ForecastDetail = ({ date, forecast, onClose }) => { - ) : null} + )}
@@ -325,7 +357,7 @@ const TechObjCard = ({ //При нажатии на отображение деталей прогноза const handleShowForecastDetailClick = modelHist => { - setState(pv => ({ ...pv, forecastDetail: modelHist.STO_FORECAST, forecastDate: modelHist.SRQ_DATE })); + setState(pv => ({ ...pv, forecastDetail: modelHist.STO_FORECAST_CARD, forecastDate: modelHist.SRQ_DATE })); }; //При нажатии на закрытие деталей прогноза