当前位置: 首页 > news >正文

21、工业大数据分析与实时告警 (模拟根因分析) - /数据与物联网组件/bigdata-root-cause-analysis

76个工业组件库示例汇总

工业大数据分析与实时告警 - 停机根因分析 (模拟)

概述

这是一个交互式的 Web 组件,旨在模拟一个工业大数据分析平台的关键应用场景:生产线异常停机的实时告警与根因分析 (Root Cause Analysis, RCA)。组件通过模拟生产线状态、传感器数据和简单的分析规则,动态展示异常停机事件,并为选定的事件提供推测性的根因、相关数据快照和建议措施。

请注意:这是一个高度简化的前端模拟演示,不执行真实的复杂大数据分析或机器学习。所有生产线状态、传感器数据、停机事件、根因推断和置信度都是基于预设的简单规则和随机性在浏览器端模拟生成的。

主要功能

  • 生产线状态监控:
    • 左侧面板实时显示模拟的多条生产线(如装配线、加工中心等)及其当前状态:RUNNING (运行中), STOPPED (已停止), IDLE (空闲)。
    • 状态会基于模拟的传感器数据和运行逻辑动态变化。
    • 显示每条产线当前的连续运行时长 (Uptime)。
  • 实时停机告警:
    • 中间面板实时列出由于模拟的传感器异常而触发的"异常停机"事件告警。
    • 每个告警项显示所属生产线名称、事件发生时间,并按严重性(目前均为 Critical)标记。
    • 支持按时间"最新优先"或"最早优先"对告警列表进行排序。
    • 告警列表可点击,用于选择要进行根因分析的事件。
  • 停机根因分析 (模拟):
    • 右侧面板用于展示选定停机告警的详细分析结果。
    • 事件详情: 显示停机事件发生的产线和精确时间。
    • 推测根因: 基于一组预定义的、简化的规则(例如,温度和振动同时超标 -> 轴承故障),显示一个最可能的故障原因。
    • 置信度: 显示对推测根因的模拟置信度百分比。
    • 相关传感器数据: 展示导致停机时处于异常状态的关键传感器及其数值快照。
    • 历史模式/趋势 (占位符): 提供一个模拟的图表占位符,概念上表示与该故障模式相关的历史数据趋势。
    • 建议措施: 根据推测的根因,提供几条模拟的建议处理或维护措施。
    • 如果未选择任何告警,则显示提示信息。
  • 动态模拟与交互:
    • 传感器数据(温度、振动、压力、电流等)实时模拟波动。
    • 生产线状态根据传感器数据和预设逻辑(如阈值触发、随机空闲)自动变化。
    • 异常停机事件动态生成并添加到告警列表。
    • 用户点击告警项即可触发右侧面板更新分析结果。
  • 界面风格: 采用苹果科技工业风格,三栏布局,侧重信息清晰度和专业感,支持响应式设计。

如何使用

  1. 打开页面: 在浏览器中打开 index.html
  2. 观察产线状态: 在左侧面板观察各生产线的实时状态(运行、停止、空闲)和运行时长变化。
  3. 监控实时告警: 中间面板会实时出现模拟的异常停机告警。注意新告警的出现。
  4. 排序告警: 使用告警面板右上角的下拉菜单选择按"最新"或"最早"排序。
  5. 选择告警进行分析: 点击中间面板中的任意一个告警项。该项会被选中高亮。
  6. 查看根因分析: 右侧面板会更新,显示所选告警的详细信息,包括推测的根因、置信度、异常传感器数据快照、模拟的历史图表和建议措施。
  7. 取消选择: 再次点击已选中的告警项,将取消选择,右侧面板恢复显示提示信息。
  8. 观察动态: 持续观察,会发现新的停机事件不断产生,产线状态也会变化。

模拟细节

  • 生产线与传感器: 组件初始化时会创建几条生产线,每条线随机配置 2-4 种传感器(温度、振动、压力、电流),并定义了各传感器的正常范围、警告阈值和错误阈值。
  • 传感器数据模拟: 传感器值在正常范围内随机波动,但会受模拟的"磨损"影响(运行时长越长,越容易偏向阈值上限)。允许值超出阈值。
  • 状态转换:
    • RUNNING 状态下,如果检测到 1 个传感器达到错误阈值,或 2 个及以上传感器达到警告阈值,则有较大概率(概率与异常程度相关)触发状态变为 STOPPED,并生成停机告警。也有小概率随机进入 IDLE 状态。
    • IDLE 状态下,有一定概率恢复为 RUNNING
    • STOPPED 状态会持续一段时间,然后有小概率自动恢复为 IDLE(模拟故障后的临时处理)。
  • 停机告警生成: 当状态因异常从 RUNNING 变为 STOPPED 时,生成一个包含产线信息、时间戳和停机前最后一个传感器数据快照的告警对象。
  • 根因分析规则: JavaScript 代码中包含一个 RCA_RULES 数组,定义了简单的 IF (条件) THEN (原因, 置信度, 建议) 规则。分析时,会用停机前的异常传感器数据去匹配这些规则,找到第一个满足条件的规则作为分析结果。包含一个默认的"未知原因"规则作为后备。
  • 置信度: 主要由匹配到的规则预设,并加入少量随机波动。
  • 数据快照: 为了进行根因分析,系统会短暂缓存生产线最近几次的传感器读数快照,并在触发停机时使用最后一次快照。

文件结构

数据与物联网组件/bigdata-root-cause-analysis/
├── index.html         # 组件的 HTML 结构
├── styles.css         # 组件的 CSS 样式 (苹果科技工业风格, 三栏响应式)
├── script.js          # 组件的 JavaScript 逻辑 (模拟产线, 传感器, 停机, RCA)
└── README.md          # 本说明文件

技术栈

  • HTML5
  • CSS3 (使用了 CSS 变量, Grid 布局, Flexbox, 动画, 媒体查询)
  • JavaScript (原生 JS, 无外部库依赖)

效果展示

在这里插入图片描述

源码

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>工业大数据分析与实时告警</title><link rel="stylesheet" href="styles.css">
</head>
<body><div class="analysis-container"><header class="main-header"><h1>工业大数据分析与实时告警 (模拟根因分析)</h1><div class="header-info"><span class="status-tag" id="analysisStatus">分析运行中</span><span class="time-display" id="currentTime">--:--:--</span></div></header><main class="main-content"><!-- Column 1: Production Line Status --><section class="line-status-panel panel"><h2><i class="icon icon-lines"></i> 生产线状态监控</h2><ul id="productionLineList" class="line-list"><li class="placeholder">加载生产线数据...</li><!-- Line items populated by JS --></ul></section><!-- Column 2: Real-time Alerts / Downtime Events --><section class="alerts-panel panel"><h2><i class="icon icon-alert"></i> 实时停机告警 (<span id="activeAlertsCount">0</span>)</h2><div class="panel-toolbar"><label>排序: </label><select id="alertSortOrder"><option value="newest">最新优先</option><option value="oldest">最早优先</option></select></div><ul id="alertList" class="alert-item-list"><li class="placeholder">暂无告警</li><!-- Alert items populated by JS --></ul></section><!-- Column 3: Root Cause Analysis Details --><section class="rca-panel panel"><h2><i class="icon icon-analysis"></i> 停机根因分析详情</h2><div id="rcaDetailsContainer" class="rca-details-container"><div class="placeholder" id="rcaPlaceholder">请在左侧选择一个停机告警查看详情</div><!-- RCA details populated by JS --><div id="rcaContent" class="rca-content is-hidden"><h3 id="rcaEventTitle">事件: --</h3><p class="timestamp" id="rcaEventTime">时间: --</p><div class="analysis-section"><h4><i class="icon icon-cause"></i> 推测根因:</h4><p class="root-cause" id="rcaRootCause">分析中...</p><p class="confidence" id="rcaConfidence">置信度: --%</p></div><div class="analysis-section sensor-data-section"><h4><i class="icon icon-sensors"></i> 相关传感器数据 (停机前):</h4><ul id="rcaSensorData" class="sensor-data-list"><!-- Sensor data populated by JS --></ul></div><div class="analysis-section chart-section"><h4><i class="icon icon-chart"></i> 历史模式/趋势 (模拟图):</h4><div class="chart-placeholder" id="rcaHistoryChart"><div class="fake-chart rca-trend"></div><span class="chart-axis-label y-axis">综合指标</span><span class="chart-axis-label x-axis">历史时间</span></div></div><div class="analysis-section suggestions-section"><h4><i class="icon icon-suggestion"></i> 建议措施:</h4><ul id="rcaSuggestions" class="suggestions-list"><!-- Suggestions populated by JS --></ul></div></div></div></section></main><footer class="main-footer"><p>&copy; 2024 工业大数据分析模拟系统. 概念演示.</p></footer></div><script src="script.js"></script>
</body>
</html> 

styles.css

:root {--bg-color-light: #f9f9f9;--bg-color-container: #ffffff;--header-bg: #f5f5f7;--panel-bg: #ffffff;--border-color: #e1e1e1;--border-color-subtle: #ebebeb;--text-primary: #1d1d1f;--text-secondary: #515154;--text-label: #6e6e73;--accent-blue: #007aff;--accent-green: #34c759;--accent-orange: #ff9500;--accent-red: #ff3b30;--accent-purple: #af52de; /* For analysis/highlight */--accent-grey: #8e8e93;--status-running: var(--accent-green);--status-stopped: var(--accent-red);--status-idle: var(--accent-orange);--alert-critical: var(--accent-red);--alert-warning: var(--accent-orange);--list-item-hover-bg: #f0f0f0;--list-item-selected-bg: #e8f3ff;--list-item-selected-text: var(--accent-blue);--input-bg: #f0f2f5;--input-border: transparent;--input-focus-border: var(--accent-blue);--placeholder-text: #aaaaaa;--chart-placeholder-bg: #f8f8f8;--chart-axis-color: #b0b0b0;--shadow-color: rgba(0, 0, 0, 0.05);--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--font-monospace: "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--border-radius: 8px;--border-radius-small: 4px;--transition-speed: 0.2s;
}* {box-sizing: border-box;margin: 0;padding: 0;
}body {font-family: var(--font-family);background-color: var(--bg-color-light);color: var(--text-primary);line-height: 1.4;overflow-x: hidden;
}.analysis-container {max-width: 1700px;margin: 1rem auto;background-color: var(--bg-color-container);border-radius: var(--border-radius);box-shadow: 0 4px 12px var(--shadow-color);overflow: hidden;display: flex;flex-direction: column;height: calc(100vh - 2rem); /* Limit height */min-height: 680px; /* Minimum reasonable height */
}/* Header */
.main-header {background-color: var(--header-bg);padding: 0.75rem 1.5rem;border-bottom: 1px solid var(--border-color);flex-shrink: 0;display: flex;justify-content: space-between;align-items: center;
}.main-header h1 {font-size: 1.3rem;font-weight: 600;color: var(--text-primary);
}.header-info {display: flex;align-items: center;gap: 1rem;
}.status-tag {font-size: 0.8rem;font-weight: 500;padding: 0.2rem 0.6rem;border-radius: 12px;background-color: rgba(52, 199, 89, 0.1);color: var(--status-running);
}.status-tag.error {background-color: rgba(255, 59, 48, 0.1);color: var(--status-stopped);
}.time-display {font-size: 0.9rem;color: var(--text-secondary);font-family: var(--font-monospace);
}/* Main Content Layout */
.main-content {flex-grow: 1;padding: 1rem;overflow: hidden;display: grid;grid-template-columns: 300px 400px 1fr; /* Lines | Alerts | RCA */gap: 1rem;
}/* Panels */
.panel {background-color: var(--panel-bg);border-radius: var(--border-radius);padding: 1rem;display: flex;flex-direction: column;overflow: hidden;/* border: 1px solid var(--border-color); */
}.panel h2 {font-size: 1.1rem;font-weight: 600;margin-bottom: 1rem;padding-bottom: 0.6rem;border-bottom: 1px solid var(--border-color);display: flex;align-items: center;color: var(--text-primary);flex-shrink: 0;
}.panel h2 .icon {margin-right: 0.6rem;color: var(--accent-blue); /* Default icon color */
}.panel h2 span {font-weight: normal;font-size: 0.9em;color: var(--text-secondary);margin-left: 0.3rem;
}.panel-toolbar {margin-bottom: 0.75rem;display: flex;gap: 0.5rem;align-items: center;flex-shrink: 0;
}.panel-toolbar label {font-size: 0.85rem;color: var(--text-label);
}.panel-toolbar select {padding: 0.3rem 2rem 0.3rem 0.5rem; /* Adjust padding for select */font-size: 0.85rem;border: 1px solid var(--border-color-subtle);background-color: var(--input-bg);border-radius: var(--border-radius-small);height: auto;margin-left: auto; /* Push sort to right */
}/* Line Status Panel */
.line-list {list-style: none;overflow-y: auto;flex-grow: 1;
}.line-item {padding: 0.8rem 0.5rem;margin-bottom: 0.3rem;border-radius: var(--border-radius-small);transition: background-color var(--transition-speed);display: flex;justify-content: space-between;align-items: center;border-bottom: 1px solid var(--border-color-subtle);font-size: 0.9rem;
}
.line-item:last-child {border-bottom: none;
}.line-details {flex-grow: 1;
}.line-name {font-weight: 600;color: var(--text-primary);
}.line-uptime {font-size: 0.8rem;color: var(--text-secondary);margin-top: 0.1rem;display: block;
}.line-status-indicator {display: flex;align-items: center;gap: 0.4rem;font-size: 0.85rem;font-weight: 500;padding: 0.2rem 0.6rem;border-radius: var(--border-radius-small);flex-shrink: 0;min-width: 70px;justify-content: center;
}.line-status-indicator .dot {width: 8px;height: 8px;border-radius: 50%;display: inline-block;
}.line-status-indicator.running {color: var(--status-running);background-color: rgba(52, 199, 89, 0.1);
}
.line-status-indicator.running .dot { background-color: var(--status-running); }.line-status-indicator.stopped {color: var(--status-stopped);background-color: rgba(255, 59, 48, 0.1);
}
.line-status-indicator.stopped .dot { background-color: var(--status-stopped); }.line-status-indicator.idle {color: var(--status-idle);background-color: rgba(255, 149, 0, 0.1);
}
.line-status-indicator.idle .dot { background-color: var(--status-idle); }/* Alerts Panel */
.alert-item-list {list-style: none;overflow-y: auto;flex-grow: 1;
}.alert-item {padding: 0.7rem 0.5rem;margin-bottom: 0.3rem;border-radius: var(--border-radius-small);cursor: pointer;transition: background-color var(--transition-speed), border-left-color 0.1s;border-bottom: 1px solid var(--border-color-subtle);border-left: 4px solid transparent; /* Indicator for severity */font-size: 0.9rem;
}
.alert-item:last-child {border-bottom: none;
}.alert-item:hover {background-color: var(--list-item-hover-bg);
}.alert-item.selected {background-color: var(--list-item-selected-bg);/* border-left-color: var(--list-item-selected-text); */
}.alert-item.critical {border-left-color: var(--alert-critical);
}.alert-item.warning { /* If we add warnings later */border-left-color: var(--alert-warning);
}.alert-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 0.2rem;
}.alert-line-name {font-weight: 600;color: var(--text-primary);
}.alert-time {font-size: 0.8rem;color: var(--text-secondary);
}.alert-message {font-size: 0.85rem;color: var(--text-secondary);
}/* RCA Panel */
.rca-details-container {flex-grow: 1;overflow-y: auto; /* Scroll only the details area */padding-right: 0.5rem; /* Space for scrollbar */
}.rca-content {display: flex;flex-direction: column;gap: 1.5rem;
}.rca-content.is-hidden {display: none;
}#rcaPlaceholder:not(.is-hidden) + #rcaContent {display: none;
}
#rcaPlaceholder.is-hidden + #rcaContent {display: flex; /* Show content when placeholder is hidden */
}#rcaEventTitle {font-size: 1.2rem;font-weight: 600;color: var(--text-primary);margin-bottom: 0.1rem;
}.rca-details-container .timestamp {font-size: 0.85rem;color: var(--text-label);margin-bottom: 0.5rem;
}.analysis-section h4 {font-size: 0.95rem;font-weight: 600;color: var(--text-label);margin-bottom: 0.7rem;padding-bottom: 0.4rem;border-bottom: 1px solid var(--border-color-subtle);display: flex;align-items: center;
}.analysis-section h4 .icon {margin-right: 0.4rem;color: var(--accent-grey);font-size: 0.9em;
}.root-cause {font-size: 1.1rem;font-weight: 500;color: var(--accent-purple);margin-bottom: 0.3rem;
}.confidence {font-size: 0.9rem;color: var(--text-secondary);
}.sensor-data-list, .suggestions-list {list-style: none;display: grid;gap: 0.5rem;
}.sensor-data-section .sensor-data-list {grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); /* Responsive grid */
}.sensor-item {font-size: 0.85rem;background-color: var(--input-bg);padding: 0.5rem 0.7rem;border-radius: var(--border-radius-small);border: 1px solid var(--border-color-subtle);
}.sensor-name {display: block;font-weight: 500;color: var(--text-label);margin-bottom: 0.1rem;
}.sensor-value {font-weight: 600;font-family: var(--font-monospace);color: var(--text-primary);
}.sensor-value.abnormal {color: var(--accent-red);
}.suggestions-list li {font-size: 0.9rem;color: var(--text-secondary);padding-left: 1.2em; /* Indent suggestions */position: relative;
}.suggestions-list li::before {content: "•";position: absolute;left: 0;color: var(--accent-blue);font-weight: bold;
}/* Chart Placeholders */
.chart-placeholder {background-color: var(--chart-placeholder-bg);border-radius: var(--border-radius-small);display: flex;align-items: center;justify-content: center;position: relative;overflow: hidden;border: 1px solid var(--border-color);min-height: 180px;
}.chart-axis-label {position: absolute;font-size: 0.7rem;color: var(--chart-axis-color);transform: rotate(-90deg);white-space: nowrap;
}.chart-axis-label.y-axis {left: -10px;top: 50%;transform-origin: left top;
}.chart-axis-label.x-axis {bottom: -10px;left: 50%;transform: translateX(-50%);
}.fake-chart {width: 90%;height: 80%;position: relative;
}.fake-chart.rca-trend::after {content: '';position: absolute;inset: 5%; /* Smaller inset */background: repeating-linear-gradient(45deg,rgba(175, 82, 222, 0.05),rgba(175, 82, 222, 0.05) 10px,rgba(175, 82, 222, 0.1) 10px,rgba(175, 82, 222, 0.1) 20px);animation: fakeAnalysisPulse 4s ease-in-out infinite;border-radius: 3px;border: 1px dashed var(--accent-purple);
}@keyframes fakeAnalysisPulse {0%, 100% { opacity: 0.6; transform: scale(0.98); }50% { opacity: 1; transform: scale(1); }
}/* Footer */
.main-footer {background-color: var(--header-bg);padding: 0.6rem 1.5rem;text-align: center;font-size: 0.8rem;color: var(--text-secondary);border-top: 1px solid var(--border-color);flex-shrink: 0;
}/* Icons (Basic Placeholders) */
.icon::before {display: inline-block;font-weight: normal;font-style: normal;font-variant: normal;text-rendering: auto;-webkit-font-smoothing: antialiased;margin-right: 0.3em;
}
.icon-lines::before { content: "🏭"; } /* Factory */
.icon-alert::before { content: "🚨"; } /* Police Car Light */
.icon-analysis::before { content: "🔬"; } /* Microscope */
.icon-cause::before { content: "💡"; } /* Light Bulb */
.icon-sensors::before { content: "🌡️"; } /* Thermometer */
.icon-chart::before { content: "📈"; } /* Chart Increasing */
.icon-suggestion::before { content: "🛠️"; } /* Hammer and Wrench *//* Placeholder */
.placeholder {color: var(--placeholder-text);font-style: italic;text-align: center;padding: 1rem;font-size: 0.9rem;
}.placeholder#rcaPlaceholder {margin-top: 2rem;font-size: 1rem;
}/* Helper */
.is-hidden {display: none !important;
}/* Responsive Design */
@media (max-width: 1400px) {.main-content {grid-template-columns: 260px 350px 1fr; /* Adjust column widths */}
}@media (max-width: 1200px) {.main-content {grid-template-columns: 240px 320px 1fr; /* Further adjust */}.main-header h1 {font-size: 1.15rem;}
}@media (max-width: 992px) {.analysis-container {height: auto;min-height: 100vh;margin: 0.5rem;}.main-content {grid-template-columns: 1fr; /* Stack columns */grid-template-rows: auto auto auto; /* Auto rows */padding: 0.5rem;gap: 0.5rem;}.panel {padding: 0.75rem;overflow-y: visible; /* Allow panels to grow */}.line-list,.alert-item-list {max-height: 250px; /* Limit list height */overflow-y: auto;}.rca-details-container {max-height: 450px; /* Limit details height */overflow-y: auto;}.main-header {flex-direction: column;align-items: flex-start;gap: 0.5rem;padding: 0.75rem;}.header-info {align-self: flex-end;}.panel-toolbar {/* Keep toolbar items inline if possible */}.panel-toolbar select {margin-left: 0.5rem; /* Adjust margin */}
}@media (max-width: 576px) {.main-header h1 {font-size: 1rem;}.panel h2 {font-size: 1rem;}.line-list,.alert-item-list {max-height: 200px;}.rca-details-container {max-height: 400px;}.sensor-data-section .sensor-data-list {grid-template-columns: 1fr; /* Stack sensors */}
} 

script.js

// script.js - Industrial Big Data Analysis & Root Cause Analysis Componentdocument.addEventListener('DOMContentLoaded', () => {// --- DOM Elements ---const analysisStatusEl = document.getElementById('analysisStatus');const currentTimeEl = document.getElementById('currentTime');const productionLineListUl = document.getElementById('productionLineList');const activeAlertsCountSpan = document.getElementById('activeAlertsCount');const alertSortOrderSelect = document.getElementById('alertSortOrder');const alertListUl = document.getElementById('alertList');const rcaPlaceholder = document.getElementById('rcaPlaceholder');const rcaContent = document.getElementById('rcaContent');const rcaEventTitle = document.getElementById('rcaEventTitle');const rcaEventTime = document.getElementById('rcaEventTime');const rcaRootCause = document.getElementById('rcaRootCause');const rcaConfidence = document.getElementById('rcaConfidence');const rcaSensorDataUl = document.getElementById('rcaSensorData');const rcaHistoryChart = document.getElementById('rcaHistoryChart'); // Placeholderconst rcaSuggestionsUl = document.getElementById('rcaSuggestions');// --- Simulation State & Parameters ---let productionLines = {};let downtimeAlerts = []; // Store triggered alertslet selectedAlertId = null;let alertSortOrder = 'newest'; // 'newest' or 'oldest'let simulationTime = new Date();// Update intervals (in ms)const timeUpdateInterval = 1000;const sensorUpdateInterval = 1500; // Update sensor dataconst lineStatusUpdateInterval = 5000; // Check for state changes, trigger downtime// --- Logging Utility (Console Version) ---// Define addLog locally to fix the ReferenceError// In a real scenario, this might integrate with a shared logging service or UI componentfunction addLog(level, message) {console.log(`[${level.toUpperCase()}] ${new Date().toLocaleTimeString('zh-CN')} - ${message}`);// If a UI log list were present (like in previous component), rendering code would go here.// Example (requires logListUl to be valid):/*if (logListUl) {const timestamp = new Date().toLocaleTimeString('zh-CN');const logEntry = { level: level.toUpperCase(), message: message, timestamp: timestamp };renderSingleLog(logEntry, true); // Assuming renderSingleLog exists}*/}// Root Cause Analysis Rules (Simplified)const RCA_RULES = [{ conditions: (sensors) => sensors['温度']?.isAbnormal && sensors['振动']?.isAbnormal,cause: '轴承故障 (温度和振动异常)', confidence: 85,suggestions: ['检查主轴承润滑', '测量轴承间隙', '必要时更换轴承'] },{ conditions: (sensors) => sensors['压力']?.isAbnormal && sensors['温度']?.isAbnormal,cause: '液压系统异常 (压力和温度超标)', confidence: 75,suggestions: ['检查液压油位和清洁度', '检查泵和阀门状态', '检查冷却系统'] },{ conditions: (sensors) => sensors['电流']?.isAbnormal > 1.5, // Current significantly highcause: '电机过载或短路风险', confidence: 80,suggestions: ['检查电机负载', '测量电机绝缘电阻', '检查供电线路'] },{ conditions: (sensors) => sensors['振动']?.isAbnormal && !sensors['温度']?.isAbnormal,cause: '机械结构松动或不对中', confidence: 65,suggestions: ['检查设备紧固件', '进行动平衡校准', '检查安装基础'] },{ conditions: (sensors) => sensors['温度']?.isAbnormal && !sensors['振动']?.isAbnormal, cause: '冷却系统效率低下或堵塞', confidence: 70,suggestions: ['清洁散热器或冷却通道', '检查冷却风扇/水泵', '确认冷却介质流量'] },// Default/fallback rule{ conditions: () => true, cause: '未知或多因素复合异常', confidence: 30, suggestions: ['进行全面设备检查', '查阅设备维护手册', '联系设备制造商技术支持'] }];// --- Sample Data Generation ---function generateSampleLines(count = 3) {const lines = {};const lineNames = ['装配线 A', '加工中心 B', '包装线 C', '测试站 D'];const sensorTemplates = {'温度': { unit: '°C', normalMin: 40, normalMax: 60, warnThreshold: 75, errorThreshold: 85 },'振动': { unit: 'mm/s', normalMin: 0.5, normalMax: 2.0, warnThreshold: 3.5, errorThreshold: 5.0 },'压力': { unit: 'MPa', normalMin: 0.8, normalMax: 1.2, warnThreshold: 1.5, errorThreshold: 1.8 },'电流': { unit: 'A', normalMin: 5, normalMax: 10, warnThreshold: 12, errorThreshold: 15 }};for (let i = 1; i <= count; i++) {const id = `LINE-${String(i).padStart(2, '0')}`;const name = lineNames[i-1] || `生产线 ${i}`;const line = {id: id,name: name,status: 'running', // running, stopped, idleuptime: 0, // secondslastStatusChange: new Date(),sensors: {},// History for RCA - Store snapshots before downtimesensorHistory: [] };// Add 2-4 random sensors to each lineconst sensorTypes = Object.keys(sensorTemplates);const numSensors = 2 + Math.floor(Math.random() * 3);const usedTypes = new Set();while(usedTypes.size < numSensors && usedTypes.size < sensorTypes.length){const type = sensorTypes[Math.floor(Math.random() * sensorTypes.length)];if(!usedTypes.has(type)){usedTypes.add(type);const template = sensorTemplates[type];line.sensors[type] = {type: type,unit: template.unit,value: template.normalMin + Math.random() * (template.normalMax - template.normalMin),normalMin: template.normalMin,normalMax: template.normalMax,warnThreshold: template.warnThreshold,errorThreshold: template.errorThreshold,isAbnormal: false, // Track if current value is abnormal (above warn)abnormalLevel: 0 // 0: normal, 1: warn, 2: error};}}lines[id] = line;}return lines;}// --- Simulation & Update Logic ---function updateTime() {simulationTime = new Date();currentTimeEl.textContent = simulationTime.toLocaleTimeString('zh-CN');}function updateSensorData() {Object.values(productionLines).forEach(line => {if (line.status === 'running' || line.status === 'idle') {Object.values(line.sensors).forEach(sensor => {const fluctuation = (Math.random() - 0.48) * (sensor.normalMax - sensor.normalMin) * 0.15;// Tend towards upper bound if line has been running long, simulate wearconst wearFactor = line.status === 'running' ? Math.min(line.uptime / (3600 * 2), 1) * 0.1 : 0;let newValue = sensor.value + fluctuation + wearFactor * (sensor.errorThreshold - sensor.normalMax);// Keep within somewhat reasonable bounds, but allow exceeding thresholdsnewValue = Math.max(sensor.normalMin * 0.8, newValue);newValue = Math.min(sensor.errorThreshold * 1.2, newValue);sensor.value = newValue;// Update abnormal statusif (sensor.value >= sensor.errorThreshold) {sensor.isAbnormal = true;sensor.abnormalLevel = 2;} else if (sensor.value >= sensor.warnThreshold) {sensor.isAbnormal = true;sensor.abnormalLevel = 1;} else {sensor.isAbnormal = false;sensor.abnormalLevel = 0;}});// Store snapshot for potential RCAline.sensorHistory.push(captureSensorSnapshot(line.sensors));if (line.sensorHistory.length > 10) { // Keep last 10 snapshotsline.sensorHistory.shift();}}});// No need to re-render lists here, sensor values aren't directly displayed in lists}function captureSensorSnapshot(sensors) {const snapshot = {};for (const type in sensors) {snapshot[type] = {value: sensors[type].value,isAbnormal: sensors[type].isAbnormal,abnormalLevel: sensors[type].abnormalLevel,unit: sensors[type].unit};}return { timestamp: new Date(), sensors: snapshot };}function updateLineStatuses() {let statusChanged = false;Object.values(productionLines).forEach(line => {const now = new Date();const timeSinceChange = (now - line.lastStatusChange) / 1000; // secondsif (line.status === 'running') {line.uptime += timeSinceChange;// Check for downtime conditionsconst abnormalSensors = Object.values(line.sensors).filter(s => s.isAbnormal);const criticalSensors = abnormalSensors.filter(s => s.abnormalLevel === 2);// Downtime trigger: 1+ critical or 2+ warningif (criticalSensors.length >= 1 || abnormalSensors.length >= 2) {// More likely to stop if sensors are highly abnormal or multiple are abnormalconst stopProbability = 0.3 + criticalSensors.length * 0.3 + abnormalSensors.length * 0.1;if (Math.random() < stopProbability) {line.status = 'stopped';line.lastStatusChange = now;statusChanged = true;triggerDowntimeAlert(line);}} else if (Math.random() < 0.02) { // Small chance to go idleline.status = 'idle';line.lastStatusChange = now;line.uptime = 0; // Reset uptime when going idlestatusChanged = true;}} else if (line.status === 'idle') {if (Math.random() < 0.4) { // Chance to start running againline.status = 'running';line.lastStatusChange = now;line.uptime = 0;statusChanged = true;}} else if (line.status === 'stopped') {// Stay stopped until manually reset (not simulated here) or maybe auto-reset after a while?// Let's add a small chance to auto-reset to idle after a whileif (timeSinceChange > 60 && Math.random() < 0.1) { // After 1 min, 10% chance to go idleline.status = 'idle';line.lastStatusChange = now;statusChanged = true;}}// Update uptime regardless of status change for running linesif (line.status !== 'running') line.uptime = 0;// else line.uptime += timeSinceChange; // This causes drift, update only on interval// Update last status change time if status didn't change but was runningif(line.status === 'running' && !statusChanged) {line.lastStatusChange = now; // Keep track for uptime calculation}});if (statusChanged) {renderProductionLineList();updateAnalysisStatus(); // Update header status based on lines}// Always render list to update uptime display smoothlyelse if (Object.values(productionLines).some(l => l.status === 'running')){renderProductionLineList();}}function triggerDowntimeAlert(line) {const eventTime = new Date();const alertId = `ALERT-${line.id}-${eventTime.getTime()}`;const sensorSnapshot = line.sensorHistory.length > 0 ? line.sensorHistory[line.sensorHistory.length - 1] : captureSensorSnapshot(line.sensors);const newAlert = {id: alertId,lineId: line.id,lineName: line.name,timestamp: eventTime,message: '检测到异常停机事件',severity: 'critical', // All stops are critical for nowsensorSnapshot: sensorSnapshot // Store data just before downtime};downtimeAlerts.push(newAlert);addLog('error', `生产线 ${line.name} 发生异常停机.`); // Log the downtimerenderAlertList(); // Re-render with the new alertupdateActiveAlertsCount();}function performRootCauseAnalysis(alert) {if (!alert || !alert.sensorSnapshot || !alert.sensorSnapshot.sensors) {return {cause: '数据不足,无法分析',confidence: 0,triggeringSensors: {},suggestions: ['请检查数据采集系统']};}const sensorsAtDowntime = alert.sensorSnapshot.sensors;const abnormalSensorsData = {};Object.entries(sensorsAtDowntime).forEach(([type, data]) => {if (data.isAbnormal) {abnormalSensorsData[type] = data;}});// Find the first matching rulefor (const rule of RCA_RULES) {if (rule.conditions(abnormalSensorsData)) {// Add slight delay to simulate analysis time// setTimeout(() => { // No, setTimeout here makes returning value trickyreturn {cause: rule.cause,confidence: rule.confidence + Math.floor(Math.random()*10 - 5), // Add slight randomnesstriggeringSensors: abnormalSensorsData, // Return sensors that were abnormalsuggestions: rule.suggestions};// }, 50 + Math.random() * 150); // Simulate 50-200ms analysis// return; // Return immediately, result will come via timeout? No, bad design.}}// Should not happen due to default rule, but as a fallback:return {cause: '未能识别的异常模式',confidence: 10,triggeringSensors: abnormalSensorsData,suggestions: ['请进行手动排查']};}// --- Initialization ---function initializeAnalysis() {addLog('info', '大数据分析服务启动...');productionLines = generateSampleLines();setupEventListeners();updateTime();setInterval(updateTime, timeUpdateInterval);setInterval(updateSensorData, sensorUpdateInterval);setInterval(updateLineStatuses, lineStatusUpdateInterval);renderProductionLineList();renderAlertList();updateAnalysisStatus();updateActiveAlertsCount();displayRcaDetails(null); // Show placeholder initiallyaddLog('info', '分析服务初始化完成。');console.log("Root Cause Analysis Monitor Initialized", productionLines);}// --- Event Handlers ---function setupEventListeners() {alertSortOrderSelect.addEventListener('change', handleAlertSortChange);alertListUl.addEventListener('click', handleAlertSelect);// Remove the check for addLog, as it's now defined locally above./*if (typeof addLog === 'undefined') {window.addLog = (level, message) => console.log(`[${level}] ${message}`);}*/}function handleAlertSortChange(event) {alertSortOrder = event.target.value;renderAlertList();}function handleAlertSelect(event) {const listItem = event.target.closest('.alert-item');if (listItem && listItem.dataset.alertId) {const alertId = listItem.dataset.alertId;if (selectedAlertId !== alertId) {selectedAlertId = alertId;const selectedAlert = downtimeAlerts.find(a => a.id === alertId);// Add a slight delay before displaying results to simulate analysisdisplayRcaDetails(null, true); // Show loading/placeholder temporarilysetTimeout(() => {displayRcaDetails(selectedAlert);renderAlertList(); // Re-render to update selection highlight after delay}, 150 + Math.random() * 200); // Simulate 150-350ms analysis delay} else {// Clicked selected alert again: DeselectselectedAlertId = null;displayRcaDetails(null);renderAlertList();}}}// --- Rendering Functions ---function renderProductionLineList() {// Ensure the UL element exists before trying to modify itif (!productionLineListUl) {console.error("Production line list UL not found!");return;}productionLineListUl.innerHTML = ''; // Clear listif (Object.keys(productionLines).length === 0) {productionLineListUl.innerHTML = '<li class="placeholder">无生产线数据</li>';return;}Object.values(productionLines).sort((a, b) => a.name.localeCompare(b.name)).forEach(line => {const li = document.createElement('li');li.className = 'line-item';li.dataset.lineId = line.id;const statusClass = line.status; // running, stopped, idleconst uptimeFormatted = line.status === 'running' ? formatDuration(line.uptime) : '--';li.innerHTML = `<div class="line-details"><span class="line-name">${line.name}</span><span class="line-uptime">连续运行: ${uptimeFormatted}</span></div><span class="line-status-indicator ${statusClass}"><span class="dot"></span>${statusClass.toUpperCase()}</span>`;productionLineListUl.appendChild(li);});}function renderAlertList() {// Ensure the UL element existsif (!alertListUl) {console.error("Alert list UL not found!");return;}alertListUl.innerHTML = ''; // Clear listconst sortedAlerts = [...downtimeAlerts].sort((a, b) => {return alertSortOrder === 'newest' ? b.timestamp - a.timestamp : a.timestamp - b.timestamp;});if (sortedAlerts.length === 0) {alertListUl.innerHTML = '<li class="placeholder">暂无停机告警</li>';return;}sortedAlerts.forEach(alert => {const li = document.createElement('li');li.className = `alert-item ${alert.severity}`; // e.g., criticalli.dataset.alertId = alert.id;if (alert.id === selectedAlertId) {li.classList.add('selected');}li.innerHTML = `<div class="alert-header"><span class="alert-line-name">${alert.lineName}</span><span class="alert-time">${alert.timestamp.toLocaleTimeString('zh-CN')}</span></div><div class="alert-message">${alert.message}</div>`;alertListUl.appendChild(li);});}// Modified to handle temporary loading statefunction displayRcaDetails(alert, isLoading = false) {// Ensure container elements existif (!rcaPlaceholder || !rcaContent) {console.error("RCA container elements not found!");return;}if (!alert || isLoading) {rcaPlaceholder.classList.remove('is-hidden');rcaContent.classList.add('is-hidden');if(isLoading && alert === null) { // Show a specific loading message if neededrcaPlaceholder.textContent = '正在分析中...';} else {rcaPlaceholder.textContent = '请在左侧选择一个停机告警查看详情';}return;}rcaPlaceholder.classList.add('is-hidden');rcaContent.classList.remove('is-hidden');// Perform analysisconst analysisResult = performRootCauseAnalysis(alert);// Populate details (ensure elements exist before setting textContent)if(rcaEventTitle) rcaEventTitle.textContent = `事件: ${alert.lineName} 停机`;if(rcaEventTime) rcaEventTime.textContent = `时间: ${alert.timestamp.toLocaleString('zh-CN')}`;if(rcaRootCause) rcaRootCause.textContent = analysisResult.cause;if(rcaConfidence) rcaConfidence.textContent = `置信度: ${analysisResult.confidence}%`;// Populate sensor dataif (rcaSensorDataUl) {rcaSensorDataUl.innerHTML = '';if (analysisResult.triggeringSensors && Object.keys(analysisResult.triggeringSensors).length > 0) {Object.entries(analysisResult.triggeringSensors).forEach(([type, sensorData]) => {const li = document.createElement('li');li.className = 'sensor-item';li.innerHTML = `<span class="sensor-name">${type}</span><span class="sensor-value ${sensorData.isAbnormal ? 'abnormal' : ''}">${sensorData.value.toFixed(2)} ${sensorData.unit}</span>`;rcaSensorDataUl.appendChild(li);});} else {rcaSensorDataUl.innerHTML = '<li class="placeholder">无明确异常传感器数据</li>';}}// Populate suggestionsif(rcaSuggestionsUl){rcaSuggestionsUl.innerHTML = '';if (analysisResult.suggestions && analysisResult.suggestions.length > 0) {analysisResult.suggestions.forEach(suggestion => {const li = document.createElement('li');li.textContent = suggestion;rcaSuggestionsUl.appendChild(li);});} else {rcaSuggestionsUl.innerHTML = '<li>暂无建议</li>';}}// TODO: Update history chart placeholder if needed}function updateAnalysisStatus() {if (!analysisStatusEl) return;const stoppedLines = Object.values(productionLines).filter(l => l.status === 'stopped').length;if (stoppedLines > 0) {analysisStatusEl.textContent = `告警: ${stoppedLines}条产线停机`;analysisStatusEl.classList.add('error');} else {analysisStatusEl.textContent = '分析运行中';analysisStatusEl.classList.remove('error');}}function updateActiveAlertsCount() {if (!activeAlertsCountSpan) return;activeAlertsCountSpan.textContent = downtimeAlerts.length;}function formatDuration(totalSeconds) {if (totalSeconds < 0) totalSeconds = 0;const hours = Math.floor(totalSeconds / 3600);const minutes = Math.floor((totalSeconds % 3600) / 60);const seconds = Math.floor(totalSeconds % 60);return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;}// --- Initial Call ---initializeAnalysis();
}); 

相关文章:

  • 线程的两种实现方式
  • 鸿蒙OSUniApp实现的倒计时功能与倒计时组件(鸿蒙系统适配版)#三方框架 #Uniapp
  • 低损耗高效能100G O Band DWDM 10km光模块 | 支持密集波分复用
  • Elasticsearch 快速入门指南
  • ChromaDB 向量库优化技巧实战
  • SymPy | 使用SymPy求解多元非线性方程组
  • 合并两个有序数组的高效算法详解
  • 1.1 认识编程与C++
  • 黑马k8s(七)
  • 腾讯开源实时语音大模型VITA-audio,92mstoken极速响应,支持多语言~
  • 麒麟v10 部署 MySQL 5.6.10 完整步骤
  • javaSE.迭代器
  • AI Agent开发第67课-彻底消除RAG知识库幻觉-文档分块全技巧(1)
  • 密码学刷题小记录
  • QML学习01(设置宽度、高度、坐标点、标题,信号与槽,键盘事件)
  • 网页渲染的两条赛道
  • 【高斯拟合】不用库手写高斯拟合算法:从最小二乘到拟合参数推导
  • 牛客网NC22012:判断闰年问题详解
  • [c语言日寄]数据结构:栈
  • RAGFlow 中的 Rerank 和 Recall 解释
  • 魔都眼|锦江乐园摩天轮“换代”开拆,新摩天轮暂定118米
  • 中国—美国经贸合作对接交流会在华盛顿成功举行
  • 河南省委常委会会议:坚持以案为鉴,深刻汲取教训
  • 秘鲁总理辞职
  • 紫光集团原董事长赵伟国一审被判死缓
  • MSCI中国指数5月调整:新增5只A股、1只港股