Расчет вероятности выхода из строи и формирование прогноза с учетом и графиков ремонтов и ремонтных ведомостей

This commit is contained in:
Mikhail Chechnev 2024-08-20 12:21:24 +03:00
parent 355a53261e
commit e59247fd2d
4 changed files with 135 additions and 51 deletions

View File

@ -1,5 +1,5 @@
create or replace package UDO_PKG_EQUIPDS_BASE as create or replace package UDO_PKG_EQUIPDS_BASE as
/* Считывание записи "Выборки данных оборудования" по регистрационному номеру */ /* Считывание записи "Выборки данных оборудования" по регистрационному номеру */
function GET function GET
( (
@ -199,6 +199,14 @@ create or replace package UDO_PKG_EQUIPDS_BASE as
function CMML_TASK_HINT function CMML_TASK_HINT
return varchar2; -- Подсказка для задачи return varchar2; -- Подсказка для задачи
/* Вычисление вероятности выхода из строя на дату по RUL-прогнозу */
function CMML_RUL_BREAKDOWN_PROB
(
NFORECAST in number, -- RUL-прогноз (интервалов до перехода в предельное состояние)
DFORECAST_DATE in date, -- Дата прогноза
DDATE in date -- Дата на
) return number; -- Значение вероятности
/* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */ /* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
procedure CMMLH_INS procedure CMMLH_INS
( (
@ -722,6 +730,41 @@ create or replace package body UDO_PKG_EQUIPDS_BASE as
'<b>FP</b> - ' || CMML_TASK_NAME(STASK => 'FP') || ' (<b>F</b>ailure <b>P</b>redict)'; '<b>FP</b> - ' || CMML_TASK_NAME(STASK => 'FP') || ' (<b>F</b>ailure <b>P</b>redict)';
end CMML_TASK_HINT; end CMML_TASK_HINT;
/* Вычисление вероятности выхода из строя на дату по RUL-прогнозу */
function CMML_RUL_BREAKDOWN_PROB
(
NFORECAST in number, -- RUL-прогноз (интервалов до перехода в предельное состояние)
DFORECAST_DATE in date, -- Дата прогноза
DDATE in date -- Дата на
) return number -- Значение вероятности
is
NRES PKG_STD.TNUMBER; -- Буфер для результата
begin
/* Проверим параметры */
if ((NFORECAST is null) or (DFORECAST_DATE is null) or (DDATE is null) or (DDATE < DFORECAST_DATE) or
(DFORECAST_DATE > sysdate) or (DDATE < sysdate)) then
return null;
end if;
/* Проверяем пограничные значения */
if (NFORECAST = 0) or ((DDATE - sysdate) = 0) then
return 100;
end if;
if (sysdate + NFORECAST > DDATE) then
return 0;
end if;
/* Вычисляем */
NRES := 100 - ROUND((NFORECAST - (TRUNC(sysdate) - TRUNC(DFORECAST_DATE))) / (DDATE - sysdate) * 100);
/* Корректируем флуктуации */
if (NRES > 100) then
NRES := 100;
end if;
if (NRES < 0) then
NRES := 0;
end if;
/* Возвращаем результат */
return NRES;
end CMML_RUL_BREAKDOWN_PROB;
/* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */ /* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
procedure CMMLH_INS procedure CMMLH_INS
( (

View File

@ -608,6 +608,34 @@ text="Проверка прав доступа при формировании
end loop; end loop;
end EQCONFIG_THOBJ_SELECT_EQRPSHT; end EQCONFIG_THOBJ_SELECT_EQRPSHT;
/* Получение даты следующего ремонта по ремонтной ведомости или графику ТО и ремонтов (что раньше) технического объекта */
function EQCONFIG_THOBJ_GEN_NXTRPR
(
NEQCONFIG in number -- Рег. номер технического объекта
) return date -- Дата ближайшего ремонта
is
DNXTEQTCHSRV PKG_STD.TLDATE; -- Дата ближайшего ТО или ремонта по графику
DNXTEQRPSHT PKG_STD.TLDATE; -- Дата ближайшего ТО или ремонта по ремонтным ведомостям
DRES PKG_STD.TLDATE; -- Буфер для результата
begin
/* Определим дату ближайшего ТО или ремонта */
DNXTEQTCHSRV := EQCONFIG_THOBJ_GET_NXTEQTCHSRV(NEQCONFIG => NEQCONFIG);
/* Определим дату ближайшей ремонтной ведомости */
DNXTEQRPSHT := EQCONFIG_THOBJ_GET_NXTEQRPSHT(NEQCONFIG => NEQCONFIG);
/* Берм меньшую */
if ((DNXTEQRPSHT is not null) and (DNXTEQTCHSRV is not null)) then
if (DNXTEQRPSHT < DNXTEQTCHSRV) then
DRES := DNXTEQRPSHT;
else
DRES := DNXTEQTCHSRV;
end if;
else
DRES := COALESCE(DNXTEQTCHSRV, DNXTEQRPSHT);
end if;
/* Вернём результат */
return DRES;
end EQCONFIG_THOBJ_GEN_NXTRPR;
/* Формирование карточки технического объекта */ /* Формирование карточки технического объекта */
procedure EQCONFIG_THOBJ_CARD procedure EQCONFIG_THOBJ_CARD
( (
@ -768,11 +796,11 @@ text="Проверка прав доступа при формировании
RCH_DS PKG_P8PANELS_VISUAL.TCHART_DATASET; -- Набор данных RCH_DS PKG_P8PANELS_VISUAL.TCHART_DATASET; -- Набор данных
CCHART clob; -- Сериализованный график CCHART clob; -- Сериализованный график
NBREAKDOWN_PROB PKG_STD.TNUMBER; -- Вероятность выхода из строя NBREAKDOWN_PROB PKG_STD.TNUMBER; -- Вероятность выхода из строя
NBREAKDOWN_PROB_CUR PKG_STD.TNUMBER; -- Вероятность выхода из строя (для текущей позиции графика ТО и ремонтов) NBREAKDOWN_PROB_CUR PKG_STD.TNUMBER; -- Вероятность выхода из строя (для текущей позиции графика ТО и ремонтов/рем. ведомости)
DNEXT_REPAIR PKG_STD.TLDATE; -- Дата ближайшего ТО и ремонта DNEXT_REPAIR PKG_STD.TLDATE; -- Дата ближайшего ТО и ремонта по графику/рем. ведомости
begin begin
/* Вычислим вероятность выхода из строя до даты ближайшего ТО с учётом прогноза */ /* Вычислим вероятность выхода из строя до даты ближайшего ТО/ремонта по графику или рем. ведомости с учётом прогноза */
DNEXT_REPAIR := UDO_PKG_EQUIPTCF.EQCONFIG_THOBJ_GET_NXTEQTCHSRV(NEQCONFIG => NEQCONFIG); DNEXT_REPAIR := EQCONFIG_THOBJ_GEN_NXTRPR(NEQCONFIG => NEQCONFIG);
if (DNEXT_REPAIR is null) then if (DNEXT_REPAIR is null) then
NBREAKDOWN_PROB := 100; NBREAKDOWN_PROB := 100;
else else
@ -814,43 +842,41 @@ text="Проверка прав доступа при формировании
SNAME => 'DNEXT_REPAIR', SNAME => 'DNEXT_REPAIR',
RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => NCUR, RVALUE00 => PKG_XMAKE.VALUE(ICURSOR => NCUR,
DVALUE => DNEXT_REPAIR))); DVALUE => DNEXT_REPAIR)));
/* Если есть запланированные графики ТО и ремонта */ /* Если есть запланированные графики ТО и ремонта или ремонтные ведомости */
if (DNEXT_REPAIR is not null) then if (DNEXT_REPAIR is not null) then
/* Строим график вероятностей выхода из строя опираясь на них */ /* Строим график вероятностей выхода из строя опираясь на них */
RCH := PKG_P8PANELS_VISUAL.TCHART_MAKE(STYPE => PKG_P8PANELS_VISUAL.SCHART_TYPE_LINE, RCH := PKG_P8PANELS_VISUAL.TCHART_MAKE(STYPE => PKG_P8PANELS_VISUAL.SCHART_TYPE_LINE,
STITLE => 'Прогноз с учётом графика ТО и ремонтов', STITLE => 'Прогноз с учётом графика ТО и ремонтов/ремонтных ведомостей',
SLGND_POS => PKG_P8PANELS_VISUAL.SCHART_LGND_POS_TOP); SLGND_POS => PKG_P8PANELS_VISUAL.SCHART_LGND_POS_TOP);
/* Сформируем набор данных */ /* Сформируем набор данных */
RCH_DS := PKG_P8PANELS_VISUAL.TCHART_DATASET_MAKE(SCAPTION => 'Вероятность перехода в критическое состояние (%)'); RCH_DS := PKG_P8PANELS_VISUAL.TCHART_DATASET_MAKE(SCAPTION => 'Вероятность перехода в критическое состояние (%)');
/* Обходим ближайшие 5 позиций графика ТО и ремонтов */ /* Обходим ближайшие 5 позиций графика ТО и ремонтов или ремонтных ведомостей */
for C in (select ROWNUM, for C in (select ROWNUM,
D.SEQTCHSRV,
D.DDATEPRD_BEG D.DDATEPRD_BEG
from (select trim(COALESCE(K.CODE, ' ') || ' ' || TO_CHAR(T.DATEPRD_BEG, 'dd.mm.yyyy')) SEQTCHSRV, from (select T.DDATEPRD_BEG
T.DATEPRD_BEG DDATEPRD_BEG from (select P.DATEPRD_BEG DDATEPRD_BEG
from EQTCHSRV T, from EQTCHSRV P
EQTECSRVKIND K where P.EQCONFIG_TECH = NEQCONFIG
where T.EQCONFIG_TECH = NEQCONFIG and P.DATEPRD_BEG >= sysdate
and T.DATEPRD_BEG >= sysdate union
and T.EQTECSRVKIND = K.RN(+) select R.DATEPLAN_BEG DDATEPRD_BEG
order by T.DATEPRD_BEG) D from EQRPSHEETS R
where R.EQCONFIG = NEQCONFIG
and R.DATEPLAN_BEG >= sysdate) T
order by T.DDATEPRD_BEG) D
where ROWNUM <= 5) where ROWNUM <= 5)
loop loop
/* Добавим метку для графика */ /* Добавим метку для графика */
PKG_P8PANELS_VISUAL.TCHART_ADD_LABEL(RCHART => RCH, SLABEL => C.SEQTCHSRV); PKG_P8PANELS_VISUAL.TCHART_ADD_LABEL(RCHART => RCH, SLABEL => TO_CHAR(C.DDATEPRD_BEG, 'dd.mm.yyyy'));
/* Добавим график в набор данных */ /* Считаем вероятность выхода из строя в эту дату */
if (sysdate + NFORECAST > C.DDATEPRD_BEG) then NBREAKDOWN_PROB_CUR := UDO_PKG_EQUIPDS_BASE.CMML_RUL_BREAKDOWN_PROB(NFORECAST => NFORECAST,
NBREAKDOWN_PROB_CUR := 0; DFORECAST_DATE => DFORECAST_DATE,
else DDATE => C.DDATEPRD_BEG);
NBREAKDOWN_PROB_CUR := 100 - ROUND((NFORECAST - (TRUNC(sysdate) - TRUNC(DFORECAST_DATE))) / /* Если ещё не фиксировали вероятность для первой даты - сделаем это */
(C.DDATEPRD_BEG - sysdate) * 100); if (NBREAKDOWN_PROB is null) then
if (NBREAKDOWN_PROB_CUR > 100) then NBREAKDOWN_PROB := NBREAKDOWN_PROB_CUR;
NBREAKDOWN_PROB_CUR := 100;
end if;
if (NBREAKDOWN_PROB is null) then
NBREAKDOWN_PROB := NBREAKDOWN_PROB_CUR;
end if;
end if; end if;
/* Добавим элемент в набор данных */
PKG_P8PANELS_VISUAL.TCHART_DATASET_ADD_ITEM(RDATASET => RCH_DS, NVALUE => NBREAKDOWN_PROB_CUR); PKG_P8PANELS_VISUAL.TCHART_DATASET_ADD_ITEM(RDATASET => RCH_DS, NVALUE => NBREAKDOWN_PROB_CUR);
end loop; end loop;
/* Добавим набор данных в график */ /* Добавим набор данных в график */
@ -932,18 +958,18 @@ text="Проверка прав доступа при формировании
/* Вероятность выхода единицы оборудования из строя с учётом графика ТО и ремонтов и RUL-прогноза */ /* Вероятность выхода единицы оборудования из строя с учётом графика ТО и ремонтов и RUL-прогноза */
function EQCONFIG_THOBJ_TCHSRV_BRKDPROB function EQCONFIG_THOBJ_TCHSRV_BRKDPROB
( (
NEQCONFIG in number -- Рег. номер технического объекта NEQCONFIG in number -- Рег. номер технического объекта
) return number -- Вероятность выхода из строя ) return number -- Вероятность выхода из строя
is is
NRES PKG_STD.TNUMBER; -- Буфер для результата NRES PKG_STD.TNUMBER; -- Буфер для результата
DFORECAST_DATE PKG_STD.TLDATE; DFORECAST_DATE PKG_STD.TLDATE; -- Дата прогноза
NFORECAST PKG_STD.TNUMBER; -- Значение самго свежего прогноза модели NFORECAST PKG_STD.TNUMBER; -- Значение самго свежего прогноза модели
DNXTEQTCHSRV PKG_STD.TLDATE; -- Дата ближайшего ТО или ремонта DCALC_DATE PKG_STD.TLDATE; -- Дата расчета вероятности
begin begin
/* Определим дату ближайшего ТО или ремонта */ /* Определим дату ближайшего ТО или ремонта */
DNXTEQTCHSRV := EQCONFIG_THOBJ_GET_NXTEQTCHSRV(NEQCONFIG => NEQCONFIG); DCALC_DATE := EQCONFIG_THOBJ_GEN_NXTRPR(NEQCONFIG => NEQCONFIG);
/* Если дата есть */ /* Если дата есть */
if (DNXTEQTCHSRV is not null) then if (DCALC_DATE is not null) then
/* Вынем самый свежий прогноз */ /* Вынем самый свежий прогноз */
for C in (select T.RQ_DATE DFORECAST_DATE, for C in (select T.RQ_DATE DFORECAST_DATE,
T.FORECAST T.FORECAST
@ -960,7 +986,9 @@ text="Проверка прав доступа при формировании
end loop; end loop;
/* Если есть и прогноз */ /* Если есть и прогноз */
if (NFORECAST is not null) then if (NFORECAST is not null) then
NRES := 100 - ROUND((NFORECAST - (TRUNC(sysdate) - TRUNC(DFORECAST_DATE))) / (DNXTEQTCHSRV - sysdate) * 100); NRES := UDO_PKG_EQUIPDS_BASE.CMML_RUL_BREAKDOWN_PROB(NFORECAST => NFORECAST,
DFORECAST_DATE => DFORECAST_DATE,
DDATE => DCALC_DATE);
end if; end if;
end if; end if;
/* Вернём то, что собрали */ /* Вернём то, что собрали */

View File

@ -189,7 +189,8 @@ const ForecastTab = ({ onGoToAdmin }) => {
const handleTechObjeCardMakeEqRpSheet = () => setDialogs(pv => ({ ...pv, makeEqRpSheet: true })); const handleTechObjeCardMakeEqRpSheet = () => setDialogs(pv => ({ ...pv, makeEqRpSheet: true }));
//При необходимости обновления карточки технического объекта //При необходимости обновления карточки технического объекта
const handleCardDataRefresh = () => setRefresh(pv => ({ ...pv, techObjCard: pv.techObjCard + 1 })); const handleCardDataRefresh = () =>
setRefresh(pv => ({ ...pv, techObjCard: pv.techObjCard + 1, techObjForecastHistList: pv.techObjForecastHistList + 1 }));
//При нажатии "ОК" в диалоге формирования ремонтной ведомости //При нажатии "ОК" в диалоге формирования ремонтной ведомости
const handleTechObjMakeEqRpSheetOk = async values => { const handleTechObjMakeEqRpSheetOk = async values => {
@ -202,6 +203,7 @@ const ForecastTab = ({ onGoToAdmin }) => {
DPLANDATE_FROM: new Date(values.planDateFrom) DPLANDATE_FROM: new Date(values.planDateFrom)
} }
}); });
setRefresh(pv => ({ ...pv, techObjForecastHistList: pv.techObjForecastHistList + 1, techObjCard: pv.techObjCard + 1 }));
setDialogs(pv => ({ ...pv, makeEqRpSheet: false })); setDialogs(pv => ({ ...pv, makeEqRpSheet: false }));
pOnlineShowDictionary({ pOnlineShowDictionary({
unitCode: "EquipRepairSheets", unitCode: "EquipRepairSheets",

View File

@ -47,7 +47,8 @@ const STYLES = {
}, },
TECH_OBJ_FORECAST_DETAIL_DIALOG: { maxWidth: "600px" }, TECH_OBJ_FORECAST_DETAIL_DIALOG: { maxWidth: "600px" },
TECH_OBJ_FORECAST_DETAIL_CHART: { width: "550px", display: "flex", justifyContent: "center", paddingTop: "20px" }, TECH_OBJ_FORECAST_DETAIL_CHART: { width: "550px", display: "flex", justifyContent: "center", paddingTop: "20px" },
TECH_OBJ_MAKE_DATASET_DIALOG_CONTENT: { ...SCROLL_STYLES } TECH_OBJ_MAKE_DATASET_DIALOG_CONTENT: { ...SCROLL_STYLES },
FORECAST_DETAIL_NOTE: { display: "block", marginTop: "-5px" }
}; };
//------------------------------------ //------------------------------------
@ -162,19 +163,24 @@ const ForecastDetail = ({ date, forecast, onClose }) => {
{datails.taskName}: <b>{`${datails.forecast} ${datails.meas}`}</b> {datails.taskName}: <b>{`${datails.forecast} ${datails.meas}`}</b>
</Typography> </Typography>
<Stack direction={"row"}> <Stack direction={"row"}>
<Typography variant="subtitle1">Дата следующего ТО/ремонта по графику:&nbsp;</Typography> <Typography variant="subtitle1">Дата следующего ТО/ремонта:&nbsp;</Typography>
<Typography variant="subtitle1" color={datails.nextRepair ? "" : "error.main"}> <Typography variant="subtitle1" color={datails.nextRepair ? "" : "error.main"}>
<b>{datails.nextRepair ? formatDateRF(datails.nextRepair) : "Нет графиков ТО и ремонтов"}</b> <b>{datails.nextRepair ? `${formatDateRF(datails.nextRepair)}*` : "Нет графиков ТО и ремонтов/ремонтых ведомостей"}</b>
</Typography> </Typography>
</Stack> </Stack>
{datails.nextRepair ? (
<Typography variant="caption" color={"text.secondary"} sx={STYLES.FORECAST_DETAIL_NOTE}>
*по графику/ремонтной ведомости
</Typography>
) : null}
<Stack direction={"row"}> <Stack direction={"row"}>
<Typography variant="subtitle1">Bероятность перехода в критическое состояние:&nbsp;</Typography> <Typography variant="subtitle1">Bероятность перехода в критическое состояние:&nbsp;</Typography>
<Typography variant="subtitle1" color={datails.breakDownColor}> <Typography variant="subtitle1" color={datails.breakDownColor}>
<b>{`${datails.breakDown}%*`}</b> <b>{`${datails.breakDown}%*`}</b>
</Typography> </Typography>
</Stack> </Stack>
<Typography variant="caption" color={"text.secondary"}> <Typography variant="caption" color={"text.secondary"} sx={STYLES.FORECAST_DETAIL_NOTE}>
*до даты следующего ТО/ремонта по графику *до даты следующего ТО/ремонта по графику/ремонтной ведомости
</Typography> </Typography>
{chart.loaded ? <P8PChart {...chart} style={STYLES.TECH_OBJ_FORECAST_DETAIL_CHART} /> : null} {chart.loaded ? <P8PChart {...chart} style={STYLES.TECH_OBJ_FORECAST_DETAIL_CHART} /> : null}
{!chart.available ? ( {!chart.available ? (
@ -182,7 +188,7 @@ const ForecastDetail = ({ date, forecast, onClose }) => {
<Paper elevation={6}> <Paper elevation={6}>
<Box p={5}> <Box p={5}>
<Typography variant="body" color={"text.secondary"}> <Typography variant="body" color={"text.secondary"}>
Зарегистрируйте графики ТО и ремонтов <br /> Зарегистрируйте графики ТО и ремонтов/ремонтные ведомости <br />
для отображения диаграммы прогнозирования. для отображения диаграммы прогнозирования.
</Typography> </Typography>
</Box> </Box>
@ -242,11 +248,16 @@ const eqConfigTechObjTableDataCellRender = ({ row, columnDef, onCMMLStatus }) =>
}; };
case "NBREAKDOWN_PROB": case "NBREAKDOWN_PROB":
return { return {
data: row[columnDef.name] ? ( data:
<Button variant="outlined" color={row.SBREAKDOWN_PROB_COLOR} onClick={event => (onCMMLStatus ? onCMMLStatus(event, row) : null)}> row[columnDef.name] !== null && row[columnDef.name] !== "" ? (
{`${row[columnDef.name]}%`} <Button
</Button> variant="outlined"
) : null color={row.SBREAKDOWN_PROB_COLOR}
onClick={event => (onCMMLStatus ? onCMMLStatus(event, row) : null)}
>
{`${row[columnDef.name]}%`}
</Button>
) : null
}; };
} }
}; };