История прогнозов, карточка технического объекта, работа с прогнозом, формирование ремонтной ведомости

This commit is contained in:
Mikhail Chechnev 2024-08-10 02:43:38 +03:00
parent f7c0dd0e10
commit b31fea736a
10 changed files with 848 additions and 136 deletions

View File

@ -102,19 +102,33 @@ create or replace package UDO_PKG_EQUIPDS as
NEQUIPDSCMML in number -- Рег. номер модели класса оборудования выборки данных NEQUIPDSCMML in number -- Рег. номер модели класса оборудования выборки данных
); );
/* Состояние моделей по единице оборудования */ /* Состояние "Выборки данных оборудования (классы оборудования, модели)" по единице оборудования */
function CMML_STATUS_BY_EQCONFIG function CMML_STATUS_BY_EQCONFIG
( (
NEQCONFIG in number -- Рег. номер позиции состава оборудования NEQCONFIG in number -- Рег. номер позиции состава оборудования
) return number; -- Код действия с моделью (0 - нет моделей, 1 - есть модели в процессе обучения, 2 - есть обученные модели) ) return number; -- Код действия с моделью (0 - нет моделей, 1 - есть модели в процессе обучения, 2 - есть обученные модели)
/* Список моделей по единице оборудования */ /* Список "Выборки данных оборудования (классы оборудования, модели)" по единице оборудования */
procedure CMML_LIST_BY_EQCONFIG procedure CMML_LIST_BY_EQCONFIG
( (
NEQCONFIG in number, -- Рег. номер позиции состава оборудования NEQCONFIG in number, -- Рег. номер позиции состава оборудования
COUT out clob -- Сериализованная таблица данных COUT out clob -- Сериализованная таблица данных
); );
/* Формирование демо-прогноза */
procedure CMMLH_DEMO_BUILD
(
STASK in varchar2, -- Задача
COUT out clob -- Данные демо-прогноза
);
/* Список "Выборки данных оборудования (классы оборудования, модели, история запросов)" по единице оборудования */
procedure CMMLH_LIST_BY_EQCONFIG
(
NEQCONFIG in number, -- Рег. номер позиции состава оборудования
COUT out clob -- Сериализованная таблица данных
);
end UDO_PKG_EQUIPDS; end UDO_PKG_EQUIPDS;
/ /
create or replace package body UDO_PKG_EQUIPDS as create or replace package body UDO_PKG_EQUIPDS as
@ -592,7 +606,7 @@ text="Проверка прав доступа на работу с ""Выбор
end loop; end loop;
end CMML_SEND_RQ; end CMML_SEND_RQ;
/* Состояние моделей по единице оборудования */ /* Состояние "Выборки данных оборудования (классы оборудования, модели)" по единице оборудования */
function CMML_STATUS_BY_EQCONFIG function CMML_STATUS_BY_EQCONFIG
( (
NEQCONFIG in number -- Рег. номер позиции состава оборудования NEQCONFIG in number -- Рег. номер позиции состава оборудования
@ -620,7 +634,7 @@ text="Проверка прав доступа на работу с ""Выбор
return NRES; return NRES;
end CMML_STATUS_BY_EQCONFIG; end CMML_STATUS_BY_EQCONFIG;
/* Список моделей по единице оборудования */ /* Список "Выборки данных оборудования (классы оборудования, модели)" по единице оборудования */
procedure CMML_LIST_BY_EQCONFIG procedure CMML_LIST_BY_EQCONFIG
( (
NEQCONFIG in number, -- Рег. номер позиции состава оборудования NEQCONFIG in number, -- Рег. номер позиции состава оборудования
@ -715,5 +729,153 @@ text="Проверка прав доступа на работу с ""Выбор
COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1); COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
end CMML_LIST_BY_EQCONFIG; end CMML_LIST_BY_EQCONFIG;
/* Формирование демо-прогноза */
procedure CMMLH_DEMO_BUILD
(
STASK in varchar2, -- Задача
COUT out clob -- Данные демо-прогноза
)
is
RCH PKG_P8PANELS_VISUAL.TCHART; -- График
RCH_DS PKG_P8PANELS_VISUAL.TCHART_DATASET; -- Набор данных
DMONTH date; -- Месяц прогноза
begin
/* Сформируем заголовок графика */
RCH := PKG_P8PANELS_VISUAL.TCHART_MAKE(STYPE => PKG_P8PANELS_VISUAL.SCHART_TYPE_LINE,
STITLE => 'Прогноз на 5 месяцев',
SLGND_POS => PKG_P8PANELS_VISUAL.SCHART_LGND_POS_RIGHT);
/* Сформируем набор данных */
RCH_DS := PKG_P8PANELS_VISUAL.TCHART_DATASET_MAKE(SCAPTION => STASK);
/* Генерируем случайные данные */
for I in 1 .. 5
loop
/* Добавим метку для месяца */
DMONTH := ADD_MONTHS(sysdate, I);
PKG_P8PANELS_VISUAL.TCHART_ADD_LABEL(RCHART => RCH,
SLABEL => F_GET_MONTH(NVALUE => EXTRACT(month from DMONTH)) || ' ' ||
TO_CHAR(DMONTH, 'yyyy'));
/* Добавим месяц в набор данных */
PKG_P8PANELS_VISUAL.TCHART_DATASET_ADD_ITEM(RDATASET => RCH_DS, NVALUE => ROUND(DBMS_RANDOM.VALUE(1, 100)));
end loop;
/* Добавим набор данных в график */
PKG_P8PANELS_VISUAL.TCHART_ADD_DATASET(RCHART => RCH, RDATASET => RCH_DS);
/* Сериализуем описание */
COUT := PKG_P8PANELS_VISUAL.TCHART_TO_XML(RCHART => RCH, NINCLUDE_DEF => 1);
end CMMLH_DEMO_BUILD;
/* Извлечение прогноза из демо-данных */
function CMMLH_DEMO_FORECAST_PARSE
(
CFORECAST in clob -- Данные демо-прогноза
) return number -- Прогнозное значение
is
XDOC PKG_XPATH.TDOCUMENT; -- Десериализованный демо-прогноз
XROOT PKG_XPATH.TNODE; -- Корневой элемент данных
begin
/* Разбираем демо-даные */
XDOC := PKG_XPATH.PARSE_FROM_CLOB(LCXML => CFORECAST);
/* Извлекаем корневой элемент */
XROOT := PKG_XPATH.ROOT_NODE(RDOCUMENT => XDOC);
/* Возвращаем значение за ближайший месяц */
return PKG_XPATH.VALUE_NUM(RNODE => PKG_XPATH.SINGLE_NODE(RPARENT_NODE => XROOT,
SPATTERN => 'XDATA/XCHART/datasets/data'));
exception
when others then
return null;
end CMMLH_DEMO_FORECAST_PARSE;
/* Список "Выборки данных оборудования (классы оборудования, модели, история запросов)" по единице оборудования */
procedure CMMLH_LIST_BY_EQCONFIG
(
NEQCONFIG in number, -- Рег. номер позиции состава оборудования
COUT out clob -- Сериализованная таблица данных
)
is
RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы
RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы
begin
/* Инициализируем таблицу данных */
RDG := PKG_P8PANELS_VISUAL.TDATA_GRID_MAKE();
/* Добавляем в таблицу описание колонок */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NRN',
SCAPTION => 'Рег. номер',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
BVISIBLE => false);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SRQ_AUTHID',
SCAPTION => 'Пользователь',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SRQ_DATE',
SCAPTION => 'Дата/время запроса',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SEQUIPDS_CODE',
SCAPTION => 'Выборка',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SEQUIPDSCMML_TASK',
SCAPTION => 'Задача',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
SHINT => UDO_PKG_EQUIPDS_BASE.CMML_TASK_HINT());
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NEQUIPDSCMML_PRECISION_F',
SCAPTION => 'Точность (факт)',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB);
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'NFORECAST',
SCAPTION => 'Прогноз',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB,
SHINT => '<b style="color:red">Опасность</b> - вероятность сбоя более 60%.<br>' ||
'<b style="color:orange">Внимание</b> - вероятность сбоя от 30% до 60%<br>' ||
'<b style="color:green">В норме</b> - вероятность сбоев менее 30%');
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG,
SNAME => 'SFORECAST',
SCAPTION => 'Данные прогноза',
SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR,
BVISIBLE => false);
/* Обходим данные */
for C in (select T.RN NRN,
T.RQ_AUTHID SRQ_AUTHID,
TO_CHAR(T.RQ_DATE, 'dd.mm.yyyy hh24:mi:ss') SRQ_DATE,
DS.CODE SEQUIPDS_CODE,
ML.TASK SEQUIPDSCMML_TASK,
ML.PRECISION_F NEQUIPDSCMML_PRECISION_F,
null NFORECAST,
null SFORECAST,
T.FORECAST BFORECAST
from UDO_T_EQUIPDSCMMLH T,
UDO_T_EQUIPDSCMML ML,
UDO_T_EQUIPDSCM CM,
UDO_T_EQUIPDS DS
where T.EQCONFIG = NEQCONFIG
and T.PRN = ML.RN
and ML.PRN = CM.RN
and CM.PRN = DS.RN
order by T.RN desc)
loop
/* Конвертируем данные прогноза */
C.SFORECAST := BLOB2CLOB(LBDATA => C.BFORECAST);
/* Извлекаем прогноз */
C.NFORECAST := CMMLH_DEMO_FORECAST_PARSE(CFORECAST => C.SFORECAST);
/* Добавляем колонки с данными */
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NRN', NVALUE => C.NRN, BCLEAR => true);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SRQ_AUTHID', SVALUE => C.SRQ_AUTHID);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SRQ_DATE', SVALUE => C.SRQ_DATE);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SEQUIPDS_CODE', SVALUE => C.SEQUIPDS_CODE);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SEQUIPDSCMML_TASK', SVALUE => C.SEQUIPDSCMML_TASK);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW,
SNAME => 'NEQUIPDSCMML_PRECISION_F',
NVALUE => C.NEQUIPDSCMML_PRECISION_F);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NFORECAST', NVALUE => C.NFORECAST);
PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SFORECAST', SVALUE => C.SFORECAST);
/* Добавляем строку в таблицу */
PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW);
end loop;
/* Сериализуем описание */
COUT := PKG_P8PANELS_VISUAL.TDATA_GRID_TO_XML(RDATA_GRID => RDG, NINCLUDE_DEF => 1);
end CMMLH_LIST_BY_EQCONFIG;
end UDO_PKG_EQUIPDS; end UDO_PKG_EQUIPDS;
/ /

View File

@ -147,6 +147,37 @@ create or replace package UDO_PKG_EQUIPDS_BASE as
function CMML_TASK_HINT function CMML_TASK_HINT
return varchar2; -- Подсказка для задачи return varchar2; -- Подсказка для задачи
/* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
procedure CMMLH_INS
(
NPRN in number, -- Родитель
NEQCONFIG in number, -- Рег. номер технического объекта
SRQ_AUTHID in varchar2, -- Пользователь, выполнивший запрос
DRQ_DATE in date, -- Дата/время запроса
BRQ in blob, -- Параметры запроса
NRN out number -- Регистрационный номер
);
/* Базовое удаление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
procedure CMMLH_DEL
(
NRN in number -- Регистрационный номер
);
/* Базовая установка данных прогноза "Выборки данных оборудования (классы оборудования, файлы данных, история запросов)" */
procedure CMMLH_SET_FORECAST
(
NRN in number, -- Регистрационный номер
BFORECAST in blob -- Данные прогноза
);
/* Базовая установка идентификатора очереди обмена "Выборки данных оборудования (классы оборудования, файлы данных, история запросов)" */
procedure CMMLH_SET_EXSQUEUE
(
NRN in number, -- Регистрационный номер
NEXSQUEUE in number -- Идентификатор очереди обмена
);
end UDO_PKG_EQUIPDS_BASE; end UDO_PKG_EQUIPDS_BASE;
/ /
create or replace package body UDO_PKG_EQUIPDS_BASE as create or replace package body UDO_PKG_EQUIPDS_BASE as
@ -467,6 +498,62 @@ create or replace package body UDO_PKG_EQUIPDS_BASE as
begin begin
return '<b>TCF</b> - Оценка технического состояния (<b>T</b>echnical <b>C</b>ondition <b>F</b>orecast)<br>' || '<b>RUL</b> - Прогнозирование остаточного ресурса (<b>R</b>emaining <b>U</b>seful <b>L</b>ife)<br>' || '<b>FP</b> - Прогнозирование отказа (<b>F</b>ailure <b>P</b>redict)'; return '<b>TCF</b> - Оценка технического состояния (<b>T</b>echnical <b>C</b>ondition <b>F</b>orecast)<br>' || '<b>RUL</b> - Прогнозирование остаточного ресурса (<b>R</b>emaining <b>U</b>seful <b>L</b>ife)<br>' || '<b>FP</b> - Прогнозирование отказа (<b>F</b>ailure <b>P</b>redict)';
end CMML_TASK_HINT; end CMML_TASK_HINT;
/* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
procedure CMMLH_INS
(
NPRN in number, -- Родитель
NEQCONFIG in number, -- Рег. номер технического объекта
SRQ_AUTHID in varchar2, -- Пользователь, выполнивший запрос
DRQ_DATE in date, -- Дата/время запроса
BRQ in blob, -- Параметры запроса
NRN out number -- Регистрационный номер
)
is
begin
/* Формируем рег. номер */
NRN := GEN_ID();
/* Добавляем запись */
insert into UDO_T_EQUIPDSCMMLH
(RN, PRN, EQCONFIG, RQ_AUTHID, RQ_DATE, RQ, FORECAST, EXSQUEUE)
values
(NRN, NPRN, NEQCONFIG, SRQ_AUTHID, DRQ_DATE, BRQ, null, null);
end CMMLH_INS;
/* Базовое удаление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
procedure CMMLH_DEL
(
NRN in number -- Регистрационный номер
)
is
begin
/* Удалим запись */
delete from UDO_T_EQUIPDSCMMLH T where T.RN = NRN;
end CMMLH_DEL;
/* Базовая установка данных прогноза "Выборки данных оборудования (классы оборудования, файлы данных, история запросов)" */
procedure CMMLH_SET_FORECAST
(
NRN in number, -- Регистрационный номер
BFORECAST in blob -- Данные прогноза
)
is
begin
/* Установим данные ответа */
update UDO_T_EQUIPDSCMMLH T set T.FORECAST = BFORECAST where T.RN = NRN;
end CMMLH_SET_FORECAST;
/* Базовая установка идентификатора очереди обмена "Выборки данных оборудования (классы оборудования, файлы данных, история запросов)" */
procedure CMMLH_SET_EXSQUEUE
(
NRN in number, -- Регистрационный номер
NEXSQUEUE in number -- Идентификатор очереди обмена
)
is
begin
/* Установим идентификатор очереди обмена */
update UDO_T_EQUIPDSCMMLH T set T.EXSQUEUE = NEXSQUEUE where T.RN = NRN;
end CMMLH_SET_EXSQUEUE;
end UDO_PKG_EQUIPDS_BASE; end UDO_PKG_EQUIPDS_BASE;
/ /

View File

@ -28,6 +28,23 @@ create or replace package UDO_PKG_EQUIPTCF as
NEQCONFIG in number, -- Рег. номер технического объекта NEQCONFIG in number, -- Рег. номер технического объекта
COUT out clob -- Сериализованная карточка COUT out clob -- Сериализованная карточка
); );
/* Отправка запроса на формирование прогноза технического объекта */
procedure EQCONFIG_THOBJ_FORECAST
(
NEQCONFIG in number, -- Рег. номер технического объекта
NEQUIPDSCMML in number -- Рег. номер модели
);
/* Формирование ремонтной ведомости для технического объекта */
procedure EQCONFIG_THOBJ_MAKE_EQRPSHEET
(
NEQCONFIG in number, -- Рег. номер технического объекта
SACATALOG in varchar2, -- Каталог размещения ведомости
SEQTECSRVKIND in varchar2, -- Вид ремонта
DPLANDATE_FROM in date, -- Плановая дата начала ремонта
NIDENT out number -- Идентификатор списка сформированных ведомостей
);
end UDO_PKG_EQUIPTCF; end UDO_PKG_EQUIPTCF;
/ /
@ -416,6 +433,109 @@ text="Проверка прав доступа при формировании
/* Закрываем документ */ /* Закрываем документ */
PKG_XMAKE.CLOSE_CURSOR(ICURSOR => NCUR); PKG_XMAKE.CLOSE_CURSOR(ICURSOR => NCUR);
end EQCONFIG_THOBJ_CARD; end EQCONFIG_THOBJ_CARD;
/* Отправка запроса на формирование прогноза технического объекта */
procedure EQCONFIG_THOBJ_FORECAST
(
NEQCONFIG in number, -- Рег. номер технического объекта
NEQUIPDSCMML in number -- Рег. номер модели
)
is
NEQUIPDSCMMLH PKG_STD.TREF; -- Рег. номер истории запросов модели
CDEMO clob; -- Демо-данные прогноза
begin
/* Добавляем запись истории запросов */
UDO_PKG_EQUIPDS_BASE.CMMLH_INS(NPRN => NEQUIPDSCMML,
NEQCONFIG => NEQCONFIG,
SRQ_AUTHID => UTILIZER(),
DRQ_DATE => sysdate,
BRQ => null,
NRN => NEQUIPDSCMMLH);
/* Добавим демо-данные */
for C in (select T.TASK from UDO_T_EQUIPDSCMML T where T.RN = NEQUIPDSCMML)
loop
UDO_PKG_EQUIPDS.CMMLH_DEMO_BUILD(STASK => C.TASK, COUT => CDEMO);
UDO_PKG_EQUIPDS_BASE.CMMLH_SET_FORECAST(NRN => NEQUIPDSCMMLH, BFORECAST => CLOB2BLOB(LCDATA => CDEMO));
end loop;
end EQCONFIG_THOBJ_FORECAST;
/* Формирование ремонтной ведомости для технического объекта */
procedure EQCONFIG_THOBJ_MAKE_EQRPSHEET
(
NEQCONFIG in number, -- Рег. номер технического объекта
SACATALOG in varchar2, -- Каталог размещения ведомости
SEQTECSRVKIND in varchar2, -- Вид ремонта
DPLANDATE_FROM in date, -- Плановая дата начала ремонта
NIDENT out number -- Идентификатор списка сформированных ведомостей
)
is
NCOMPANY PKG_STD.TREF := GET_SESSION_COMPANY(); -- Рег. номер организации
NCRN PKG_STD.TREF; -- Рег. номер каталога размещения ведомости
SJURPERSONS PKG_STD.TSTRING; -- Юридическая принадлежность технического объекта
SCURRENCY PKG_STD.TSTRING; -- Валюта ведомости
SDOCTYPES PKG_STD.TSTRING; -- Тип документа ведомости
SPREF PKG_STD.TSTRING; -- Префикс ведосмости
SNUMB PKG_STD.TSTRING; -- Номер ведомости
NEQRPSHEET PKG_STD.TREF; -- Рег. номер сформированной ведомости
NSELECTLIST PKG_STD.TREF; -- Рег. номер позиции списк сформированных ведомостей
begin
/* Определяем юридическую принадлежность технического объекта */
begin
select JP.CODE
into SJURPERSONS
from EQCONFIG T,
JURPERSONS JP
where T.RN = NEQCONFIG
and T.JUR_PERS = JP.RN;
exception
when NO_DATA_FOUND then
PKG_MSG.RECORD_NOT_FOUND(NFLAG_SMART => 0, NDOCUMENT => NEQCONFIG, SUNIT_TABLE => 'EquipConfiguration');
end;
/* Читаем валюту документа по-умолчанию из параметров */
SCURRENCY := GET_OPTIONS_STR(SCODE => 'Equip_RepairSheets_Currency');
/* Читаем тип документа по-умолчанию из параметров */
SDOCTYPES := GET_OPTIONS_STR(SCODE => 'Equip_RepairSheets_DocType');
/* Читаем префикс документа по-умолчанию из параметров */
SPREF := GET_OPTIONS_STR(SCODE => 'Equip_RepairSheets_DocPref');
/* Формируем очередной номер документа */
P_EQRPSHEETS_GETNEXTNUMB(NCOMPANY => NCOMPANY, SPREF => SPREF, SDOCTYPES => SDOCTYPES, SNUMB => SNUMB);
/* Определяем каталог размещения */
FIND_ACATALOG_NAME(NFLAG_SMART => 0,
NCOMPANY => NCOMPANY,
NVERSION => null,
SUNITCODE => 'EquipRepairSheets',
SNAME => SACATALOG,
NRN => NCRN);
/* Формируем ведомость */
P_EQRPSHEETS_MAKE_EQRPSHEET(NCOMPANY => NCOMPANY,
NCRN => NCRN,
SDOCTYPES => SDOCTYPES,
SPREF => SPREF,
SNUMB => SNUMB,
DDOCDATE => sysdate,
SJURPERSONS => SJURPERSONS,
DPLANDATE_FROM => DPLANDATE_FROM,
DPLANDATE_TO => DPLANDATE_FROM,
SCURRENCY => SCURRENCY,
NCOURSE => 1,
SEQTECSRVKIND => SEQTECSRVKIND,
NPR_OBJ => null,
NTECH_OBJ => NEQCONFIG,
STECH_SRV_DIV => null,
SRESP_DIV => null,
SPERF_DIV => null,
SPERFORM_AGN => null,
SLEVEL => null,
SEQOBJKIND => null,
NRN => NEQRPSHEET);
/* Добавим ведомость в список */
NIDENT := GEN_IDENT();
P_SELECTLIST_INSERT(NIDENT => NIDENT,
NDOCUMENT => NEQRPSHEET,
SUNITCODE => 'EquipRepairSheets',
NRN => NSELECTLIST);
end EQCONFIG_THOBJ_MAKE_EQRPSHEET;
end UDO_PKG_EQUIPTCF; end UDO_PKG_EQUIPTCF;
/ /

27
db/UDO_T_EQUIPDSCMMLH.sql Normal file
View File

@ -0,0 +1,27 @@
/* Выборки данных оборудования (классы оборудования, модели, история запросов) */
create table UDO_T_EQUIPDSCMMLH
(
/* Регистрационный номер */
RN number(17) not null,
/* Родитель */
PRN number(17) not null,
/* Рег. номер технического объекта */
EQCONFIG number(17) not null,
/* Пользователь, выполнивший запрос */
RQ_AUTHID varchar2(30) not null,
/* Дата/время запроса */
RQ_DATE date not null,
/* Параметры запроса */
RQ blob,
/* Данные прогноза */
FORECAST blob,
/* Идентификатор очереди обмена */
EXSQUEUE number(17) default null,
/* Ключи */
constraint UDO_C_EQUIPDSCMMLH_RN_PK primary key (RN),
constraint UDO_C_EQUIPDSCMMLH_PRN_FK foreign key (PRN) references UDO_T_EQUIPDSCMML(RN),
constraint UDO_C_EQUIPDSCMMLH_EQCONFIG_FK foreign key (EQCONFIG) references EQCONFIG(RN),
constraint UDO_C_EQUIPDSCMMLH_RQAUTHID_NB check (rtrim(RQ_AUTHID) is not null),
constraint UDO_C_EQUIPDSCMMLH_EXSQUEUE_FK foreign key (EXSQUEUE) references EXSQUEUE(RN),
constraint UDO_C_EQUIPDSCMMLH_UN unique (PRN, RQ_AUTHID, RQ_DATE)
);

View File

@ -43,6 +43,9 @@ const REFRESH_INITIAL = {
dataSelectionClassMachineModelsList: 0 dataSelectionClassMachineModelsList: 0
}; };
//Начальное состояние диалогов
const DIALOGS_INITIAL = { dataSelectionIU: false, dataSelectionClassMachineIU: false, dataSelectionClassMachineModelIU: false };
//Стили //Стили
const STYLES = { const STYLES = {
DATA_SELECTION_STACK: { alignItems: "center" }, DATA_SELECTION_STACK: { alignItems: "center" },
@ -62,7 +65,7 @@ const AdminTab = ({ dataSelection = DS_RN_DEFAULT, dataSelectionClassMachine = n
const { pOnlineUserProcedure } = useContext(ApplicationСtx); const { pOnlineUserProcedure } = useContext(ApplicationСtx);
//Собственное состояние - отображаемые диалоги //Собственное состояние - отображаемые диалоги
const [dialogs, setDialogs] = useState({ dataSelectionIU: false, dataSelectionClassMachineIU: false, dataSelectionClassMachineModelIU: false }); const [dialogs, setDialogs] = useState(DIALOGS_INITIAL);
//Собственное состояние - флаги обновления данных //Собственное состояние - флаги обновления данных
const [refresh, setRefresh] = useState(REFRESH_INITIAL); const [refresh, setRefresh] = useState(REFRESH_INITIAL);

View File

@ -7,7 +7,7 @@
//Подключение библиотек //Подключение библиотек
//--------------------- //---------------------
import React, { useState, useContext, useEffect } from "react"; //Классы React import React, { useState, useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
import { import {
Box, Box,
@ -28,15 +28,13 @@ import {
DialogTitle, DialogTitle,
DialogContent, DialogContent,
DialogActions, DialogActions,
Input,
InputAdornment,
IconButton, IconButton,
Icon Icon
} from "@mui/material"; //Интерфейсные компоненты } from "@mui/material"; //Интерфейсные компоненты
import { useTheme } from "@mui/material/styles"; //Темы оформления import { useTheme } from "@mui/material/styles"; //Темы оформления
import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { ApplicationСtx } from "../../context/application"; //Контекст приложения
import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы
import { APP_BAR_HEIGHT, TABS_HEIGHT, SCROLL_STYLES, formatModelStateValue } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка import { APP_BAR_HEIGHT, TABS_HEIGHT, SCROLL_STYLES, formatModelStateValue, IUDFormTextField } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { DS_RN_DEFAULT } from "./admin_tab_hooks"; //Вспомогательные хуки import { DS_RN_DEFAULT } from "./admin_tab_hooks"; //Вспомогательные хуки
@ -104,85 +102,6 @@ const STYLES = {
//Вспомогательные функции и компоненты //Вспомогательные функции и компоненты
//------------------------------------ //------------------------------------
//Поле ввода из словаря
const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, dictionary, list, ...other }) => {
//Значение элемента
const [value, setValue] = useState(elementValue);
//При получении нового значения из вне
useEffect(() => {
setValue(elementValue);
}, [elementValue]);
//Выбор значения из словаря
const handleDictionaryClick = () =>
dictionary ? dictionary(res => (res ? res.map(i => handleChange({ target: { name: i.name, value: i.value } })) : null)) : null;
//Изменение значения элемента
const handleChange = e => {
console.log(e.target.name, e.target.value);
setValue(e.target.value);
if (onChange) onChange(e.target.name, e.target.value);
};
//Генерация содержимого
return (
<Box p={1}>
<FormControl variant="standard" fullWidth {...other}>
{list ? (
<>
<InputLabel id={`${elementCode}Lable`}>{labelText}</InputLabel>
<Select
labelId={`${elementCode}Lable`}
id={elementCode}
name={elementCode}
label={labelText}
value={value ? value : ""}
onChange={handleChange}
displayEmpty
>
{list.map((item, i) => (
<MenuItem key={i} value={item.value ? item.value : ""}>
{item.name}
</MenuItem>
))}
</Select>
</>
) : (
<>
<InputLabel htmlFor={elementCode}>{labelText}</InputLabel>
<Input
id={elementCode}
name={elementCode}
value={value ? value : ""}
endAdornment={
dictionary ? (
<InputAdornment position="end">
<IconButton aria-label={`${elementCode} select`} onClick={handleDictionaryClick} edge="end">
<Icon>list</Icon>
</IconButton>
</InputAdornment>
) : null
}
onChange={handleChange}
/>
</>
)}
</FormControl>
</Box>
);
};
//Контроль свойств - Поле ввода из словаря
IUDFormTextField.propTypes = {
elementCode: PropTypes.string.isRequired,
elementValue: PropTypes.string,
labelText: PropTypes.string.isRequired,
onChange: PropTypes.func,
dictionary: PropTypes.func,
list: PropTypes.array
};
//Кнопка с дополнительной подписью //Кнопка с дополнительной подписью
const ExtraCaptionButton = ({ caption, title, subtitle, maxWidth, onClick, theme }) => { const ExtraCaptionButton = ({ caption, title, subtitle, maxWidth, onClick, theme }) => {
//При нажатии на кнопку //При нажатии на кнопку

View File

@ -7,9 +7,9 @@
//Подключение библиотек //Подключение библиотек
//--------------------- //---------------------
import React from "react"; //Классы React import React, { useState, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
import { Box, Stack, Icon } from "@mui/material"; //Интерфейсные компоненты import { Box, Stack, Icon, Input, InputAdornment, FormControl, Select, InputLabel, MenuItem, IconButton } from "@mui/material"; //Интерфейсные компоненты
//--------- //---------
//Константы //Константы
@ -62,10 +62,10 @@ const formatModelStateValue = (theme, value, err) => {
value == 0 value == 0
? ["Зарегистрирована", "app_registration", null] ? ["Зарегистрирована", "app_registration", null]
: value == 1 : value == 1
? ["Обрабатывается внешней системой", "manage_history", theme.palette.warning.main] ? ["Обучается", "manage_history", theme.palette.warning.main]
: value == 2 : value == 2
? ["Успешно обработана внешней системой", "check_circle", theme.palette.primary.main] ? ["Обучена", "check_circle", theme.palette.primary.main]
: [`Ошибка обработки внешней системой: ${err}`, "error", theme.palette.error.main]; : [`Ошибка обучения: ${err}`, "error", theme.palette.error.main];
return ( return (
<Stack direction="row" gap={0.5} alignItems="center" justifyContent="left" color={color}> <Stack direction="row" gap={0.5} alignItems="center" justifyContent="left" color={color}>
<Icon title={text}>{icon}</Icon> <Icon title={text}>{icon}</Icon>
@ -84,8 +84,98 @@ TabPanel.propTypes = {
children: PropTypes.element children: PropTypes.element
}; };
//Поле ввода формы
const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, dictionary, list, type, ...other }) => {
//Значение элемента
const [value, setValue] = useState(elementValue);
//При получении нового значения из вне
useEffect(() => {
setValue(elementValue);
}, [elementValue]);
//Выбор значения из словаря
const handleDictionaryClick = () =>
dictionary ? dictionary(res => (res ? res.map(i => handleChange({ target: { name: i.name, value: i.value } })) : null)) : null;
//Изменение значения элемента
const handleChange = e => {
setValue(e.target.value);
if (onChange) onChange(e.target.name, e.target.value);
};
//Генерация содержимого
return (
<Box p={1}>
<FormControl variant="standard" fullWidth {...other}>
{list ? (
<>
<InputLabel id={`${elementCode}Lable`}>{labelText}</InputLabel>
<Select
labelId={`${elementCode}Lable`}
id={elementCode}
name={elementCode}
label={labelText}
value={value ? value : ""}
onChange={handleChange}
displayEmpty
>
{list.map((item, i) => (
<MenuItem key={i} value={item.value ? item.value : ""}>
{item.name}
</MenuItem>
))}
</Select>
</>
) : (
<>
<InputLabel htmlFor={elementCode}>{labelText}</InputLabel>
<Input
id={elementCode}
name={elementCode}
value={value ? value : ""}
endAdornment={
dictionary ? (
<InputAdornment position="end">
<IconButton aria-label={`${elementCode} select`} onClick={handleDictionaryClick} edge="end">
<Icon>list</Icon>
</IconButton>
</InputAdornment>
) : null
}
{...(type ? { type } : {})}
onChange={handleChange}
/>
</>
)}
</FormControl>
</Box>
);
};
//Контроль свойств - Поле ввода формы
IUDFormTextField.propTypes = {
elementCode: PropTypes.string.isRequired,
elementValue: PropTypes.string,
labelText: PropTypes.string.isRequired,
onChange: PropTypes.func,
dictionary: PropTypes.func,
list: PropTypes.array,
type: PropTypes.string
};
//---------------- //----------------
//Интерфейс модуля //Интерфейс модуля
//---------------- //----------------
export { MODES, APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_MORE_HEIGHT, TABLE_FILTERS_HEIGHT, SCROLL_STYLES, formatModelStateValue, TabPanel }; export {
MODES,
APP_BAR_HEIGHT,
TABS_HEIGHT,
TABLE_MORE_HEIGHT,
TABLE_FILTERS_HEIGHT,
SCROLL_STYLES,
formatModelStateValue,
TabPanel,
IUDFormTextField
};

View File

@ -13,15 +13,18 @@ import { Box, Grid } from "@mui/material"; //Интерфейсные компо
import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; //Дерево import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; //Дерево
import { useTreeViewApiRef } from "@mui/x-tree-view/hooks/useTreeViewApiRef"; //API дерева import { useTreeViewApiRef } from "@mui/x-tree-view/hooks/useTreeViewApiRef"; //API дерева
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с серверомs
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_FILTERS_HEIGHT, TABLE_MORE_HEIGHT, SCROLL_STYLES } from "./eqs_tech_cond_forecast_lyaout"; //Общие Вспомогательные компоненты и вёрстка import { APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_FILTERS_HEIGHT, TABLE_MORE_HEIGHT, SCROLL_STYLES } from "./eqs_tech_cond_forecast_lyaout"; //Общие Вспомогательные компоненты и вёрстка
import { TechObjCard, eqConfigTechObjTableValueFormatter, eqConfigTechObjTableDataCellRender } from "./forecast_tab_layout"; //Вспомогательные компоненты и вёрстка import { TechObjCard, eqConfigTechObjTableValueFormatter, eqConfigTechObjTableDataCellRender, TechObjMakeEqRpSheet } from "./forecast_tab_layout"; //Вспомогательные компоненты и вёрстка
import { import {
useEqConfigTree, useEqConfigTree,
useEqConfigTechObjTable, useEqConfigTechObjTable,
useEqConfigTechObjCard, useEqConfigTechObjCard,
useTechObjModelsList, useTechObjModelsList,
useTechObjForecastHistList,
findTreeItem, findTreeItem,
needLoadLevel needLoadLevel
} from "./forecast_tab_hooks"; //Вспомогательные хуки } from "./forecast_tab_hooks"; //Вспомогательные хуки
@ -30,6 +33,14 @@ import {
//Константы //Константы
//--------- //---------
//Начальное состояние флагов обновления
const REFRESH_INITIAL = {
techObjForecastHistList: 0
};
//Начальное состояние диалогов
const DIALOGS_INITIAL = { makeEqRpSheet: false };
//Стили //Стили
const STYLES = { const STYLES = {
LEFT_SIDE_GRID: {}, LEFT_SIDE_GRID: {},
@ -59,9 +70,15 @@ const TECH_OBJ_SPEC_INIT = { parent: null, filters: [], orders: [], pageNumber:
//Закладка прогнозирования //Закладка прогнозирования
const ForecastTab = ({ onGoToAdmin }) => { const ForecastTab = ({ onGoToAdmin }) => {
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Подключение к контексту сообщений //Подключение к контексту сообщений
const { InlineMsgInfo } = useContext(MessagingСtx); const { InlineMsgInfo } = useContext(MessagingСtx);
//Подключение к контексту приложения
const { pOnlineShowUnit } = useContext(ApplicationСtx);
//Подключение к API дерева //Подключение к API дерева
const apiRef = useTreeViewApiRef(); const apiRef = useTreeViewApiRef();
@ -71,9 +88,15 @@ const ForecastTab = ({ onGoToAdmin }) => {
//Собственное состояние - спецификация технических объектов //Собственное состояние - спецификация технических объектов
const [techObjSpec, setTechObjSpec] = useState({ ...TECH_OBJ_SPEC_INIT }); const [techObjSpec, setTechObjSpec] = useState({ ...TECH_OBJ_SPEC_INIT });
//Собственное состояния - карточка технического объекта //Собственное состояния - карточка технического объекта - 11984229
const [techObjCardId, setTechObjCardId] = useState(null); const [techObjCardId, setTechObjCardId] = useState(null);
//Собственное состояние - отображаемые диалоги
const [dialogs, setDialogs] = useState(DIALOGS_INITIAL);
//Собственное состояния - флаги перезагрузки
const [refresh, setRefresh] = useState(REFRESH_INITIAL);
//Загрузчик веток дерева //Загрузчик веток дерева
const { eQconfigTree } = useEqConfigTree(loadingTreeItem); const { eQconfigTree } = useEqConfigTree(loadingTreeItem);
@ -89,7 +112,10 @@ const ForecastTab = ({ onGoToAdmin }) => {
const { techObjCard, techObjCardIsLoading } = useEqConfigTechObjCard(techObjCardId); const { techObjCard, techObjCardIsLoading } = useEqConfigTechObjCard(techObjCardId);
//Загрузчик моделей выбранного технического объекта //Загрузчик моделей выбранного технического объекта
const { techObjModelsList, techObjModelsListIsLoading } = useTechObjModelsList(techObjCardId); const { techObjModelsList } = useTechObjModelsList(techObjCardId);
//
const { techObjForecastHistList } = useTechObjForecastHistList(techObjCardId, refresh.techObjForecastHistList);
//Обработка развёртывания/свёртывания уровня дерева //Обработка развёртывания/свёртывания уровня дерева
const handleTreeItemExpansionToggle = (event, itemId, isExpanded) => const handleTreeItemExpansionToggle = (event, itemId, isExpanded) =>
@ -128,9 +154,36 @@ const ForecastTab = ({ onGoToAdmin }) => {
//При переходе к закладке администрирования из карточки технического объекта //При переходе к закладке администрирования из карточки технического объекта
const handleTechObjeCardGoToModel = model => (onGoToAdmin ? onGoToAdmin(model.NEQUIPDS, model.NEQUIPDSCM) : null); const handleTechObjeCardGoToModel = model => (onGoToAdmin ? onGoToAdmin(model.NEQUIPDS, model.NEQUIPDSCM) : null);
//При запросе прогноза по модели из карточки технического объекта
const handleTechObjeCardGetPrediction = async model => {
await executeStored({ stored: "UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_FORECAST", args: { NEQCONFIG: techObjCardId, NEQUIPDSCMML: model.NRN } });
setRefresh(pv => ({ ...pv, techObjForecastHistList: pv.techObjForecastHistList + 1 }));
};
//При закрытии карточки технического объекта //При закрытии карточки технического объекта
const handleTechObjeCardClose = () => setTechObjCardId(null); const handleTechObjeCardClose = () => setTechObjCardId(null);
//При нажатии на "Ремонтировать" в карточке технического объекта
const handleTechObjeCardMakeEqRpSheet = () => setDialogs(pv => ({ ...pv, makeEqRpSheet: true }));
//При нажатии "ОК" в диалоге формирования ремонтной ведомости
const handleTechObjMakeEqRpSheetOk = async values => {
const data = await executeStored({
stored: "UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_MAKE_EQRPSHEET",
args: {
NEQCONFIG: techObjCardId,
SACATALOG: values.acatalog,
SEQTECSRVKIND: values.eQTecSrvKind,
DPLANDATE_FROM: new Date(values.planDateFrom)
}
});
setDialogs(pv => ({ ...pv, makeEqRpSheet: false }));
pOnlineShowUnit({ unitCode: "EquipRepairSheets", showMethod: "main_selected", inputParameters: [{ name: "in_Ident", value: data.NIDENT }] });
};
//При нажитии "Отмена" в диалоге формирования ремонтной ведомости
const handleTechObjMakeEqRpSheetCancel = () => setDialogs(pv => ({ ...pv, makeEqRpSheet: false }));
//Текст при отсутствии данных в списке технических объектов //Текст при отсутствии данных в списке технических объектов
const noDataFoundText = const noDataFoundText =
techObjsDataGridIsLoading || !techObjsDataGrid.init techObjsDataGridIsLoading || !techObjsDataGrid.init
@ -142,6 +195,7 @@ const ForecastTab = ({ onGoToAdmin }) => {
//Генерация содержимого //Генерация содержимого
return ( return (
<div> <div>
{dialogs.makeEqRpSheet ? <TechObjMakeEqRpSheet onCancel={handleTechObjMakeEqRpSheetCancel} onOk={handleTechObjMakeEqRpSheetOk} /> : null}
<Grid container> <Grid container>
<Grid item xs={3} sx={STYLES.LEFT_SIDE_GRID}> <Grid item xs={3} sx={STYLES.LEFT_SIDE_GRID}>
<Box sx={STYLES.TREE_BOX}> <Box sx={STYLES.TREE_BOX}>
@ -155,12 +209,15 @@ const ForecastTab = ({ onGoToAdmin }) => {
</Grid> </Grid>
<Grid item xs={9} sx={STYLES.RIGHT_SIDE_GRID}> <Grid item xs={9} sx={STYLES.RIGHT_SIDE_GRID}>
{techObjCardId ? ( {techObjCardId ? (
!techObjCardIsLoading && !techObjModelsListIsLoading ? ( !techObjCardIsLoading ? (
<TechObjCard <TechObjCard
cardData={techObjCard} cardData={techObjCard}
modelsList={techObjModelsList} modelsList={techObjModelsList}
forecastHistList={techObjForecastHistList}
onClose={handleTechObjeCardClose} onClose={handleTechObjeCardClose}
onGoToModel={handleTechObjeCardGoToModel} onGoToModel={handleTechObjeCardGoToModel}
onGetPrediction={handleTechObjeCardGetPrediction}
onMakeEqRpSheet={handleTechObjeCardMakeEqRpSheet}
/> />
) : null ) : null
) : techObjsDataGrid.init ? ( ) : techObjsDataGrid.init ? (

View File

@ -225,8 +225,59 @@ const useTechObjModelsList = (id, refresh) => {
return { techObjModelsList: data, techObjModelsListIsLoading: isLoading }; return { techObjModelsList: data, techObjModelsListIsLoading: isLoading };
}; };
//Загрузка истории запросов к модели единицы оборудования
const useTechObjForecastHistList = (id, refresh) => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(false);
//Собственное состояние - таблица данных
const [data, setData] = useState({
init: false,
columnsDef: [],
rows: []
});
//Подключение к контексту взаимодействия с сервером
const { executeStored } = useContext(BackEndСtx);
//Загрузка данных при изменении зависимостей
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
const data = await executeStored({
stored: "UDO_PKG_EQUIPDS.CMMLH_LIST_BY_EQCONFIG",
args: { NEQCONFIG: id },
respArg: "COUT",
loader: false
});
setData(pv => ({
...pv,
columnsDef: [...data.XCOLUMNS_DEF],
rows: [...(data.XROWS || [])],
init: true
}));
} finally {
setLoading(false);
}
};
if (id) loadData();
}, [id, refresh, executeStored]);
//Вернём данные
return { techObjForecastHistList: data, techObjForecastHistListIsLoading: isLoading };
};
//---------------- //----------------
//Интерфейс модуля //Интерфейс модуля
//---------------- //----------------
export { useEqConfigTree, useEqConfigTechObjTable, useEqConfigTechObjCard, findTreeItem, needLoadLevel, useTechObjModelsList }; export {
useEqConfigTree,
useEqConfigTechObjTable,
useEqConfigTechObjCard,
findTreeItem,
needLoadLevel,
useTechObjModelsList,
useTechObjForecastHistList
};

View File

@ -7,15 +7,33 @@
//Подключение библиотек //Подключение библиотек
//--------------------- //---------------------
import React from "react"; //Классы React import React, { useState, useEffect, useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
import { Stack, Icon, Box, Card, CardContent, Typography, CardActions, Button, IconButton, Divider } from "@mui/material"; //Интерфейсные компоненты import {
Stack,
Icon,
Box,
Card,
CardContent,
Typography,
CardActions,
Button,
IconButton,
Divider,
Link,
Dialog,
DialogTitle,
DialogContent,
DialogActions
} from "@mui/material"; //Интерфейсные компоненты
import { useTheme } from "@mui/material/styles"; //Темы оформления import { useTheme } from "@mui/material/styles"; //Темы оформления
import { ApplicationСtx } from "../../context/application"; //Контекст приложения
import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { formatDateRF } from "../../core/utils"; //Вспомогательные функции import { formatDateRF, xml2JSON } from "../../core/utils"; //Вспомогательные функции
import { SCROLL_STYLES, formatModelStateValue } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка import { SCROLL_STYLES, formatModelStateValue, IUDFormTextField } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка
import { Link } from "react-router-dom"; import { P8PChart } from "../../components/p8p_chart"; //График
//--------- //---------
//Константы //Константы
@ -28,7 +46,13 @@ const STYLES = {
EQ_ML_TABLE: { EQ_ML_TABLE: {
maxHeight: "200px", maxHeight: "200px",
...SCROLL_STYLES ...SCROLL_STYLES
} },
EQ_FORECAST_TABLE: {
maxHeight: "200px",
...SCROLL_STYLES
},
EQ_FORECAST_DETAIL_DIALOG: { maxWidth: "600px" },
EQ_FORECAST_DETAIL_CHART: { width: "550px", display: "flex", justifyContent: "center" }
}; };
//------------------------------------ //------------------------------------
@ -48,7 +72,7 @@ const techObjCardModelsTableHeadCellRender = ({ columnDef }) => {
} }
}; };
//Форматирование колонок таблицы моделей класса оборудования выборки данных //Форматирование колонок таблицы моделей единицы оборудования
const techObjCardModelsTableDataCellRender = ({ row, columnDef, theme, onGoToModel, onGetPrediction }) => { const techObjCardModelsTableDataCellRender = ({ row, columnDef, theme, onGoToModel, onGetPrediction }) => {
switch (columnDef.name) { switch (columnDef.name) {
case "SNEQUIPDS": case "SNEQUIPDS":
@ -79,15 +103,71 @@ const techObjCardModelsTableDataCellRender = ({ row, columnDef, theme, onGoToMod
} }
}; };
//Форматирование колонок таблицы истории прогнозов класса оборудования выборки данных
const techObjCardForecastListTableDataCellRender = ({ row, columnDef, onShowForecastDetail }) => {
switch (columnDef.name) {
case "NFORECAST":
return {
data: (
<Button
variant="outlined"
color={row.NFORECAST < 30 ? "success" : row.NFORECAST >= 30 && row.NFORECAST < 60 ? "warning" : "error"}
onClick={() => onShowForecastDetail(row)}
>
{`${row.NFORECAST}%`}
</Button>
)
};
}
};
//Детали прогноза
const ForecastDetail = ({ date, forecast, onClose }) => {
//Собственное состояние - график
const [chart, setChart] = useState({ loaded: false, labels: [], datasets: [] });
//При подключении к странице
useEffect(() => {
const loadChart = async () => {
const chart = await xml2JSON({ xmlDoc: forecast });
console.log(chart);
setChart(pv => ({ ...pv, loaded: true, ...chart.XDATA.XCHART }));
};
loadChart();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//Генерация содержимого
return (
<Dialog open={true} onClose={() => (onClose ? onClose() : null)} {...STYLES.EQ_FORECAST_DETAIL_DIALOG}>
<DialogTitle>{`Детали прогноза от ${date}`}</DialogTitle>
<DialogContent>{chart.loaded ? <P8PChart {...chart} style={STYLES.EQ_FORECAST_DETAIL_CHART} /> : null}</DialogContent>
<DialogActions>
<Button onClick={() => (onClose ? onClose() : null)}>{BUTTONS.CLOSE}</Button>
</DialogActions>
</Dialog>
);
};
//Контроль свойств - Детали прогноза
ForecastDetail.propTypes = {
date: PropTypes.string.isRequired,
forecast: PropTypes.string.isRequired,
onClose: PropTypes.func
};
//----------- //-----------
//Тело модуля //Тело модуля
//----------- //-----------
//Карточка технического объекта //Карточка технического объекта
const TechObjCard = ({ cardData, modelsList, onClose, onGoToModel }) => { const TechObjCard = ({ cardData, modelsList, forecastHistList, onClose, onGoToModel, onGetPrediction, onMakeEqRpSheet }) => {
//Подключаемся к теме //Подключаемся к теме
const theme = useTheme(); const theme = useTheme();
//Собственное состояние
const [state, setState] = useState({ forecastDetail: null, forecastDate: null });
//При нажатии на кнопку закрытия карточки //При нажатии на кнопку закрытия карточки
const handleClose = () => (onClose ? onClose() : null); const handleClose = () => (onClose ? onClose() : null);
@ -95,13 +175,25 @@ const TechObjCard = ({ cardData, modelsList, onClose, onGoToModel }) => {
const handleGoToModelClick = model => (onGoToModel ? onGoToModel(model) : null); const handleGoToModelClick = model => (onGoToModel ? onGoToModel(model) : null);
//При нажатии на запрос предсказания //При нажатии на запрос предсказания
const handleGetPredictionClick = model => { const handleGetPredictionClick = model => (onGetPrediction ? onGetPrediction(model) : null);
console.log(model);
//При нажатии на формирование ремонтной ведомости
const handleMakeEqRpSheetClick = () => (onMakeEqRpSheet ? onMakeEqRpSheet() : null);
//При нажатии на отображение деталей прогноза
const handleShowForecastDetailClick = modelHist => {
setState(pv => ({ ...pv, forecastDetail: modelHist.SFORECAST, forecastDate: modelHist.SRQ_DATE }));
}; };
//При нажатии на закрытие деталей прогноза
const handleCloseForecastDetailClick = () => setState(pv => ({ ...pv, forecastDetail: null, forecastDate: null }));
//Генерация содержимого //Генерация содержимого
return ( return (
<Box p={2}> <Box p={2}>
{state.forecastDetail ? (
<ForecastDetail date={state.forecastDate} forecast={state.forecastDetail} onClose={handleCloseForecastDetailClick} />
) : null}
<Card> <Card>
<CardContent> <CardContent>
<Stack spacing={2} direction={"row"} alignItems={"center"}> <Stack spacing={2} direction={"row"} alignItems={"center"}>
@ -122,33 +214,61 @@ const TechObjCard = ({ cardData, modelsList, onClose, onGoToModel }) => {
</Typography> </Typography>
<Typography variant="h6">{cardData.SNAME}</Typography> <Typography variant="h6">{cardData.SNAME}</Typography>
<br /> <br />
<Divider /> {modelsList.init ? (
<Typography variant="h6" component="div"> <>
Модели <Divider />
</Typography> <Typography variant="h6" component="div">
<P8PDataGrid Модели
{...{ ...P8P_DATA_GRID_CONFIG_PROPS }} </Typography>
containerComponentProps={{ sx: STYLES.EQ_ML_TABLE, elevation: 0 }} <P8PDataGrid
columnsDef={modelsList.columnsDef} {...{ ...P8P_DATA_GRID_CONFIG_PROPS }}
rows={modelsList.rows} containerComponentProps={{ sx: STYLES.EQ_ML_TABLE, elevation: 0 }}
size={P8P_DATA_GRID_SIZE.SMALL} columnsDef={modelsList.columnsDef}
morePages={false} rows={modelsList.rows}
fixedHeader={true} size={P8P_DATA_GRID_SIZE.SMALL}
reloading={false} morePages={false}
headCellRender={techObjCardModelsTableHeadCellRender} fixedHeader={true}
dataCellRender={prms => reloading={false}
techObjCardModelsTableDataCellRender({ headCellRender={techObjCardModelsTableHeadCellRender}
...prms, dataCellRender={prms =>
theme, techObjCardModelsTableDataCellRender({
onGoToModel: handleGoToModelClick, ...prms,
onGetPrediction: handleGetPredictionClick theme,
}) onGoToModel: handleGoToModelClick,
} onGetPrediction: handleGetPredictionClick
/> })
<Divider /> }
/>
</>
) : null}
{forecastHistList.init ? (
<>
<Divider />
<Typography variant="h6" component="div">
Прогнозы
</Typography>
<P8PDataGrid
{...{ ...P8P_DATA_GRID_CONFIG_PROPS }}
containerComponentProps={{ sx: STYLES.EQ_FORECAST_TABLE, elevation: 0 }}
columnsDef={forecastHistList.columnsDef}
rows={forecastHistList.rows}
size={P8P_DATA_GRID_SIZE.SMALL}
morePages={false}
fixedHeader={true}
reloading={false}
dataCellRender={prms =>
techObjCardForecastListTableDataCellRender({
...prms,
theme,
onShowForecastDetail: handleShowForecastDetailClick
})
}
/>
</>
) : null}
</CardContent> </CardContent>
<CardActions> <CardActions>
<Button>Ремонтировать</Button> <Button onClick={handleMakeEqRpSheetClick}>Ремонтировать</Button>
</CardActions> </CardActions>
</Card> </Card>
</Box> </Box>
@ -159,8 +279,11 @@ const TechObjCard = ({ cardData, modelsList, onClose, onGoToModel }) => {
TechObjCard.propTypes = { TechObjCard.propTypes = {
cardData: PropTypes.object.isRequired, cardData: PropTypes.object.isRequired,
modelsList: PropTypes.object.isRequired, modelsList: PropTypes.object.isRequired,
forecastHistList: PropTypes.object.isRequired,
onClose: PropTypes.func, onClose: PropTypes.func,
onGoToModel: PropTypes.func onGoToModel: PropTypes.func,
onGetPrediction: PropTypes.func,
onMakeEqRpSheet: PropTypes.func
}; };
//Формирование значения для колонки "Медель" таблицы технических объектов //Формирование значения для колонки "Медель" таблицы технических объектов
@ -198,8 +321,81 @@ const eqConfigTechObjTableDataCellRender = ({ row, columnDef, onCMMLStatus }) =>
} }
}; };
//Диалог формирование ремонтной ведомости
const TechObjMakeEqRpSheet = ({ onOk, onCancel }) => {
//Подключение к контексту приложения
const { pOnlineShowDictionary } = useContext(ApplicationСtx);
//Собственное состояние - значения формы
const [values, setValues] = useState({
acatalog: "",
eQTecSrvKind: "",
planDateFrom: ""
});
//Отработка воода значения в форму
const handleValueChanged = (name, value) => setValues(pv => ({ ...pv, [name]: value }));
//Выбор каталога размещения ремонтной ведомости
const selectEquipRepairSheetsCatalog = (name, callBack) => {
pOnlineShowDictionary({
unitCode: "EquipRepairSheets",
showMethod: "catalog",
callBack: res => callBack(res.success ? [{ name, value: res.outParameters.ctlgname }] : null)
});
};
//Выбор вида технического обслуживания
const selectEquipTechServiceKinds = (name, callBack) => {
pOnlineShowDictionary({
unitCode: "EquipTechServiceKinds",
callBack: res => callBack(res.success ? [{ name, value: res.outParameters.out_CODE }] : null)
});
};
//Генерация содержимого
return (
<Dialog open={true} onClose={() => (onOk ? onCancel() : null)}>
<DialogTitle>Формирование ремонтной ведомости</DialogTitle>
<DialogContent>
<IUDFormTextField
elementCode={"acatalog"}
elementValue={values.acatalog}
labelText={"Каталог размещения"}
onChange={handleValueChanged}
dictionary={callBack => selectEquipRepairSheetsCatalog("acatalog", callBack)}
/>
<IUDFormTextField
elementCode={"eQTecSrvKind"}
elementValue={values.eQTecSrvKind}
labelText={"Вид технического обслуживания"}
onChange={handleValueChanged}
dictionary={callBack => selectEquipTechServiceKinds("eQTecSrvKind", callBack)}
/>
<IUDFormTextField
elementCode={"planDateFrom"}
elementValue={values.planDateFrom}
labelText={"Плановая дата ремонта"}
onChange={handleValueChanged}
type={"date"}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => (onOk ? onOk(values) : null)}>{BUTTONS.OK}</Button>
<Button onClick={() => (onOk ? onCancel() : null)}>{BUTTONS.CANCEL}</Button>
</DialogActions>
</Dialog>
);
};
//Контроль свойств - Диалог формирование ремонтной ведомости
TechObjMakeEqRpSheet.propTypes = {
onOk: PropTypes.func,
onCancel: PropTypes.func
};
//---------------- //----------------
//Интерфейс модуля //Интерфейс модуля
//---------------- //----------------
export { TechObjCard, eqConfigTechObjTableValueFormatter, eqConfigTechObjTableDataCellRender }; export { TechObjCard, eqConfigTechObjTableValueFormatter, eqConfigTechObjTableDataCellRender, TechObjMakeEqRpSheet };