device-monitor.html 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>数控车床振幅监测系统</title>
  7. <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
  8. <style>
  9. :root {
  10. --primary-color: #0066cc;
  11. --secondary-color: #00b6c1;
  12. --warning-color: #ff9800;
  13. --danger-color: #f44336;
  14. --success-color: #4caf50;
  15. --dark-bg: #1a2a3a;
  16. --card-bg: rgba(255, 255, 255, 0.1);
  17. }
  18. * {
  19. margin: 0;
  20. padding: 0;
  21. box-sizing: border-box;
  22. font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
  23. }
  24. body {
  25. background: linear-gradient(135deg, var(--dark-bg) 0%, #0d1721 100%);
  26. color: #fff;
  27. min-height: 100vh;
  28. padding: 20px;
  29. }
  30. .container {
  31. max-width: 1800px;
  32. margin: 0 auto;
  33. }
  34. header {
  35. display: flex;
  36. justify-content: space-between;
  37. align-items: center;
  38. padding: 20px 0;
  39. border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  40. margin-bottom: 30px;
  41. }
  42. .logo {
  43. display: flex;
  44. align-items: center;
  45. gap: 15px;
  46. }
  47. .logo h1 {
  48. font-size: 28px;
  49. font-weight: 700;
  50. background: linear-gradient(90deg, var(--secondary-color), #6ec1e4);
  51. -webkit-background-clip: text;
  52. background-clip: text;
  53. -webkit-text-fill-color: transparent;
  54. }
  55. .logo .tagline {
  56. font-size: 18px;
  57. color: #aaa;
  58. margin-top: 5px;
  59. }
  60. .system-stats {
  61. display: flex;
  62. gap: 25px;
  63. }
  64. .stat-card {
  65. background: var(--card-bg);
  66. border-radius: 12px;
  67. padding: 15px 25px;
  68. min-width: 200px;
  69. text-align: center;
  70. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
  71. border: 1px solid rgba(255, 255, 255, 0.05);
  72. }
  73. .stat-card h3 {
  74. font-size: 14px;
  75. font-weight: 500;
  76. margin-bottom: 10px;
  77. color: #ccc;
  78. }
  79. .stat-card .value {
  80. font-size: 26px;
  81. font-weight: 700;
  82. margin-bottom: 5px;
  83. }
  84. .stat-card .highlight {
  85. color: var(--secondary-color);
  86. font-weight: 700;
  87. }
  88. .alert-indicator {
  89. position: absolute;
  90. top: 10px;
  91. right: 10px;
  92. width: 12px;
  93. height: 12px;
  94. border-radius: 50%;
  95. background-color: var(--success-color);
  96. box-shadow: 0 0 10px var(--success-color);
  97. transition: all 0.3s ease;
  98. }
  99. .alert-indicator.warning {
  100. background-color: var(--warning-color);
  101. box-shadow: 0 0 10px var(--warning-color);
  102. }
  103. .alert-indicator.danger {
  104. background-color: var(--danger-color);
  105. box-shadow: 0 0 10px var(--danger-color);
  106. animation: pulse 1.5s infinite;
  107. }
  108. @keyframes pulse {
  109. 0% { opacity: 1; }
  110. 50% { opacity: 0.4; }
  111. 100% { opacity: 1; }
  112. }
  113. .dashboard {
  114. display: grid;
  115. grid-template-columns: 1fr 1fr;
  116. gap: 25px;
  117. margin-bottom: 30px;
  118. }
  119. .card {
  120. background: var(--card-bg);
  121. border-radius: 15px;
  122. padding: 25px;
  123. box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
  124. border: 1px solid rgba(255, 255, 255, 0.05);
  125. position: relative;
  126. overflow: hidden;
  127. }
  128. .card-header {
  129. display: flex;
  130. justify-content: space-between;
  131. align-items: center;
  132. margin-bottom: 20px;
  133. }
  134. .card-title {
  135. font-size: 20px;
  136. font-weight: 600;
  137. color: #fff;
  138. }
  139. .chart-container {
  140. height: 400px;
  141. width: 100%;
  142. }
  143. .dashboard-row {
  144. display: grid;
  145. grid-template-columns: 1fr 1fr;
  146. gap: 25px;
  147. margin-bottom: 30px;
  148. }
  149. .cost-comparison {
  150. display: grid;
  151. grid-template-columns: 1fr 1fr;
  152. gap: 20px;
  153. margin-top: 20px;
  154. }
  155. .cost-card {
  156. background: rgba(0, 0, 0, 0.3);
  157. border-radius: 10px;
  158. padding: 15px;
  159. text-align: center;
  160. }
  161. .cost-card h4 {
  162. font-size: 16px;
  163. margin-bottom: 10px;
  164. color: #ccc;
  165. }
  166. .cost-value {
  167. font-size: 24px;
  168. font-weight: 700;
  169. }
  170. .import-value {
  171. color: #ff6b6b;
  172. }
  173. .our-value {
  174. color: var(--secondary-color);
  175. }
  176. .key-metrics {
  177. display: grid;
  178. grid-template-columns: repeat(3, 1fr);
  179. gap: 15px;
  180. margin-top: 20px;
  181. }
  182. .metric-card {
  183. background: rgba(0, 0, 0, 0.3);
  184. border-radius: 10px;
  185. padding: 15px;
  186. text-align: center;
  187. }
  188. .metric-card h4 {
  189. font-size: 14px;
  190. margin-bottom: 10px;
  191. color: #ccc;
  192. }
  193. .metric-value {
  194. font-size: 22px;
  195. font-weight: 700;
  196. }
  197. .controls {
  198. display: flex;
  199. gap: 15px;
  200. margin-top: 20px;
  201. }
  202. .btn {
  203. background: var(--primary-color);
  204. color: white;
  205. border: none;
  206. padding: 10px 20px;
  207. border-radius: 6px;
  208. cursor: pointer;
  209. font-size: 16px;
  210. font-weight: 500;
  211. transition: all 0.3s ease;
  212. flex: 1;
  213. }
  214. .btn:hover {
  215. background: #004d99;
  216. transform: translateY(-2px);
  217. box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
  218. }
  219. .btn.secondary {
  220. background: rgba(255, 255, 255, 0.1);
  221. }
  222. .btn.secondary:hover {
  223. background: rgba(255, 255, 255, 0.2);
  224. }
  225. .btn.warning {
  226. background: var(--warning-color);
  227. }
  228. .btn.warning:hover {
  229. background: #e68a00;
  230. }
  231. .status-bar {
  232. display: flex;
  233. align-items: center;
  234. gap: 15px;
  235. margin-top: 20px;
  236. padding: 10px 15px;
  237. background: rgba(0, 0, 0, 0.3);
  238. border-radius: 8px;
  239. }
  240. .status-indicator {
  241. width: 12px;
  242. height: 12px;
  243. border-radius: 50%;
  244. background: var(--success-color);
  245. }
  246. .status-text {
  247. font-size: 16px;
  248. }
  249. .status-indicator.warning {
  250. background: var(--warning-color);
  251. }
  252. .status-indicator.danger {
  253. background: var(--danger-color);
  254. }
  255. footer {
  256. text-align: center;
  257. padding: 30px 0;
  258. color: #777;
  259. font-size: 14px;
  260. border-top: 1px solid rgba(255, 255, 255, 0.1);
  261. margin-top: 40px;
  262. }
  263. .alert-notification {
  264. position: fixed;
  265. top: 20px;
  266. right: 20px;
  267. background: var(--danger-color);
  268. color: white;
  269. padding: 20px 30px;
  270. border-radius: 10px;
  271. box-shadow: 0 10px 30px rgba(244, 67, 54, 0.3);
  272. animation: slideIn 0.5s ease;
  273. z-index: 1000;
  274. display: flex;
  275. align-items: center;
  276. gap: 15px;
  277. max-width: 400px;
  278. }
  279. @keyframes slideIn {
  280. from { transform: translateX(100%); opacity: 0; }
  281. to { transform: translateX(0); opacity: 1; }
  282. }
  283. .alert-icon {
  284. font-size: 28px;
  285. }
  286. .alert-content h3 {
  287. font-size: 18px;
  288. margin-bottom: 5px;
  289. }
  290. .alert-content p {
  291. font-size: 14px;
  292. opacity: 0.9;
  293. }
  294. .close-alert {
  295. background: none;
  296. border: none;
  297. color: white;
  298. font-size: 20px;
  299. cursor: pointer;
  300. margin-left: 10px;
  301. }
  302. </style>
  303. </head>
  304. <body>
  305. <div class="container">
  306. <header>
  307. <div class="logo">
  308. <div>
  309. <h1>数控车床振幅监测系统</h1>
  310. <div class="tagline">工业监测领域的"歼-20"方案</div>
  311. </div>
  312. </div>
  313. <div class="system-stats">
  314. <div class="stat-card">
  315. <h3>当前状态</h3>
  316. <div class="value" id="system-status">运行中</div>
  317. <div class="highlight" id="uptime">正常运行: 32小时</div>
  318. </div>
  319. <div class="stat-card">
  320. <h3>检测响应速度</h3>
  321. <div class="value"><span id="response-time">8</span>ms</div>
  322. <div class="highlight">↑德国方案150倍</div>
  323. </div>
  324. <div class="stat-card">
  325. <h3>误报率</h3>
  326. <div class="value"><span id="error-rate">0.8</span>%</div>
  327. <div class="highlight">↓德国方案86%</div>
  328. </div>
  329. </div>
  330. </header>
  331. <div class="dashboard">
  332. <div class="card">
  333. <div class="card-header">
  334. <h2 class="card-title">实时振动波形</h2>
  335. <div class="alert-indicator" id="vibration-alert"></div>
  336. </div>
  337. <div class="chart-container" id="vibration-chart"></div>
  338. <div class="status-bar">
  339. <div class="status-indicator" id="status-indicator"></div>
  340. <div class="status-text" id="status-text">系统运行正常</div>
  341. </div>
  342. </div>
  343. <div class="card">
  344. <div class="card-header">
  345. <h2 class="card-title">成本控制与性能对比</h2>
  346. </div>
  347. <div class="chart-container" id="cost-chart"></div>
  348. <div class="cost-comparison">
  349. <div class="cost-card">
  350. <h4>单通道成本 (德国方案)</h4>
  351. <div class="cost-value import-value">¥10,284</div>
  352. </div>
  353. <div class="cost-card">
  354. <h4>单通道成本 (本系统)</h4>
  355. <div class="cost-value our-value">¥1,920</div>
  356. </div>
  357. </div>
  358. <div class="key-metrics">
  359. <div class="metric-card">
  360. <h4>成本节约</h4>
  361. <div class="metric-value" style="color: var(--secondary-color);">92%</div>
  362. </div>
  363. <div class="metric-card">
  364. <h4>响应提升</h4>
  365. <div class="metric-value" style="color: var(--secondary-color);">150倍</div>
  366. </div>
  367. <div class="metric-card">
  368. <h4>误报率降低</h4>
  369. <div class="metric-value" style="color: var(--secondary-color);">86%</div>
  370. </div>
  371. </div>
  372. </div>
  373. </div>
  374. <div class="dashboard-row">
  375. <div class="card">
  376. <div class="card-header">
  377. <h2 class="card-title">刀具寿命预测</h2>
  378. <div class="alert-indicator" id="tool-alert"></div>
  379. </div>
  380. <div class="chart-container" id="tool-chart"></div>
  381. <div class="controls">
  382. <button class="btn">导出报告</button>
  383. <button class="btn secondary">维护计划</button>
  384. <button class="btn warning">更换刀具</button>
  385. </div>
  386. </div>
  387. <div class="card">
  388. <div class="card-header">
  389. <h2 class="card-title">异常事件分析</h2>
  390. </div>
  391. <div class="chart-container" id="event-chart"></div>
  392. </div>
  393. </div>
  394. <footer>
  395. <p>国产化高精度振动监测系统 © 2025 | 技术指标:微秒级采样(100kHz) | AI动态阈值算法(误报率0.8%) | 响应延迟<10ms</p>
  396. </footer>
  397. </div>
  398. <div class="alert-notification" id="alert-notification" style="display: none;">
  399. <div class="alert-icon">⚠️</div>
  400. <div class="alert-content">
  401. <h3>振动异常报警!</h3>
  402. <p>检测到异常振动幅度超过阈值,请立即检查设备!</p>
  403. </div>
  404. <button class="close-alert" onclick="closeAlert()">×</button>
  405. </div>
  406. <script>
  407. // 初始化图表
  408. const vibrationChart = echarts.init(document.getElementById('vibration-chart'));
  409. const costChart = echarts.init(document.getElementById('cost-chart'));
  410. const toolChart = echarts.init(document.getElementById('tool-chart'));
  411. const eventChart = echarts.init(document.getElementById('event-chart'));
  412. // 振动图表配置
  413. const vibrationOption = {
  414. backgroundColor: 'transparent',
  415. grid: {
  416. top: 30,
  417. right: 30,
  418. bottom: 40,
  419. left: 50
  420. },
  421. tooltip: {
  422. trigger: 'axis'
  423. },
  424. legend: {
  425. data: ['振动幅度', '动态阈值'],
  426. textStyle: {
  427. color: '#ccc'
  428. },
  429. top: 0
  430. },
  431. xAxis: {
  432. type: 'category',
  433. data: [],
  434. axisLine: {
  435. lineStyle: {
  436. color: '#666'
  437. }
  438. },
  439. axisLabel: {
  440. color: '#999'
  441. }
  442. },
  443. yAxis: {
  444. type: 'value',
  445. name: '振幅 (g)',
  446. nameTextStyle: {
  447. color: '#999'
  448. },
  449. axisLine: {
  450. lineStyle: {
  451. color: '#666'
  452. }
  453. },
  454. axisLabel: {
  455. color: '#999'
  456. },
  457. splitLine: {
  458. lineStyle: {
  459. color: 'rgba(255, 255, 255, 0.05)'
  460. }
  461. }
  462. },
  463. series: [
  464. {
  465. name: '振动幅度',
  466. type: 'line',
  467. smooth: true,
  468. data: [],
  469. lineStyle: {
  470. width: 3,
  471. color: '#00b6c1'
  472. },
  473. itemStyle: {
  474. color: '#00b6c1'
  475. },
  476. areaStyle: {
  477. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  478. { offset: 0, color: 'rgba(0, 182, 193, 0.7)' },
  479. { offset: 1, color: 'rgba(0, 182, 193, 0.1)' }
  480. ])
  481. },
  482. symbol: 'none'
  483. },
  484. {
  485. name: '动态阈值',
  486. type: 'line',
  487. smooth: true,
  488. data: [],
  489. lineStyle: {
  490. width: 2,
  491. color: '#ff9800',
  492. type: 'dashed'
  493. },
  494. itemStyle: {
  495. color: '#ff9800'
  496. },
  497. symbol: 'none'
  498. }
  499. ],
  500. animation: false
  501. };
  502. // 成本对比图表配置
  503. const costOption = {
  504. backgroundColor: 'transparent',
  505. tooltip: {
  506. trigger: 'axis',
  507. axisPointer: {
  508. type: 'shadow'
  509. }
  510. },
  511. legend: {
  512. data: ['德国方案', '本系统'],
  513. textStyle: {
  514. color: '#ccc'
  515. },
  516. top: 0
  517. },
  518. grid: {
  519. top: 40,
  520. right: 30,
  521. bottom: 40,
  522. left: 50
  523. },
  524. xAxis: {
  525. type: 'category',
  526. data: ['单通道成本', '误报率', '响应延迟'],
  527. axisLine: {
  528. lineStyle: {
  529. color: '#666'
  530. }
  531. },
  532. axisLabel: {
  533. color: '#999',
  534. interval: 0
  535. }
  536. },
  537. yAxis: [
  538. {
  539. type: 'value',
  540. name: '成本 (元)',
  541. min: 0,
  542. max: 12000,
  543. axisLine: {
  544. lineStyle: {
  545. color: '#666'
  546. }
  547. },
  548. axisLabel: {
  549. color: '#999',
  550. formatter: '{value}'
  551. },
  552. splitLine: {
  553. lineStyle: {
  554. color: 'rgba(255, 255, 255, 0.05)'
  555. }
  556. }
  557. },
  558. {
  559. type: 'value',
  560. name: '比率/毫秒',
  561. min: 0,
  562. max: 6,
  563. axisLine: {
  564. lineStyle: {
  565. color: '#666'
  566. }
  567. },
  568. axisLabel: {
  569. color: '#999',
  570. formatter: function(value) {
  571. if (value < 1) return value * 100 + '%';
  572. return value + 'ms';
  573. }
  574. }
  575. }
  576. ],
  577. series: [
  578. {
  579. name: '德国方案',
  580. type: 'bar',
  581. barWidth: 30,
  582. itemStyle: {
  583. color: '#ff6b6b'
  584. },
  585. data: [10284, 0.058, 1.2]
  586. },
  587. {
  588. name: '本系统',
  589. type: 'bar',
  590. barWidth: 30,
  591. itemStyle: {
  592. color: '#00b6c1'
  593. },
  594. data: [1920, 0.008, 0.008]
  595. }
  596. ]
  597. };
  598. // 刀具寿命图表配置
  599. const toolOption = {
  600. backgroundColor: 'transparent',
  601. tooltip: {
  602. trigger: 'axis'
  603. },
  604. legend: {
  605. data: ['刀具磨损度'],
  606. textStyle: {
  607. color: '#ccc'
  608. },
  609. top: 0
  610. },
  611. grid: {
  612. top: 30,
  613. right: 30,
  614. bottom: 40,
  615. left: 50
  616. },
  617. xAxis: {
  618. type: 'category',
  619. data: [],
  620. axisLine: {
  621. lineStyle: {
  622. color: '#666'
  623. }
  624. },
  625. axisLabel: {
  626. color: '#999'
  627. }
  628. },
  629. yAxis: {
  630. type: 'value',
  631. name: '磨损度 (%)',
  632. min: 0,
  633. max: 100,
  634. axisLine: {
  635. lineStyle: {
  636. color: '#666'
  637. }
  638. },
  639. axisLabel: {
  640. color: '#999'
  641. },
  642. splitLine: {
  643. lineStyle: {
  644. color: 'rgba(255, 255, 255, 0.05)'
  645. }
  646. }
  647. },
  648. series: [
  649. {
  650. name: '刀具磨损度',
  651. type: 'line',
  652. data: [],
  653. smooth: true,
  654. lineStyle: {
  655. width: 3,
  656. color: '#ff9800'
  657. },
  658. itemStyle: {
  659. color: '#ff9800'
  660. },
  661. areaStyle: {
  662. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  663. { offset: 0, color: 'rgba(255, 152, 0, 0.5)' },
  664. { offset: 1, color: 'rgba(255, 152, 0, 0.1)' }
  665. ])
  666. },
  667. markLine: {
  668. silent: true,
  669. lineStyle: {
  670. color: '#f44336',
  671. width: 2,
  672. type: 'dashed'
  673. },
  674. data: [
  675. {
  676. yAxis: 85,
  677. label: {
  678. formatter: '更换阈值',
  679. position: 'start'
  680. }
  681. }
  682. ]
  683. }
  684. }
  685. ]
  686. };
  687. // 异常事件图表配置
  688. const eventOption = {
  689. backgroundColor: 'transparent',
  690. tooltip: {
  691. trigger: 'axis'
  692. },
  693. legend: {
  694. data: ['异常事件'],
  695. textStyle: {
  696. color: '#ccc'
  697. },
  698. top: 0
  699. },
  700. grid: {
  701. top: 30,
  702. right: 30,
  703. bottom: 40,
  704. left: 50
  705. },
  706. xAxis: {
  707. type: 'category',
  708. data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
  709. axisLine: {
  710. lineStyle: {
  711. color: '#666'
  712. }
  713. },
  714. axisLabel: {
  715. color: '#999'
  716. }
  717. },
  718. yAxis: {
  719. type: 'value',
  720. name: '事件次数',
  721. axisLine: {
  722. lineStyle: {
  723. color: '#666'
  724. }
  725. },
  726. axisLabel: {
  727. color: '#999'
  728. },
  729. splitLine: {
  730. lineStyle: {
  731. color: 'rgba(255, 255, 255, 0.05)'
  732. }
  733. }
  734. },
  735. series: [
  736. {
  737. name: '异常事件',
  738. type: 'bar',
  739. barWidth: 30,
  740. itemStyle: {
  741. color: '#00b6c1'
  742. },
  743. data: [2, 0, 1, 3, 1, 0, 0]
  744. }
  745. ]
  746. };
  747. // 应用图表配置
  748. vibrationChart.setOption(vibrationOption);
  749. costChart.setOption(costOption);
  750. toolChart.setOption(toolOption);
  751. eventChart.setOption(eventOption);
  752. // 窗口大小调整时重新渲染图表
  753. window.addEventListener('resize', function() {
  754. vibrationChart.resize();
  755. costChart.resize();
  756. toolChart.resize();
  757. eventChart.resize();
  758. });
  759. // 模拟实时数据
  760. let timeCounter = 0;
  761. let vibrationData = [];
  762. let thresholdData = [];
  763. let toolWearData = [];
  764. let isAlertActive = false;
  765. // 动态阈值算法
  766. function calculateDynamicThreshold(data) {
  767. // 简单实现:基于历史数据的均值和标准差
  768. if (data.length < 20) return 0.8;
  769. const recentData = data.slice(-20);
  770. const sum = recentData.reduce((a, b) => a + b, 0);
  771. const mean = sum / recentData.length;
  772. const squareDiffs = recentData.map(value => Math.pow(value - mean, 2));
  773. const variance = squareDiffs.reduce((a, b) => a + b, 0) / recentData.length;
  774. const stdDev = Math.sqrt(variance);
  775. return mean + 4 * stdDev; // 4倍标准差作为阈值
  776. }
  777. // 生成模拟数据
  778. function generateData() {
  779. timeCounter++;
  780. // 生成振动数据(模拟微秒级采样)
  781. const baseValue = Math.sin(timeCounter * 0.2) * 0.8;
  782. const noise = (Math.random() - 0.5) * 0.3;
  783. let amplitude = baseValue + noise;
  784. // 0.1%概率产生异常
  785. if (Math.random() < 0.001) {
  786. amplitude = 2.5 + Math.random() * 1.0; // 异常值范围2.5-3.5
  787. }
  788. vibrationData.push(amplitude);
  789. if (vibrationData.length > 200) vibrationData.shift();
  790. // 计算动态阈值
  791. const threshold = calculateDynamicThreshold(vibrationData);
  792. thresholdData.push(threshold);
  793. if (thresholdData.length > 200) thresholdData.shift();
  794. // 更新刀具磨损数据
  795. const wear = Math.min(85, 20 + timeCounter * 0.05);
  796. toolWearData.push(wear);
  797. if (toolWearData.length > 20) toolWearData.shift();
  798. // 更新图表
  799. const xAxisData = Array.from({length: vibrationData.length}, (_, i) => i);
  800. vibrationChart.setOption({
  801. xAxis: {
  802. data: xAxisData
  803. },
  804. series: [
  805. { data: vibrationData },
  806. { data: thresholdData }
  807. ]
  808. });
  809. // 更新刀具寿命图表
  810. const toolXAxisData = Array.from({length: toolWearData.length}, (_, i) => i);
  811. toolChart.setOption({
  812. xAxis: {
  813. data: toolXAxisData
  814. },
  815. series: [
  816. { data: toolWearData }
  817. ]
  818. });
  819. // 检查是否需要报警
  820. const statusIndicator = document.getElementById('status-indicator');
  821. const statusText = document.getElementById('status-text');
  822. const alertIndicator = document.getElementById('vibration-alert');
  823. if (amplitude > threshold) {
  824. statusIndicator.className = 'status-indicator danger';
  825. statusText.textContent = '检测到异常振动!';
  826. alertIndicator.className = 'alert-indicator danger';
  827. if (!isAlertActive) {
  828. showAlert();
  829. isAlertActive = true;
  830. }
  831. } else if (amplitude > threshold * 0.8)
  832. {
  833. statusIndicator.className = 'status-indicator'
  834. statusIndicator.className = 'status-indicator warning';
  835. statusText.textContent = '振动接近阈值,请注意!';
  836. alertIndicator.className = 'alert-indicator warning';
  837. isAlertActive = false;
  838. }
  839. else
  840. {
  841. statusIndicator.className = 'status-indicator';
  842. statusText.textContent = '系统运行正常';
  843. alertIndicator.className = 'alert-indicator';
  844. isAlertActive = false;
  845. }
  846. }
  847. // 显示报警通知
  848. function showAlert() {
  849. const alertNotification = document.getElementById('alert-notification');
  850. alertNotification.style.display = 'flex';
  851. // 播放警报声
  852. const audioContext = new (window.AudioContext || window.webkitAudioContext)();
  853. const oscillator = audioContext.createOscillator();
  854. const gainNode = audioContext.createGain();
  855. oscillator.connect(gainNode);
  856. gainNode.connect(audioContext.destination);
  857. oscillator.type = 'sine';
  858. oscillator.frequency.setValueAtTime(440, audioContext.currentTime);
  859. oscillator.frequency.setValueAtTime(880, audioContext.currentTime + 0.1);
  860. gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
  861. oscillator.start();
  862. oscillator.stop(audioContext.currentTime + 0.5);
  863. // 背景闪烁效果
  864. let flashCount = 0;
  865. const flashInterval = setInterval(() => {
  866. document.body.style.backgroundColor = flashCount % 2 === 0 ? 'rgba(244, 67, 54, 0.2)' : '';
  867. flashCount++;
  868. if (flashCount > 10) {
  869. clearInterval(flashInterval);
  870. document.body.style.backgroundColor = '';
  871. }
  872. }, 200);
  873. }
  874. // 关闭报警通知
  875. function closeAlert() {
  876. document.getElementById('alert-notification').style.display = 'none';
  877. }
  878. // 每秒生成4次数据(模拟100kHz采样率)
  879. setInterval(generateData, 250);
  880. // 更新运行时间
  881. let uptime = 0;
  882. setInterval(() => {
  883. uptime++;
  884. const hours = Math.floor(uptime / 3600);
  885. const minutes = Math.floor((uptime % 3600) / 60);
  886. const seconds = uptime % 60;
  887. document.getElementById('uptime').textContent =
  888. `正常运行: ${hours}小时${minutes}分${seconds}秒`;
  889. }, 1000);
  890. </script>
  891. </body>
  892. </html>