diff --git a/db/UDO_PKG_EQUIPDS.pck b/db/UDO_PKG_EQUIPDS.pck index 0ab68df..073b616 100644 --- a/db/UDO_PKG_EQUIPDS.pck +++ b/db/UDO_PKG_EQUIPDS.pck @@ -31,7 +31,7 @@ create or replace package UDO_PKG_EQUIPDS as procedure CM_LIST ( NEQUIPDS in number, -- Рег. номер выборки данных - COUT out clob -- Сериализованная таблица данных + COUT out clob -- Сериализованный список ); /* Клиентское добавление "Выборки данных оборудования (классы оборудования)" */ @@ -102,17 +102,18 @@ create or replace package UDO_PKG_EQUIPDS as NEQUIPDSCMML in number -- Рег. номер модели класса оборудования выборки данных ); - /* Код доступного действия с моделью по единице оборудования */ - function CMML_ACT_BY_EQCONFIG + /* Состояние моделей по единице оборудования */ + function CMML_STATUS_BY_EQCONFIG ( NEQCONFIG in number -- Рег. номер позиции состава оборудования - ) return number; -- Код действия с моделью (0 - нет доступных, 1 - запросить прогноз, 2 - обучить) + ) return number; -- Код действия с моделью (0 - нет моделей, 1 - есть модели в процессе обучения, 2 - есть обученные модели) /* Список моделей по единице оборудования */ - function CMML_LIST_BY_EQCONFIG + procedure CMML_LIST_BY_EQCONFIG ( - NEQCONFIG in number -- Рег. номер позиции состава оборудования - ) return varchar2; -- Список моделей с системным разделителем по умолчанию + NEQCONFIG in number, -- Рег. номер позиции состава оборудования + COUT out clob -- Сериализованная таблица данных + ); end UDO_PKG_EQUIPDS; / @@ -273,7 +274,7 @@ text="Проверка прав доступа на работу с ""Выбор procedure CM_LIST ( NEQUIPDS in number, -- Рег. номер выборки данных - COUT out clob -- Сериализованная таблица данных + COUT out clob -- Сериализованный список ) is NCUR integer; -- Курсор документа для результата @@ -493,7 +494,8 @@ text="Проверка прав доступа на работу с ""Выбор PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, SNAME => 'STASK', SCAPTION => 'Задача', - SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + 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 => 'NPRECISION_P', SCAPTION => 'Точность (план)', @@ -511,6 +513,10 @@ text="Проверка прав доступа на работу с ""Выбор SCAPTION => 'Сообщение об ошибке', SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SACTIONS', + SCAPTION => 'Действия', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); /* Обходим данные */ for C in (select T.RN NRN, T.TASK STASK, @@ -529,6 +535,7 @@ text="Проверка прав доступа на работу с ""Выбор PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NPRECISION_F', NVALUE => C.NPRECISION_F); PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NSTATUS', NVALUE => C.NSTATUS); PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SERR', SVALUE => C.SERR); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SACTIONS'); /* Добавляем строку в таблицу */ PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); end loop; @@ -585,25 +592,128 @@ text="Проверка прав доступа на работу с ""Выбор end loop; end CMML_SEND_RQ; - /* Код доступного действия с моделью по единице оборудования */ - function CMML_ACT_BY_EQCONFIG + /* Состояние моделей по единице оборудования */ + function CMML_STATUS_BY_EQCONFIG ( - NEQCONFIG in number -- Рег. номер позиции состава оборудования - ) return number -- Код действия с моделью (0 - нет доступных, 1 - запросить прогноз, 2 - обучить) + NEQCONFIG in number -- Рег. номер позиции состава оборудования + ) return number -- Код действия с моделью (0 - нет моделей, 1 - есть модели в процессе обучения, 2 - есть обученные модели) is + NRES PKG_STD.TNUMBER; -- Буфер для результата begin - return 2; - end CMML_ACT_BY_EQCONFIG; + /* По умолчанию - моделей нет */ + NRES := 0; + /* Обходим модели */ + for C in (select ML.STATUS + from UDO_T_EQUIPDSCM CM, + UDO_T_EQUIPDSCMML ML + where CM.EQOBJKIND = (select CF.OBJ_KIND from EQCONFIG CF where CF.RN = NEQCONFIG) + and CM.RN = ML.PRN) + loop + /* Модель есть */ + NRES := 1; + /* Если она уже обучена */ + if (C.STATUS = 2) then + NRES := 2; + end if; + end loop; + /* Возвращаем результат */ + return NRES; + end CMML_STATUS_BY_EQCONFIG; /* Список моделей по единице оборудования */ - function CMML_LIST_BY_EQCONFIG + procedure CMML_LIST_BY_EQCONFIG ( - NEQCONFIG in number -- Рег. номер позиции состава оборудования - ) return varchar2 -- Список моделей с системным разделителем по умолчанию + NEQCONFIG in number, -- Рег. номер позиции состава оборудования + COUT out clob -- Сериализованная таблица данных + ) is + RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы + RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы begin - return 'TCF;RUL;FP'; - end CMML_LIST_BY_EQCONFIG; + /* Инициализируем таблицу данных */ + 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 => 'NEQUIPDS', + SCAPTION => 'Рег. номер выборки', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NEQUIPDSCM', + SCAPTION => 'Рег. номер класса оборудования выборки', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SNEQUIPDS', + SCAPTION => 'Выборка', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'STASK', + 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 => 'NPRECISION_P', + SCAPTION => 'Точность (план)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NPRECISION_F', + SCAPTION => 'Точность (факт)', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'NSTATUS', + SCAPTION => 'Состояние', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SERR', + SCAPTION => 'Сообщение об ошибке', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, + BVISIBLE => false); + PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, + SNAME => 'SACTIONS', + SCAPTION => 'Действия', + SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR); + /* Обходим данные */ + for C in (select T.RN NRN, + CM.PRN NEQUIPDS, + T.PRN NEQUIPDSCM, + DS.NAME SNEQUIPDS, + T.TASK STASK, + T.PRECISION_P NPRECISION_P, + T.PRECISION_F NPRECISION_F, + T.STATUS NSTATUS, + T.ERR SERR + from UDO_T_EQUIPDSCMML T, + UDO_T_EQUIPDSCM CM, + UDO_T_EQUIPDS DS + where T.PRN = CM.RN + and CM.PRN = DS.RN + and CM.EQOBJKIND = (select CF.OBJ_KIND from EQCONFIG CF where CF.RN = NEQCONFIG) + order by T.RN) + loop + /* Добавляем колонки с данными */ + 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 => 'NEQUIPDS', NVALUE => C.NEQUIPDS); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NEQUIPDSCM', NVALUE => C.NEQUIPDSCM); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SNEQUIPDS', SVALUE => C.SNEQUIPDS); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'STASK', SVALUE => C.STASK); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NPRECISION_P', NVALUE => C.NPRECISION_P); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NPRECISION_F', NVALUE => C.NPRECISION_F); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'NSTATUS', NVALUE => C.NSTATUS); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SERR', SVALUE => C.SERR); + PKG_P8PANELS_VISUAL.TROW_ADD_COL(RROW => RDG_ROW, SNAME => 'SACTIONS'); + /* Добавляем строку в таблицу */ + 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 CMML_LIST_BY_EQCONFIG; end UDO_PKG_EQUIPDS; / diff --git a/db/UDO_PKG_EQUIPDS_BASE.pck b/db/UDO_PKG_EQUIPDS_BASE.pck index 4b2f34c..e916612 100644 --- a/db/UDO_PKG_EQUIPDS_BASE.pck +++ b/db/UDO_PKG_EQUIPDS_BASE.pck @@ -142,6 +142,10 @@ create or replace package UDO_PKG_EQUIPDS_BASE as SQUEUE_ID in varchar2, -- Идентификатор очереди обработки SERR in varchar2 -- Сообщение об ошибке ); + + /* Формирование подсказки для задачи "Выборки данных оборудования (классы оборудования, модели)" */ + function CMML_TASK_HINT + return varchar2; -- Подсказка для задачи end UDO_PKG_EQUIPDS_BASE; / @@ -455,6 +459,14 @@ create or replace package body UDO_PKG_EQUIPDS_BASE as T.ERR = SERR where T.RN = NRN; end CMML_SET_STATUS; + + /* Формирование подсказки для задачи "Выборки данных оборудования (классы оборудования, модели)" */ + function CMML_TASK_HINT + return varchar2 -- Подсказка для задачи + is + begin + return 'TCF - Оценка технического состояния (Technical Condition Forecast)
' || 'RUL - Прогнозирование остаточного ресурса (Remaining Useful Life)
' || 'FP - Прогнозирование отказа (Failure Predict)'; + end CMML_TASK_HINT; end UDO_PKG_EQUIPDS_BASE; / diff --git a/db/UDO_PKG_EQUIPTCF.pck b/db/UDO_PKG_EQUIPTCF.pck index f572a26..421bedc 100644 --- a/db/UDO_PKG_EQUIPTCF.pck +++ b/db/UDO_PKG_EQUIPTCF.pck @@ -166,13 +166,12 @@ text="Проверка прав доступа при формировании PKG_COND_BROKER.ADD_CONDITION_CODE(SCOLUMN_NAME => 'NAME', SCONDITION_NAME => 'SOBJ_KINDFrom', SJOINS => 'OBJ_KIND <- RN;EQOBJKIND'); - /* Модели прогнозирования */ - if (PKG_COND_BROKER.CONDITION_EXISTS(SCONDITION_NAME => 'SEQUIPDSCMMLFrom') = 1) then - PKG_COND_BROKER.ADD_CLAUSE(SCLAUSE => 'UPPER(' || - PKG_SQL_BUILD.PKG_NAME(SNAME => 'UDO_PKG_EQUIPDS.CMML_LIST_BY_EQCONFIG') || - '(RN)) like REPLACE(REPLACE(UPPER(:SEQUIPDSCMML), ''?'', ''_''), ''*'', ''%'')'); - PKG_COND_BROKER.BIND_VARIABLE(SVARIABLE_NAME => 'SEQUIPDSCMML', - SVALUE => PKG_COND_BROKER.GET_CONDITION_STR(SCONDITION_NAME => 'SEQUIPDSCMMLFrom')); + /* Действия с моделями прогнозирования */ + if (PKG_COND_BROKER.CONDITION_EXISTS(SCONDITION_NAME => 'NCMML_STATUSFrom') = 1) then + PKG_COND_BROKER.ADD_CLAUSE(SCLAUSE => PKG_SQL_BUILD.PKG_NAME(SNAME => 'UDO_PKG_EQUIPDS.CMML_STATUS_BY_EQCONFIG') || + '(RN) = :NCMML_STATUS'); + PKG_COND_BROKER.BIND_VARIABLE(SVARIABLE_NAME => 'NCMML_STATUS', + SVALUE => PKG_COND_BROKER.GET_CONDITION_NUM(SCONDITION_NAME => 'NCMML_STATUSFrom')); end if; end EQCONFIG_THOBJ_LIST_COND; @@ -194,6 +193,7 @@ text="Проверка прав доступа при формировании RO PKG_P8PANELS_VISUAL.TORDERS; -- Сортировки RDG PKG_P8PANELS_VISUAL.TDATA_GRID; -- Описание таблицы RDG_ROW PKG_P8PANELS_VISUAL.TROW; -- Строка таблицы + RCOL_VALS PKG_P8PANELS_VISUAL.TCOL_VALS; -- Предопределённые значения столбцов NROW_FROM PKG_STD.TREF; -- Номер строки с NROW_TO PKG_STD.TREF; -- Номер строки по CSQL clob; -- Буфер для запроса @@ -251,21 +251,16 @@ text="Проверка прав доступа при формировании SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, BORDER => true, BFILTER => true); + PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RCOL_VALS, NVALUE => 0, BCLEAR => true); + PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RCOL_VALS, NVALUE => 1); + PKG_P8PANELS_VISUAL.TCOL_VALS_ADD(RCOL_VALS => RCOL_VALS, NVALUE => 2); PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, - SNAME => 'SEQUIPDSCMML', - SCAPTION => 'Модели прогнозирования', - SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_STR, - SHINT => 'Модели прогнозирования - доступные для использования модели прогнозирования и задачи, ими решаемые:
' || - 'TCF - Оценка технического состояния (Technical Condition Forecast)
' || - 'RUL - Прогнозирование остаточного ресурса (Remaining Useful Life)
' || - 'FP - Прогнозирование отказа (Failure Predict)', - BORDER => true, - BFILTER => true); - PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_COL_DEF(RDATA_GRID => RDG, - SNAME => 'NEQUIPDSCMML_ACTION', - SCAPTION => 'Действия с моделью', + SNAME => 'NCMML_STATUS', + SCAPTION => 'Модель', SDATA_TYPE => PKG_P8PANELS_VISUAL.SDATA_TYPE_NUMB, - BVISIBLE => false); + BORDER => true, + BFILTER => true, + RCOL_VALS => RCOL_VALS); /* Обходим данные */ begin /* Добавляем подсказку совместимости */ @@ -281,8 +276,7 @@ text="Проверка прав доступа при формировании PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' ST.CODE SUSE_KIND,'); PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' C.OBJ_KIND NOBJ_KIND,'); PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' OK.NAME SOBJ_KIND,'); - PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'UDO_PKG_EQUIPDS.CMML_LIST_BY_EQCONFIG') || '(C.RN) SEQUIPDSCMML,'); - PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'UDO_PKG_EQUIPDS.CMML_ACT_BY_EQCONFIG') || '(C.RN) NEQUIPDSCMML_ACTION'); + PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => PKG_SQL_BUILD.PKG_NAME(SNAME => 'UDO_PKG_EQUIPDS.CMML_STATUS_BY_EQCONFIG') || '(C.RN) NCMML_STATUS'); PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' from EQCONFIG C'); PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' left outer join HLSTATETYPES ST on C.HLSTATETYPES = ST.RN,'); PKG_SQL_BUILD.APPEND(SSQL => CSQL, SELEMENT1 => ' EQOBJKIND OK'); @@ -315,9 +309,8 @@ text="Проверка прав доступа при формировании PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 5); PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 6); PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 7); - PKG_SQL_DML.DEFINE_COLUMN_STR(ICURSOR => ICURSOR, IPOSITION => 8); + PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 8); PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 9); - PKG_SQL_DML.DEFINE_COLUMN_NUM(ICURSOR => ICURSOR, IPOSITION => 10); /* Делаем выборку */ if (PKG_SQL_DML.EXECUTE(ICURSOR => ICURSOR) = 0) then null; @@ -349,14 +342,10 @@ text="Проверка прав доступа при формировании SNAME => 'SOBJ_KIND', ICURSOR => ICURSOR, NPOSITION => 7); - PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLS(RROW => RDG_ROW, - SNAME => 'SEQUIPDSCMML', + PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLN(RROW => RDG_ROW, + SNAME => 'NCMML_STATUS', ICURSOR => ICURSOR, NPOSITION => 8); - PKG_P8PANELS_VISUAL.TROW_ADD_CUR_COLN(RROW => RDG_ROW, - SNAME => 'NEQUIPDSCMML_ACTION', - ICURSOR => ICURSOR, - NPOSITION => 9); /* Добавляем строку в таблицу */ PKG_P8PANELS_VISUAL.TDATA_GRID_ADD_ROW(RDATA_GRID => RDG, RROW => RDG_ROW); end loop; @@ -388,9 +377,12 @@ text="Проверка прав доступа при формировании for C in (select T.RN NRN, T.CODE SCODE, T.NAME SNAME, - T.OPER_DATE DOPER_DATE - from EQCONFIG T - where T.RN = NEQCONFIG) + T.OPER_DATE DOPER_DATE, + OK.NAME SEQOBJKIND + from EQCONFIG T, + EQOBJKIND OK + where T.RN = NEQCONFIG + and T.OBJ_KIND = OK.RN) loop /* Соберем карточку */ XCARD := PKG_XMAKE.ELEMENT(ICURSOR => NCUR, @@ -407,7 +399,10 @@ text="Проверка прав доступа при формировании SVALUE => C.SNAME), RATTRIBUTE03 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR, SNAME => 'DOPER_DATE', - DVALUE => C.DOPER_DATE))); + DVALUE => C.DOPER_DATE), + RATTRIBUTE04 => PKG_XMAKE.ATTRIBUTE(ICURSOR => NCUR, + SNAME => 'SEQOBJKIND', + SVALUE => C.SEQOBJKIND))); end loop; /* Формируем XML-представление документа ответа */ XDOC := PKG_XMAKE.ELEMENT(ICURSOR => NCUR, SNAME => 'XDATA', RNODE00 => XCARD); diff --git a/panels/eqs_tech_cond_forecast/admin_tab.js b/panels/eqs_tech_cond_forecast/admin_tab.js index 0643458..377d3ea 100644 --- a/panels/eqs_tech_cond_forecast/admin_tab.js +++ b/panels/eqs_tech_cond_forecast/admin_tab.js @@ -8,6 +8,7 @@ //--------------------- import React, { useState, useEffect, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Grid, Stack, Icon, Button, IconButton } from "@mui/material"; //Интерфейсные компоненты import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { BackEndСtx } from "../../context/backend"; //Контекст взаимодействия с серверомs @@ -16,7 +17,8 @@ import { EquipDataSelectionList, EquipDataSelectionClassMachineIU, EquipDataSelectionClassMachineList, - EquipDataSelectionClassMachineCard + EquipDataSelectionClassMachineCard, + EquipDataSelectionClassMachineModelIU } from "./admin_tab_layout"; //Вспомогательные компоненты и вёрстка import { DS_RN_DEFAULT, @@ -37,7 +39,8 @@ const REFRESH_INITIAL = { dataSelection: null, dataSelectionClassMachine: null, dataSelectionClassMachineCard: 0, - dataSelectionClassMachineFilesList: 0 + dataSelectionClassMachineFilesList: 0, + dataSelectionClassMachineModelsList: 0 }; //Стили @@ -51,7 +54,7 @@ const STYLES = { //----------- //Закладка администрирования -const AdminTab = () => { +const AdminTab = ({ dataSelection = DS_RN_DEFAULT, dataSelectionClassMachine = null }) => { //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); @@ -59,16 +62,16 @@ const AdminTab = () => { const { pOnlineUserProcedure } = useContext(ApplicationСtx); //Собственное состояние - отображаемые диалоги - const [dialogs, setDialogs] = useState({ dataSelectionIU: false, dataSelectionClassMachineIU: false }); + const [dialogs, setDialogs] = useState({ dataSelectionIU: false, dataSelectionClassMachineIU: false, dataSelectionClassMachineModelIU: false }); //Собственное состояние - флаги обновления данных const [refresh, setRefresh] = useState(REFRESH_INITIAL); //Собственное состояние - выбранная выборка данных - const [equipDataSelection, setDataSelection] = useState(DS_RN_DEFAULT); + const [equipDataSelection, setDataSelection] = useState(dataSelection); //Собственное состояние - выбранный класс оборудования - const [equipDataSelectionClassMachine, setDataSelectionClassMachine] = useState(null); + const [equipDataSelectionClassMachine, setDataSelectionClassMachine] = useState(dataSelectionClassMachine); //Загрузка списка выборок данных const { equipDataSelectionList } = useEquipDataSelectionList(refresh.dataSelection); @@ -88,8 +91,11 @@ const AdminTab = () => { refresh.dataSelectionClassMachineFilesList ); - //Загрузка файлов класса оборудования - const { equipDataSelectionClassMachineModelsList } = useEquipDataSelectionClassMachineModelsList(equipDataSelectionClassMachine); + //Загрузка моделей класса оборудования + const { equipDataSelectionClassMachineModelsList } = useEquipDataSelectionClassMachineModelsList( + equipDataSelectionClassMachine, + refresh.dataSelectionClassMachineModelsList + ); //При смене выборки данных const handleDataSelectionChange = value => { @@ -166,37 +172,71 @@ const AdminTab = () => { //При нажатии "Загрузить на сервер" в списке файлов карточки класса оборудования const handleUploadEquipDataSelectionClassMachineFiles = async equipDSCM => { - await executeStored({ - stored: "UDO_PKG_EQUIPDS.CMFL_UPLOAD", - args: { - NEQUIPDSCM: equipDSCM - } - }); + await executeStored({ stored: "UDO_PKG_EQUIPDS.CMFL_UPLOAD", args: { NEQUIPDSCM: equipDSCM } }); setRefresh(pv => ({ ...pv, dataSelectionClassMachineFilesList: pv.dataSelectionClassMachineFilesList + 1 })); }; //При нажатии "Передать внешней системе" в списке файлов карточки класса оборудования const handleSendMdEquipDataSelectionClassMachineFiles = async equipDSCM => { - await executeStored({ - stored: "UDO_PKG_EQUIPDS.CMFL_SEND_MD", - args: { - NEQUIPDSCM: equipDSCM - } - }); + await executeStored({ stored: "UDO_PKG_EQUIPDS.CMFL_SEND_MD", args: { NEQUIPDSCM: equipDSCM } }); setRefresh(pv => ({ ...pv, dataSelectionClassMachineFilesList: pv.dataSelectionClassMachineFilesList + 1 })); }; + //При нажатии на "Добавить модель класса оборудования" + const handleAddEquipDataSelectionClassMachineModel = () => setDialogs(pv => ({ ...pv, dataSelectionClassMachineModelIU: true })); + + //При нажатии на "Удалить модель класса оборудования" + const handleDeleteEquipDataSelectionClassMachineModel = async equipDSCMML => { + await executeStored({ stored: "UDO_PKG_EQUIPDS.CMML_DEL", args: { NRN: equipDSCMML } }); + setRefresh(pv => ({ ...pv, dataSelectionClassMachineModelsList: pv.dataSelectionClassMachineModelsList + 1 })); + }; + + //При отмене диалога IU модели класса оборудования + const handleEquipDataSelectionClassMachineModelIUCancel = () => setDialogs(pv => ({ ...pv, dataSelectionClassMachineModelIU: false })); + + //При сохранении диалога IU модели класса оборудования + const handleEquipDataSelectionClassMachineModelIUOk = async values => { + await executeStored({ + stored: "UDO_PKG_EQUIPDS.CMML_INS", + args: { + NPRN: equipDataSelectionClassMachine, + STASK: values.task, + NPRECISION_P: parseInt(values.precisionP) + } + }); + setDialogs(pv => ({ ...pv, dataSelectionClassMachineModelIU: false })); + setRefresh(pv => ({ ...pv, dataSelectionClassMachineModelsList: pv.dataSelectionClassMachineModelsList + 1 })); + }; + + //При нажатии "Обучить" в списке моделей карточки класса оборудования + const handleSendRqEquipDataSelectionClassMachineModel = async equipDSCMML => { + await executeStored({ stored: "UDO_PKG_EQUIPDS.CMML_SEND_RQ", args: { NEQUIPDSCMML: equipDSCMML } }); + setRefresh(pv => ({ ...pv, dataSelectionClassMachineModelsList: pv.dataSelectionClassMachineModelsList + 1 })); + }; + + //При измении выборки данных через свойства + useEffect(() => { + setDataSelection(dataSelection); + }, [dataSelection]); + + //При изменеии класса оборудования через свойства + useEffect(() => { + setDataSelectionClassMachine(dataSelectionClassMachine); + }, [dataSelectionClassMachine]); + //При изменении списка выборок данных useEffect(() => { if (refresh.action == "INS" && refresh.dataSelection) { setDataSelection(refresh.dataSelection); setDataSelectionClassMachine(null); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [equipDataSelectionList]); //При изменении списка классов оборудования выборки данных useEffect(() => { if (refresh.action == "INS" && refresh.dataSelectionClassMachine) setDataSelectionClassMachine(refresh.dataSelectionClassMachine); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [equipDataSelectionClassMachineList]); //Генерация содержимого @@ -211,6 +251,12 @@ const AdminTab = () => { onCancel={handleEquipDataSelectionClassMachineIUCancel} /> ) : null} + {dialogs.dataSelectionClassMachineModelIU ? ( + + ) : null} @@ -251,6 +297,9 @@ const AdminTab = () => { onClassMachineFilesMake={handleMakeEquipDataSelectionClassMachineFiles} onClassMachineFilesUpload={handleUploadEquipDataSelectionClassMachineFiles} onClassMachineFilesSendMd={handleSendMdEquipDataSelectionClassMachineFiles} + onClassMachineModelAdd={handleAddEquipDataSelectionClassMachineModel} + onClassMachineModelDelete={handleDeleteEquipDataSelectionClassMachineModel} + onClassMachineModelSendRq={handleSendRqEquipDataSelectionClassMachineModel} /> @@ -260,6 +309,12 @@ const AdminTab = () => { ); }; +//Контроль свойств - Закладка администрирования +AdminTab.propTypes = { + dataSelection: PropTypes.number, + dataSelectionClassMachine: PropTypes.number +}; + //---------------- //Интерфейс модуля //---------------- diff --git a/panels/eqs_tech_cond_forecast/admin_tab_hooks.js b/panels/eqs_tech_cond_forecast/admin_tab_hooks.js index ccd144e..0a1a606 100644 --- a/panels/eqs_tech_cond_forecast/admin_tab_hooks.js +++ b/panels/eqs_tech_cond_forecast/admin_tab_hooks.js @@ -176,7 +176,7 @@ const useEquipDataSelectionClassMachineFilesList = (classMachine, refresh) => { }; //Загрузка списка моделей класса оборудования -const useEquipDataSelectionClassMachineModelsList = classMachine => { +const useEquipDataSelectionClassMachineModelsList = (classMachine, refresh) => { //Собственное состояние - флаг загрузки const [isLoading, setLoading] = useState(false); @@ -212,7 +212,7 @@ const useEquipDataSelectionClassMachineModelsList = classMachine => { } }; if (classMachine) loadData(); - }, [classMachine, executeStored]); + }, [classMachine, refresh, executeStored]); //Вернём данные return { equipDataSelectionClassMachineModelsList: data, equipDataSelectionClassMachineModelsListIsLoading: isLoading }; diff --git a/panels/eqs_tech_cond_forecast/admin_tab_layout.js b/panels/eqs_tech_cond_forecast/admin_tab_layout.js index 49dbec4..d4557d5 100644 --- a/panels/eqs_tech_cond_forecast/admin_tab_layout.js +++ b/panels/eqs_tech_cond_forecast/admin_tab_layout.js @@ -36,9 +36,10 @@ import { import { useTheme } from "@mui/material/styles"; //Темы оформления import { ApplicationСtx } from "../../context/application"; //Контекст приложения import { BUTTONS } from "../../../app.text"; //Текстовые ресурсы и константы -import { APP_BAR_HEIGHT, TABS_HEIGHT, SCROLL_STYLES } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка +import { APP_BAR_HEIGHT, TABS_HEIGHT, SCROLL_STYLES, formatModelStateValue } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения +import { DS_RN_DEFAULT } from "./admin_tab_hooks"; //Вспомогательные хуки //--------- //Константы @@ -104,7 +105,7 @@ const STYLES = { //------------------------------------ //Поле ввода из словаря -const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, dictionary, ...other }) => { +const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, dictionary, list, ...other }) => { //Значение элемента const [value, setValue] = useState(elementValue); @@ -119,6 +120,7 @@ const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, dict //Изменение значения элемента const handleChange = e => { + console.log(e.target.name, e.target.value); setValue(e.target.value); if (onChange) onChange(e.target.name, e.target.value); }; @@ -127,22 +129,45 @@ const IUDFormTextField = ({ elementCode, elementValue, labelText, onChange, dict return ( - {labelText} - - - list - - - ) : null - } - onChange={handleChange} - /> + {list ? ( + <> + {labelText} + + + ) : ( + <> + {labelText} + + + list + + + ) : null + } + onChange={handleChange} + /> + + )} ); @@ -154,7 +179,8 @@ IUDFormTextField.propTypes = { elementValue: PropTypes.string, labelText: PropTypes.string.isRequired, onChange: PropTypes.func, - dictionary: PropTypes.func + dictionary: PropTypes.func, + list: PropTypes.array }; //Кнопка с дополнительной подписью @@ -165,7 +191,9 @@ const ExtraCaptionButton = ({ caption, title, subtitle, maxWidth, onClick, theme //Генерация содержимого return ( - + {title} {subtitle} @@ -218,6 +246,37 @@ const filesListDataCellRender = ({ row, columnDef, theme }) => { } }; +//Форматирование колонок таблицы моделей класса оборудования выборки данных +const modelsListDataCellRender = ({ row, columnDef, theme, card, onDelete, onSendRq }) => { + switch (columnDef.name) { + case "NSTATUS": + return { + cellProps: { align: "left" }, + data: formatModelStateValue(theme, row.NSTATUS, row.SERR) + }; + case "SACTIONS": + return { + data: ( + + (onDelete ? onDelete(row.NRN) : null)}> + delete + + {row.NSTATUS == 0 ? ( + (onSendRq ? onSendRq(row.NRN) : null)} + theme={theme} + /> + ) : null} + + ) + }; + } +}; + //Форматирование ячеек заголовка таблиц файлов и моделей класса оборудования выборки данных const classMachineCardTableHeadCellRender = ({ columnDef }) => { switch (columnDef.name) { @@ -226,6 +285,8 @@ const classMachineCardTableHeadCellRender = ({ columnDef }) => { stackProps: { justifyContent: "left" }, cellProps: { align: "left" } }; + case "SACTIONS": + return { data: " " }; } }; @@ -401,6 +462,50 @@ EquipDataSelectionClassMachineIU.propTypes = { onCancel: PropTypes.func }; +//Диалог IU модели класса оборудования выборки данных +const EquipDataSelectionClassMachineModelIU = ({ onOk, onCancel }) => { + //Собственное состояние - значения формы + const [values, setValues] = useState({ + task: "RUL", + precisionP: "" + }); + + //Отработка воода значения в форму + const handleValueChanged = (name, value) => setValues(pv => ({ ...pv, [name]: value })); + + //Генерация содержимого + return ( + (onOk ? onCancel() : null)}> + Модель класса оборудования выборки данных + + + + + + + + + + ); +}; + +//Контроль свойств - Диалог IU модели класса оборудования выборки данных +EquipDataSelectionClassMachineModelIU.propTypes = { + onOk: PropTypes.func, + onCancel: PropTypes.func +}; + //Список выборок данных оборудования const EquipDataSelectionList = ({ list, value, onChange }) => { //При выборе элемента @@ -410,7 +515,14 @@ const EquipDataSelectionList = ({ list, value, onChange }) => { return ( Выборка данных оборудования - {list.map((item, i) => ( {item.SNAME} @@ -462,7 +574,10 @@ const EquipDataSelectionClassMachineCard = ({ onCardDelete, onClassMachineFilesMake, onClassMachineFilesUpload, - onClassMachineFilesSendMd + onClassMachineFilesSendMd, + onClassMachineModelAdd, + onClassMachineModelDelete, + onClassMachineModelSendRq }) => { //Подключаемся к теме const theme = useTheme(); @@ -479,6 +594,15 @@ const EquipDataSelectionClassMachineCard = ({ //При нажатии на "Загрузить на сервер" для файлов данных const handleClassMachineFilesSendMdClick = () => (onClassMachineFilesSendMd ? onClassMachineFilesSendMd(card.NRN) : null); + //При нажатии на "Добавить" для моделей + const handleClassMachineModelAddClick = () => (onClassMachineModelAdd ? onClassMachineModelAdd() : null); + + //При нажатии на "Удалить" для моделей + const handleClassMachineModelDeleteClick = equipDSCMML => (onClassMachineModelDelete ? onClassMachineModelDelete(equipDSCMML) : null); + + //При нажатии на "Обучить" для моделей + const handleClassMachineModelSendRqClick = equipDSCMML => (onClassMachineModelSendRq ? onClassMachineModelSendRq(equipDSCMML) : null); + //Генерация содержимого return ( @@ -525,10 +649,7 @@ const EquipDataSelectionClassMachineCard = ({ Модели + + add + + modelsListDataCellRender({ + ...prms, + theme, + card, + onDelete: handleClassMachineModelDeleteClick, + onSendRq: handleClassMachineModelSendRqClick + }) + } /> @@ -577,7 +707,10 @@ EquipDataSelectionClassMachineCard.propTypes = { onCardDelete: PropTypes.func, onClassMachineFilesMake: PropTypes.func, onClassMachineFilesUpload: PropTypes.func, - onClassMachineFilesSendMd: PropTypes.func + onClassMachineFilesSendMd: PropTypes.func, + onClassMachineModelAdd: PropTypes.func, + onClassMachineModelDelete: PropTypes.func, + onClassMachineModelSendRq: PropTypes.func }; //---------------- @@ -587,6 +720,7 @@ EquipDataSelectionClassMachineCard.propTypes = { export { EquipDataSelectionIU, EquipDataSelectionClassMachineIU, + EquipDataSelectionClassMachineModelIU, EquipDataSelectionList, EquipDataSelectionClassMachineList, EquipDataSelectionClassMachineCard diff --git a/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast.js b/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast.js index 899446c..a27ce88 100644 --- a/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast.js +++ b/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast.js @@ -17,6 +17,13 @@ import { ForecastTab } from "./forecast_tab"; //Интерфейс прогно //Константы //--------- +//Состояние по умолчанию +const STATE_INITIAL = { + mode: MODES.FORECAST, + dataSelection: -1, + dataSelectionClassMachine: null +}; + //Стили const STYLES = { TABS_CONTAINER_BOX: { borderBottom: 1, borderColor: "divider" } @@ -29,25 +36,34 @@ const STYLES = { //Корневая панель прогнозирования технического состояния const EqsTechCondForecast = () => { //Собственное состояние - const [mode, setMode] = useState(MODES.ADMIN); + const [state, setState] = useState(STATE_INITIAL); //При переключении закладки - const handleTabChange = (e, newValue) => setMode(newValue); + const handleTabChange = (e, newValue) => setState(pv => ({ ...pv, mode: newValue })); + + //При запросе перехода на закладку администрирования + const handleGoToAdmin = (dataSelection, dataSelectionClassMachine) => + setState(pv => ({ + ...pv, + mode: MODES.ADMIN, + dataSelection: dataSelection ? dataSelection : STATE_INITIAL.dataSelection, + dataSelectionClassMachine: dataSelectionClassMachine ? dataSelectionClassMachine : STATE_INITIAL.dataSelectionClassMachine + })); //Генерация содержимого return ( <> - + - - + + - - + + ); diff --git a/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast_lyaout.js b/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast_lyaout.js index ead138e..45b9516 100644 --- a/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast_lyaout.js +++ b/panels/eqs_tech_cond_forecast/eqs_tech_cond_forecast_lyaout.js @@ -9,7 +9,7 @@ import React from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента -import { Box } from "@mui/material"; //Интерфейсные компоненты +import { Box, Stack, Icon } from "@mui/material"; //Интерфейсные компоненты //--------- //Константы @@ -56,6 +56,24 @@ const SCROLL_STYLES = { //Тело модуля //----------- +//Формирование значения для колонки "Состояние" модели класса оборудования выборки данных +const formatModelStateValue = (theme, value, err) => { + const [text, icon, color] = + value == 0 + ? ["Зарегистрирована", "app_registration", null] + : value == 1 + ? ["Обрабатывается внешней системой", "manage_history", theme.palette.warning.main] + : value == 2 + ? ["Успешно обработана внешней системой", "check_circle", theme.palette.primary.main] + : [`Ошибка обработки внешней системой: ${err}`, "error", theme.palette.error.main]; + return ( + + {icon} + {text} + + ); +}; + //Закладка const TabPanel = ({ mode, value, children }) => ; @@ -70,4 +88,4 @@ TabPanel.propTypes = { //Интерфейс модуля //---------------- -export { MODES, APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_MORE_HEIGHT, TABLE_FILTERS_HEIGHT, SCROLL_STYLES, TabPanel }; +export { MODES, APP_BAR_HEIGHT, TABS_HEIGHT, TABLE_MORE_HEIGHT, TABLE_FILTERS_HEIGHT, SCROLL_STYLES, formatModelStateValue, TabPanel }; diff --git a/panels/eqs_tech_cond_forecast/forecast_tab.js b/panels/eqs_tech_cond_forecast/forecast_tab.js index b1e5711..e45aa2e 100644 --- a/panels/eqs_tech_cond_forecast/forecast_tab.js +++ b/panels/eqs_tech_cond_forecast/forecast_tab.js @@ -8,14 +8,23 @@ //--------------------- import React, { useState, useContext } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Grid } from "@mui/material"; //Интерфейсные компоненты import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; //Дерево +import { useTreeViewApiRef } from "@mui/x-tree-view/hooks/useTreeViewApiRef"; //API дерева import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных 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 { TechObjCard, eqConfigTechObjTableValueFormatter } from "./forecast_tab_layout"; //Вспомогательные компоненты и вёрстка -import { useEqConfigTree, useEqConfigTechObjTable, useEqConfigTechObjCard, findTreeItem, needLoadLevel } from "./forecast_tab_hooks"; //Вспомогательные хуки +import { TechObjCard, eqConfigTechObjTableValueFormatter, eqConfigTechObjTableDataCellRender } from "./forecast_tab_layout"; //Вспомогательные компоненты и вёрстка +import { + useEqConfigTree, + useEqConfigTechObjTable, + useEqConfigTechObjCard, + useTechObjModelsList, + findTreeItem, + needLoadLevel +} from "./forecast_tab_hooks"; //Вспомогательные хуки //--------- //Константы @@ -49,10 +58,13 @@ const TECH_OBJ_SPEC_INIT = { parent: null, filters: [], orders: [], pageNumber: //----------- //Закладка прогнозирования -const ForecastTab = () => { +const ForecastTab = ({ onGoToAdmin }) => { //Подключение к контексту сообщений const { InlineMsgInfo } = useContext(MessagingСtx); + //Подключение к API дерева + const apiRef = useTreeViewApiRef(); + //Собственное состояние - текущая загружаемая ветка const [loadingTreeItem, setLoadingTreeItem] = useState(0); @@ -73,9 +85,12 @@ const ForecastTab = () => { techObjSpec.pageNumber ); - //Загрузчик карточки выбоанного технического объекта + //Загрузчик карточки выбранного технического объекта const { techObjCard, techObjCardIsLoading } = useEqConfigTechObjCard(techObjCardId); + //Загрузчик моделей выбранного технического объекта + const { techObjModelsList, techObjModelsListIsLoading } = useTechObjModelsList(techObjCardId); + //Обработка развёртывания/свёртывания уровня дерева const handleTreeItemExpansionToggle = (event, itemId, isExpanded) => isExpanded && needLoadLevel(eQconfigTree, itemId) ? setLoadingTreeItem(parseInt(itemId)) : null; @@ -99,6 +114,23 @@ const ForecastTab = () => { //При изменении количества отображаемых страниц спецификации технических объектов const handleTechObjSpecPagesCountChanged = () => setTechObjSpec(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 })); + //При выполнении действия в колоке "Модели" таблицы технических объектов + const handleCMMLStatusClick = (event, row) => { + if (row.NCMML_STATUS == 0) { + if (onGoToAdmin) onGoToAdmin(); + } else { + const item = findTreeItem(eQconfigTree, row.NRN); + if (item && item?.showCard) apiRef.current?.focusItem(event, item.id); + else setTechObjCardId(parseInt(row.NRN)); + } + }; + + //При переходе к закладке администрирования из карточки технического объекта + const handleTechObjeCardGoToModel = model => (onGoToAdmin ? onGoToAdmin(model.NEQUIPDS, model.NEQUIPDSCM) : null); + + //При закрытии карточки технического объекта + const handleTechObjeCardClose = () => setTechObjCardId(null); + //Текст при отсутствии данных в списке технических объектов const noDataFoundText = techObjsDataGridIsLoading || !techObjsDataGrid.init @@ -113,13 +145,23 @@ const ForecastTab = () => { - + {techObjCardId ? ( - !techObjCardIsLoading ? ( - + !techObjCardIsLoading && !techObjModelsListIsLoading ? ( + ) : null ) : techObjsDataGrid.init ? ( { fixedHeader={true} reloading={false} valueFormatter={eqConfigTechObjTableValueFormatter} + dataCellRender={prms => eqConfigTechObjTableDataCellRender({ ...prms, onCMMLStatus: handleCMMLStatusClick })} onFilterChanged={handleTechObjSpecFilterChanged} onOrderChanged={handleTechObjSpecOrderChanged} onPagesCountChanged={handleTechObjSpecPagesCountChanged} @@ -149,6 +192,11 @@ const ForecastTab = () => { ); }; +//Контроль свойств - Закладка прогнозирования +ForecastTab.propTypes = { + onGoToAdmin: PropTypes.func +}; + //---------------- //Интерфейс модуля //---------------- diff --git a/panels/eqs_tech_cond_forecast/forecast_tab_hooks.js b/panels/eqs_tech_cond_forecast/forecast_tab_hooks.js index c4d43fa..3cb34c1 100644 --- a/panels/eqs_tech_cond_forecast/forecast_tab_hooks.js +++ b/panels/eqs_tech_cond_forecast/forecast_tab_hooks.js @@ -126,7 +126,7 @@ const useEqConfigTechObjTable = (parent, filters, orders, pageNumber) => { NINCLUDE_DEF: pageNumber == 1 ? 1 : 0 }, respArg: "COUT", - loader: false + loader: true }); setData(pv => ({ ...pv, @@ -182,8 +182,51 @@ const useEqConfigTechObjCard = id => { return { techObjCard: card, techObjCardIsLoading: isLoading }; }; +//Загрузка списка моделей единицы оборудования +const useTechObjModelsList = (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.CMML_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 { techObjModelsList: data, techObjModelsListIsLoading: isLoading }; +}; + //---------------- //Интерфейс модуля //---------------- -export { useEqConfigTree, useEqConfigTechObjTable, useEqConfigTechObjCard, findTreeItem, needLoadLevel }; +export { useEqConfigTree, useEqConfigTechObjTable, useEqConfigTechObjCard, findTreeItem, needLoadLevel, useTechObjModelsList }; diff --git a/panels/eqs_tech_cond_forecast/forecast_tab_layout.js b/panels/eqs_tech_cond_forecast/forecast_tab_layout.js index ef3fca8..4803d16 100644 --- a/panels/eqs_tech_cond_forecast/forecast_tab_layout.js +++ b/panels/eqs_tech_cond_forecast/forecast_tab_layout.js @@ -9,8 +9,13 @@ import React from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента -import { Box, Card, CardContent, Typography, CardActions, Button } from "@mui/material"; //Интерфейсные компоненты +import { Stack, Icon, Box, Card, CardContent, Typography, CardActions, Button, IconButton, Divider } from "@mui/material"; //Интерфейсные компоненты +import { useTheme } from "@mui/material/styles"; //Темы оформления +import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../components/p8p_data_grid"; //Таблица данных +import { P8P_DATA_GRID_CONFIG_PROPS } from "../../config_wrapper"; //Подключение компонентов к настройкам приложения import { formatDateRF } from "../../core/utils"; //Вспомогательные функции +import { SCROLL_STYLES, formatModelStateValue } from "./eqs_tech_cond_forecast_lyaout"; //Общие вспомогательные компоненты и вёрстка +import { Link } from "react-router-dom"; //--------- //Константы @@ -19,7 +24,59 @@ import { formatDateRF } from "../../core/utils"; //Вспомогательны //Стили const STYLES = { TECH_OBJ_CARD_TITLE: { fontSize: 14 }, - TECH_OBJ_CARD_SUB_TITLE: { mb: 1.5 } + TECH_OBJ_CARD_SUB_TITLE: { mb: 1.5 }, + EQ_ML_TABLE: { + maxHeight: "200px", + ...SCROLL_STYLES + } +}; + +//------------------------------------ +//Вспомогательные функции и компоненты +//------------------------------------ + +//Форматирование ячеек заголовка таблицы моделей единицы оборудования +const techObjCardModelsTableHeadCellRender = ({ columnDef }) => { + switch (columnDef.name) { + case "NSTATUS": + return { + stackProps: { justifyContent: "left" }, + cellProps: { align: "left" } + }; + case "SACTIONS": + return { data: " " }; + } +}; + +//Форматирование колонок таблицы моделей класса оборудования выборки данных +const techObjCardModelsTableDataCellRender = ({ row, columnDef, theme, onGoToModel, onGetPrediction }) => { + switch (columnDef.name) { + case "SNEQUIPDS": + case "STASK": + case "NPRECISION_P": + case "NPRECISION_F": + return { + data: ( + onGoToModel(row)}> + {row[columnDef.name]} + + ) + }; + case "NSTATUS": + return { + cellProps: { align: "left" }, + data: formatModelStateValue(theme, row.NSTATUS, row.SERR) + }; + case "SACTIONS": + return { + data: + row.NSTATUS == 2 ? ( + + ) : null + }; + } }; //----------- @@ -27,27 +84,71 @@ const STYLES = { //----------- //Карточка технического объекта -const TechObjCard = ({ cardData }) => { +const TechObjCard = ({ cardData, modelsList, onClose, onGoToModel }) => { + //Подключаемся к теме + const theme = useTheme(); + + //При нажатии на кнопку закрытия карточки + const handleClose = () => (onClose ? onClose() : null); + + //При нажатии на переход к модели + const handleGoToModelClick = model => (onGoToModel ? onGoToModel(model) : null); + + //При нажатии на запрос предсказания + const handleGetPredictionClick = model => { + console.log(model); + }; + //Генерация содержимого return ( - - Технический объект - + + + close + + + Технический объект + + {cardData.SCODE} - - {cardData.DOPER_DATE} + + {`Введён в эксплуатацию: ${formatDateRF(cardData.DOPER_DATE)}`} +
+ {`Класс: ${cardData.SEQOBJKIND}`}
- {cardData.SNAME} + {cardData.SNAME} +
+ + + Модели + + + techObjCardModelsTableDataCellRender({ + ...prms, + theme, + onGoToModel: handleGoToModelClick, + onGetPrediction: handleGetPredictionClick + }) + } + /> +
- - - +
@@ -56,20 +157,49 @@ const TechObjCard = ({ cardData }) => { //Контроль свойств - Карточка технического объекта TechObjCard.propTypes = { - cardData: PropTypes.object + cardData: PropTypes.object.isRequired, + modelsList: PropTypes.object.isRequired, + onClose: PropTypes.func, + onGoToModel: PropTypes.func }; +//Формирование значения для колонки "Медель" таблицы технических объектов +const formatTechObjCMMLStatusValue = value => (value == 0 ? "Обучить" : value == 1 ? "Обучается" : "Прогнозировать"); + +//Формирование цвета для колонки "Медель" таблицы технических объектов +const formatTechObjCMMLStatusColor = value => (value == 0 ? "primary" : value == 1 ? "warning" : "success"); + //Форматирование значений колонок таблицы технических объектов const eqConfigTechObjTableValueFormatter = ({ value, columnDef }) => { switch (columnDef.name) { case "DOPER_DATE": return formatDateRF(value); + case "NCMML_STATUS": + return formatTechObjCMMLStatusValue(value); } return value; }; +//Форматирование колонок таблицы технических объектов +const eqConfigTechObjTableDataCellRender = ({ row, columnDef, onCMMLStatus }) => { + switch (columnDef.name) { + case "NCMML_STATUS": + return { + data: ( + + ) + }; + } +}; + //---------------- //Интерфейс модуля //---------------- -export { TechObjCard, eqConfigTechObjTableValueFormatter }; +export { TechObjCard, eqConfigTechObjTableValueFormatter, eqConfigTechObjTableDataCellRender };