|
@@ -0,0 +1,988 @@
|
|
|
+<!DOCTYPE html>
|
|
|
+<html lang="zh-CN">
|
|
|
+<head>
|
|
|
+ <meta charset="UTF-8">
|
|
|
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
+ <title>数控车床振幅监测系统</title>
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
|
|
+ <style>
|
|
|
+ :root {
|
|
|
+ --primary-color: #0066cc;
|
|
|
+ --secondary-color: #00b6c1;
|
|
|
+ --warning-color: #ff9800;
|
|
|
+ --danger-color: #f44336;
|
|
|
+ --success-color: #4caf50;
|
|
|
+ --dark-bg: #1a2a3a;
|
|
|
+ --card-bg: rgba(255, 255, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ * {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
|
|
+ }
|
|
|
+
|
|
|
+ body {
|
|
|
+ background: linear-gradient(135deg, var(--dark-bg) 0%, #0d1721 100%);
|
|
|
+ color: #fff;
|
|
|
+ min-height: 100vh;
|
|
|
+ padding: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .container {
|
|
|
+ max-width: 1800px;
|
|
|
+ margin: 0 auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20px 0;
|
|
|
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
+ margin-bottom: 30px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .logo {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .logo h1 {
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: 700;
|
|
|
+ background: linear-gradient(90deg, var(--secondary-color), #6ec1e4);
|
|
|
+ -webkit-background-clip: text;
|
|
|
+ background-clip: text;
|
|
|
+ -webkit-text-fill-color: transparent;
|
|
|
+ }
|
|
|
+
|
|
|
+ .logo .tagline {
|
|
|
+ font-size: 18px;
|
|
|
+ color: #aaa;
|
|
|
+ margin-top: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .system-stats {
|
|
|
+ display: flex;
|
|
|
+ gap: 25px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-card {
|
|
|
+ background: var(--card-bg);
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 15px 25px;
|
|
|
+ min-width: 200px;
|
|
|
+ text-align: center;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.05);
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-card h3 {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #ccc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-card .value {
|
|
|
+ font-size: 26px;
|
|
|
+ font-weight: 700;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-card .highlight {
|
|
|
+ color: var(--secondary-color);
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-indicator {
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ right: 10px;
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: var(--success-color);
|
|
|
+ box-shadow: 0 0 10px var(--success-color);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-indicator.warning {
|
|
|
+ background-color: var(--warning-color);
|
|
|
+ box-shadow: 0 0 10px var(--warning-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-indicator.danger {
|
|
|
+ background-color: var(--danger-color);
|
|
|
+ box-shadow: 0 0 10px var(--danger-color);
|
|
|
+ animation: pulse 1.5s infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes pulse {
|
|
|
+ 0% { opacity: 1; }
|
|
|
+ 50% { opacity: 0.4; }
|
|
|
+ 100% { opacity: 1; }
|
|
|
+ }
|
|
|
+
|
|
|
+ .dashboard {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ gap: 25px;
|
|
|
+ margin-bottom: 30px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card {
|
|
|
+ background: var(--card-bg);
|
|
|
+ border-radius: 15px;
|
|
|
+ padding: 25px;
|
|
|
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.05);
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-title {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-container {
|
|
|
+ height: 400px;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .dashboard-row {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ gap: 25px;
|
|
|
+ margin-bottom: 30px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cost-comparison {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
+ gap: 20px;
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cost-card {
|
|
|
+ background: rgba(0, 0, 0, 0.3);
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 15px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cost-card h4 {
|
|
|
+ font-size: 16px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #ccc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cost-value {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ .import-value {
|
|
|
+ color: #ff6b6b;
|
|
|
+ }
|
|
|
+
|
|
|
+ .our-value {
|
|
|
+ color: var(--secondary-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .key-metrics {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-card {
|
|
|
+ background: rgba(0, 0, 0, 0.3);
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 15px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-card h4 {
|
|
|
+ font-size: 14px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ color: #ccc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-value {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ .controls {
|
|
|
+ display: flex;
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn {
|
|
|
+ background: var(--primary-color);
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 10px 20px;
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn:hover {
|
|
|
+ background: #004d99;
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn.secondary {
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn.secondary:hover {
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn.warning {
|
|
|
+ background: var(--warning-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn.warning:hover {
|
|
|
+ background: #e68a00;
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-bar {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 20px;
|
|
|
+ padding: 10px 15px;
|
|
|
+ background: rgba(0, 0, 0, 0.3);
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-indicator {
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: var(--success-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-text {
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-indicator.warning {
|
|
|
+ background: var(--warning-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-indicator.danger {
|
|
|
+ background: var(--danger-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ footer {
|
|
|
+ text-align: center;
|
|
|
+ padding: 30px 0;
|
|
|
+ color: #777;
|
|
|
+ font-size: 14px;
|
|
|
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
+ margin-top: 40px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-notification {
|
|
|
+ position: fixed;
|
|
|
+ top: 20px;
|
|
|
+ right: 20px;
|
|
|
+ background: var(--danger-color);
|
|
|
+ color: white;
|
|
|
+ padding: 20px 30px;
|
|
|
+ border-radius: 10px;
|
|
|
+ box-shadow: 0 10px 30px rgba(244, 67, 54, 0.3);
|
|
|
+ animation: slideIn 0.5s ease;
|
|
|
+ z-index: 1000;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ max-width: 400px;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes slideIn {
|
|
|
+ from { transform: translateX(100%); opacity: 0; }
|
|
|
+ to { transform: translateX(0); opacity: 1; }
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-icon {
|
|
|
+ font-size: 28px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-content h3 {
|
|
|
+ font-size: 18px;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .alert-content p {
|
|
|
+ font-size: 14px;
|
|
|
+ opacity: 0.9;
|
|
|
+ }
|
|
|
+
|
|
|
+ .close-alert {
|
|
|
+ background: none;
|
|
|
+ border: none;
|
|
|
+ color: white;
|
|
|
+ font-size: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+</head>
|
|
|
+<body>
|
|
|
+ <div class="container">
|
|
|
+ <header>
|
|
|
+ <div class="logo">
|
|
|
+ <div>
|
|
|
+ <h1>数控车床振幅监测系统</h1>
|
|
|
+ <div class="tagline">工业监测领域的"歼-20"方案</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="system-stats">
|
|
|
+ <div class="stat-card">
|
|
|
+ <h3>当前状态</h3>
|
|
|
+ <div class="value" id="system-status">运行中</div>
|
|
|
+ <div class="highlight" id="uptime">正常运行: 32小时</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="stat-card">
|
|
|
+ <h3>检测响应速度</h3>
|
|
|
+ <div class="value"><span id="response-time">8</span>ms</div>
|
|
|
+ <div class="highlight">↑德国方案150倍</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="stat-card">
|
|
|
+ <h3>误报率</h3>
|
|
|
+ <div class="value"><span id="error-rate">0.8</span>%</div>
|
|
|
+ <div class="highlight">↓德国方案86%</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <div class="dashboard">
|
|
|
+ <div class="card">
|
|
|
+ <div class="card-header">
|
|
|
+ <h2 class="card-title">实时振动波形</h2>
|
|
|
+ <div class="alert-indicator" id="vibration-alert"></div>
|
|
|
+ </div>
|
|
|
+ <div class="chart-container" id="vibration-chart"></div>
|
|
|
+ <div class="status-bar">
|
|
|
+ <div class="status-indicator" id="status-indicator"></div>
|
|
|
+ <div class="status-text" id="status-text">系统运行正常</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="card">
|
|
|
+ <div class="card-header">
|
|
|
+ <h2 class="card-title">成本控制与性能对比</h2>
|
|
|
+ </div>
|
|
|
+ <div class="chart-container" id="cost-chart"></div>
|
|
|
+
|
|
|
+ <div class="cost-comparison">
|
|
|
+ <div class="cost-card">
|
|
|
+ <h4>单通道成本 (德国方案)</h4>
|
|
|
+ <div class="cost-value import-value">¥10,284</div>
|
|
|
+ </div>
|
|
|
+ <div class="cost-card">
|
|
|
+ <h4>单通道成本 (本系统)</h4>
|
|
|
+ <div class="cost-value our-value">¥1,920</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="key-metrics">
|
|
|
+ <div class="metric-card">
|
|
|
+ <h4>成本节约</h4>
|
|
|
+ <div class="metric-value" style="color: var(--secondary-color);">92%</div>
|
|
|
+ </div>
|
|
|
+ <div class="metric-card">
|
|
|
+ <h4>响应提升</h4>
|
|
|
+ <div class="metric-value" style="color: var(--secondary-color);">150倍</div>
|
|
|
+ </div>
|
|
|
+ <div class="metric-card">
|
|
|
+ <h4>误报率降低</h4>
|
|
|
+ <div class="metric-value" style="color: var(--secondary-color);">86%</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="dashboard-row">
|
|
|
+ <div class="card">
|
|
|
+ <div class="card-header">
|
|
|
+ <h2 class="card-title">刀具寿命预测</h2>
|
|
|
+ <div class="alert-indicator" id="tool-alert"></div>
|
|
|
+ </div>
|
|
|
+ <div class="chart-container" id="tool-chart"></div>
|
|
|
+ <div class="controls">
|
|
|
+ <button class="btn">导出报告</button>
|
|
|
+ <button class="btn secondary">维护计划</button>
|
|
|
+ <button class="btn warning">更换刀具</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="card">
|
|
|
+ <div class="card-header">
|
|
|
+ <h2 class="card-title">异常事件分析</h2>
|
|
|
+ </div>
|
|
|
+ <div class="chart-container" id="event-chart"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <footer>
|
|
|
+ <p>国产化高精度振动监测系统 © 2025 | 技术指标:微秒级采样(100kHz) | AI动态阈值算法(误报率0.8%) | 响应延迟<10ms</p>
|
|
|
+ </footer>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="alert-notification" id="alert-notification" style="display: none;">
|
|
|
+ <div class="alert-icon">⚠️</div>
|
|
|
+ <div class="alert-content">
|
|
|
+ <h3>振动异常报警!</h3>
|
|
|
+ <p>检测到异常振动幅度超过阈值,请立即检查设备!</p>
|
|
|
+ </div>
|
|
|
+ <button class="close-alert" onclick="closeAlert()">×</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ // 初始化图表
|
|
|
+ const vibrationChart = echarts.init(document.getElementById('vibration-chart'));
|
|
|
+ const costChart = echarts.init(document.getElementById('cost-chart'));
|
|
|
+ const toolChart = echarts.init(document.getElementById('tool-chart'));
|
|
|
+ const eventChart = echarts.init(document.getElementById('event-chart'));
|
|
|
+
|
|
|
+ // 振动图表配置
|
|
|
+ const vibrationOption = {
|
|
|
+ backgroundColor: 'transparent',
|
|
|
+ grid: {
|
|
|
+ top: 30,
|
|
|
+ right: 30,
|
|
|
+ bottom: 40,
|
|
|
+ left: 50
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['振动幅度', '动态阈值'],
|
|
|
+ textStyle: {
|
|
|
+ color: '#ccc'
|
|
|
+ },
|
|
|
+ top: 0
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: [],
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '振幅 (g)',
|
|
|
+ nameTextStyle: {
|
|
|
+ color: '#999'
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999'
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: 'rgba(255, 255, 255, 0.05)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '振动幅度',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ data: [],
|
|
|
+ lineStyle: {
|
|
|
+ width: 3,
|
|
|
+ color: '#00b6c1'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#00b6c1'
|
|
|
+ },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'rgba(0, 182, 193, 0.7)' },
|
|
|
+ { offset: 1, color: 'rgba(0, 182, 193, 0.1)' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ symbol: 'none'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '动态阈值',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ data: [],
|
|
|
+ lineStyle: {
|
|
|
+ width: 2,
|
|
|
+ color: '#ff9800',
|
|
|
+ type: 'dashed'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ff9800'
|
|
|
+ },
|
|
|
+ symbol: 'none'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ animation: false
|
|
|
+ };
|
|
|
+
|
|
|
+ // 成本对比图表配置
|
|
|
+ const costOption = {
|
|
|
+ backgroundColor: 'transparent',
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['德国方案', '本系统'],
|
|
|
+ textStyle: {
|
|
|
+ color: '#ccc'
|
|
|
+ },
|
|
|
+ top: 0
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ top: 40,
|
|
|
+ right: 30,
|
|
|
+ bottom: 40,
|
|
|
+ left: 50
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: ['单通道成本', '误报率', '响应延迟'],
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ interval: 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '成本 (元)',
|
|
|
+ min: 0,
|
|
|
+ max: 12000,
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ formatter: '{value}'
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: 'rgba(255, 255, 255, 0.05)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '比率/毫秒',
|
|
|
+ min: 0,
|
|
|
+ max: 6,
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ formatter: function(value) {
|
|
|
+ if (value < 1) return value * 100 + '%';
|
|
|
+ return value + 'ms';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '德国方案',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: 30,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ff6b6b'
|
|
|
+ },
|
|
|
+ data: [10284, 0.058, 1.2]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '本系统',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: 30,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#00b6c1'
|
|
|
+ },
|
|
|
+ data: [1920, 0.008, 0.008]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ // 刀具寿命图表配置
|
|
|
+ const toolOption = {
|
|
|
+ backgroundColor: 'transparent',
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['刀具磨损度'],
|
|
|
+ textStyle: {
|
|
|
+ color: '#ccc'
|
|
|
+ },
|
|
|
+ top: 0
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ top: 30,
|
|
|
+ right: 30,
|
|
|
+ bottom: 40,
|
|
|
+ left: 50
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: [],
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '磨损度 (%)',
|
|
|
+ min: 0,
|
|
|
+ max: 100,
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999'
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: 'rgba(255, 255, 255, 0.05)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '刀具磨损度',
|
|
|
+ type: 'line',
|
|
|
+ data: [],
|
|
|
+ smooth: true,
|
|
|
+ lineStyle: {
|
|
|
+ width: 3,
|
|
|
+ color: '#ff9800'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ff9800'
|
|
|
+ },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'rgba(255, 152, 0, 0.5)' },
|
|
|
+ { offset: 1, color: 'rgba(255, 152, 0, 0.1)' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ markLine: {
|
|
|
+ silent: true,
|
|
|
+ lineStyle: {
|
|
|
+ color: '#f44336',
|
|
|
+ width: 2,
|
|
|
+ type: 'dashed'
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ {
|
|
|
+ yAxis: 85,
|
|
|
+ label: {
|
|
|
+ formatter: '更换阈值',
|
|
|
+ position: 'start'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ // 异常事件图表配置
|
|
|
+ const eventOption = {
|
|
|
+ backgroundColor: 'transparent',
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['异常事件'],
|
|
|
+ textStyle: {
|
|
|
+ color: '#ccc'
|
|
|
+ },
|
|
|
+ top: 0
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ top: 30,
|
|
|
+ right: 30,
|
|
|
+ bottom: 40,
|
|
|
+ left: 50
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '事件次数',
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999'
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: 'rgba(255, 255, 255, 0.05)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '异常事件',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: 30,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#00b6c1'
|
|
|
+ },
|
|
|
+ data: [2, 0, 1, 3, 1, 0, 0]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ // 应用图表配置
|
|
|
+ vibrationChart.setOption(vibrationOption);
|
|
|
+ costChart.setOption(costOption);
|
|
|
+ toolChart.setOption(toolOption);
|
|
|
+ eventChart.setOption(eventOption);
|
|
|
+
|
|
|
+ // 窗口大小调整时重新渲染图表
|
|
|
+ window.addEventListener('resize', function() {
|
|
|
+ vibrationChart.resize();
|
|
|
+ costChart.resize();
|
|
|
+ toolChart.resize();
|
|
|
+ eventChart.resize();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 模拟实时数据
|
|
|
+ let timeCounter = 0;
|
|
|
+ let vibrationData = [];
|
|
|
+ let thresholdData = [];
|
|
|
+ let toolWearData = [];
|
|
|
+ let isAlertActive = false;
|
|
|
+
|
|
|
+ // 动态阈值算法
|
|
|
+ function calculateDynamicThreshold(data) {
|
|
|
+ // 简单实现:基于历史数据的均值和标准差
|
|
|
+ if (data.length < 20) return 0.8;
|
|
|
+
|
|
|
+ const recentData = data.slice(-20);
|
|
|
+ const sum = recentData.reduce((a, b) => a + b, 0);
|
|
|
+ const mean = sum / recentData.length;
|
|
|
+
|
|
|
+ const squareDiffs = recentData.map(value => Math.pow(value - mean, 2));
|
|
|
+ const variance = squareDiffs.reduce((a, b) => a + b, 0) / recentData.length;
|
|
|
+ const stdDev = Math.sqrt(variance);
|
|
|
+
|
|
|
+ return mean + 4 * stdDev; // 4倍标准差作为阈值
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成模拟数据
|
|
|
+ function generateData() {
|
|
|
+ timeCounter++;
|
|
|
+
|
|
|
+ // 生成振动数据(模拟微秒级采样)
|
|
|
+ const baseValue = Math.sin(timeCounter * 0.2) * 0.8;
|
|
|
+ const noise = (Math.random() - 0.5) * 0.3;
|
|
|
+ let amplitude = baseValue + noise;
|
|
|
+
|
|
|
+ // 0.1%概率产生异常
|
|
|
+ if (Math.random() < 0.001) {
|
|
|
+ amplitude = 2.5 + Math.random() * 1.0; // 异常值范围2.5-3.5
|
|
|
+ }
|
|
|
+
|
|
|
+ vibrationData.push(amplitude);
|
|
|
+ if (vibrationData.length > 200) vibrationData.shift();
|
|
|
+
|
|
|
+ // 计算动态阈值
|
|
|
+ const threshold = calculateDynamicThreshold(vibrationData);
|
|
|
+ thresholdData.push(threshold);
|
|
|
+ if (thresholdData.length > 200) thresholdData.shift();
|
|
|
+
|
|
|
+ // 更新刀具磨损数据
|
|
|
+ const wear = Math.min(85, 20 + timeCounter * 0.05);
|
|
|
+ toolWearData.push(wear);
|
|
|
+ if (toolWearData.length > 20) toolWearData.shift();
|
|
|
+
|
|
|
+ // 更新图表
|
|
|
+ const xAxisData = Array.from({length: vibrationData.length}, (_, i) => i);
|
|
|
+ vibrationChart.setOption({
|
|
|
+ xAxis: {
|
|
|
+ data: xAxisData
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ { data: vibrationData },
|
|
|
+ { data: thresholdData }
|
|
|
+ ]
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新刀具寿命图表
|
|
|
+ const toolXAxisData = Array.from({length: toolWearData.length}, (_, i) => i);
|
|
|
+ toolChart.setOption({
|
|
|
+ xAxis: {
|
|
|
+ data: toolXAxisData
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ { data: toolWearData }
|
|
|
+ ]
|
|
|
+ });
|
|
|
+
|
|
|
+ // 检查是否需要报警
|
|
|
+ const statusIndicator = document.getElementById('status-indicator');
|
|
|
+ const statusText = document.getElementById('status-text');
|
|
|
+ const alertIndicator = document.getElementById('vibration-alert');
|
|
|
+
|
|
|
+ if (amplitude > threshold) {
|
|
|
+ statusIndicator.className = 'status-indicator danger';
|
|
|
+ statusText.textContent = '检测到异常振动!';
|
|
|
+ alertIndicator.className = 'alert-indicator danger';
|
|
|
+
|
|
|
+ if (!isAlertActive) {
|
|
|
+ showAlert();
|
|
|
+ isAlertActive = true;
|
|
|
+ }
|
|
|
+ } else if (amplitude > threshold * 0.8)
|
|
|
+ {
|
|
|
+ statusIndicator.className = 'status-indicator'
|
|
|
+ statusIndicator.className = 'status-indicator warning';
|
|
|
+ statusText.textContent = '振动接近阈值,请注意!';
|
|
|
+ alertIndicator.className = 'alert-indicator warning';
|
|
|
+ isAlertActive = false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ statusIndicator.className = 'status-indicator';
|
|
|
+ statusText.textContent = '系统运行正常';
|
|
|
+ alertIndicator.className = 'alert-indicator';
|
|
|
+ isAlertActive = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示报警通知
|
|
|
+ function showAlert() {
|
|
|
+ const alertNotification = document.getElementById('alert-notification');
|
|
|
+ alertNotification.style.display = 'flex';
|
|
|
+
|
|
|
+ // 播放警报声
|
|
|
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
|
+ const oscillator = audioContext.createOscillator();
|
|
|
+ const gainNode = audioContext.createGain();
|
|
|
+
|
|
|
+ oscillator.connect(gainNode);
|
|
|
+ gainNode.connect(audioContext.destination);
|
|
|
+
|
|
|
+ oscillator.type = 'sine';
|
|
|
+ oscillator.frequency.setValueAtTime(440, audioContext.currentTime);
|
|
|
+ oscillator.frequency.setValueAtTime(880, audioContext.currentTime + 0.1);
|
|
|
+
|
|
|
+ gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
|
|
|
+
|
|
|
+ oscillator.start();
|
|
|
+ oscillator.stop(audioContext.currentTime + 0.5);
|
|
|
+
|
|
|
+ // 背景闪烁效果
|
|
|
+ let flashCount = 0;
|
|
|
+ const flashInterval = setInterval(() => {
|
|
|
+ document.body.style.backgroundColor = flashCount % 2 === 0 ? 'rgba(244, 67, 54, 0.2)' : '';
|
|
|
+ flashCount++;
|
|
|
+
|
|
|
+ if (flashCount > 10) {
|
|
|
+ clearInterval(flashInterval);
|
|
|
+ document.body.style.backgroundColor = '';
|
|
|
+ }
|
|
|
+ }, 200);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关闭报警通知
|
|
|
+ function closeAlert() {
|
|
|
+ document.getElementById('alert-notification').style.display = 'none';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 每秒生成4次数据(模拟100kHz采样率)
|
|
|
+ setInterval(generateData, 250);
|
|
|
+
|
|
|
+ // 更新运行时间
|
|
|
+ let uptime = 0;
|
|
|
+ setInterval(() => {
|
|
|
+ uptime++;
|
|
|
+ const hours = Math.floor(uptime / 3600);
|
|
|
+ const minutes = Math.floor((uptime % 3600) / 60);
|
|
|
+ const seconds = uptime % 60;
|
|
|
+ document.getElementById('uptime').textContent =
|
|
|
+ `正常运行: ${hours}小时${minutes}分${seconds}秒`;
|
|
|
+ }, 1000);
|
|
|
+ </script>
|
|
|
+</body>
|
|
|
+</html>
|