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

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

@ -199,6 +199,14 @@ create or replace package UDO_PKG_EQUIPDS_BASE as
function CMML_TASK_HINT
return varchar2; -- Подсказка для задачи
/* Вычисление вероятности выхода из строя на дату по RUL-прогнозу */
function CMML_RUL_BREAKDOWN_PROB
(
NFORECAST in number, -- RUL-прогноз (интервалов до перехода в предельное состояние)
DFORECAST_DATE in date, -- Дата прогноза
DDATE in date -- Дата на
) return number; -- Значение вероятности
/* Базовое добавление "Выборки данных оборудования (классы оборудования, модели, история запросов)" */
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)';
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
(

View File

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

View File

@ -189,7 +189,8 @@ const ForecastTab = ({ onGoToAdmin }) => {
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 => {
@ -202,6 +203,7 @@ const ForecastTab = ({ onGoToAdmin }) => {
DPLANDATE_FROM: new Date(values.planDateFrom)
}
});
setRefresh(pv => ({ ...pv, techObjForecastHistList: pv.techObjForecastHistList + 1, techObjCard: pv.techObjCard + 1 }));
setDialogs(pv => ({ ...pv, makeEqRpSheet: false }));
pOnlineShowDictionary({
unitCode: "EquipRepairSheets",

View File

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