1、PLC控制面板 - /自动化与控制组件/plc-control-panel

76个工业组件库示例汇总

PLC控制系统监控面板

这是一个用于PLC控制系统监控面板的自定义组件,提供了PLC编程与自动化控制逻辑设计的可视化监控界面。组件采用工业风格设计,包含实时数据展示、系统状态监控、控制功能以及报警和日志记录等功能。

功能特点

  1. 工业风格UI设计:深色主题,高对比度,符合工业控制系统的视觉风格
  2. PLC状态监控:实时展示CPU负载、内存使用、通信状态等关键指标
  3. 交互式逻辑流程图:可视化展示PLC逻辑控制流程,支持缩放和重置视图
  4. 控制面板:包含启动、停止、重置和紧急停止等核心控制功能
  5. 实时数据展示:显示温度、压力、流量和电机速度等关键参数
  6. 报警系统:实时显示系统报警信息,自动更新报警计数
  7. 操作日志:记录系统操作和状态变化,支持日志清除
  8. 动态数据模拟:自动模拟传感器数据变化,提供真实的使用体验
  9. 响应式设计:适应不同屏幕尺寸,确保在各种设备上正常显示

主要区域说明

组件包含以下主要区域:

  1. 顶部标题和状态栏:显示系统名称和当前运行状态
  2. PLC状态监控区:展示PLC核心性能指标和运行状态
  3. 逻辑控制流程区:以交互式图形方式展示PLC逻辑流程
  4. 控制面板区:提供系统控制按钮和实时参数数据表格
  5. 报警和日志区:展示系统报警和操作日志信息

自定义选项

可以通过修改代码自定义以下内容:

  • 颜色主题 :在CSS中修改:root中的颜色变量
  • 数据更新频率 :在startDataSimulation函数中调整更新间隔
  • PLC逻辑图 :在setupLogicDiagram函数中修改组件和连接定义
  • 报警阈值 :在simulateRealtimeData函数中调整报警触发条件
  • 添加新的监控参数:在HTML中添加新的表格行,并在JavaScript中更新对应数据

浏览器兼容性

组件使用了现代Web标准,包括SVG、CSS变量和ES6特性。建议在最新版本的Chrome、Firefox或Edge浏览器中使用,以获得最佳体验。

注意事项

  • 组件中的数据为模拟数据,实际应用中需要连接到真实的PLC数据源
  • 为了获得更好的性能,大型部署环境中应优化数据更新频率
  • 如果需要连接实际的PLC系统,请替换simulateRealtimeData函数,使用适当的API调用

效果展示

源码

index.html

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>PLC控制系统监控面板</title>
  <link rel="stylesheet" href="styles.css">
  
</head>
<body>
  <div id="plc-control-panel">
    <!-- 顶部标题和状态栏 -->
    <div class="panel-header">
      <div class="panel-title">PLC控制系统监控面板</div>
      <div class="status-indicator">
        <span class="status-text">系统状态:</span>
        <span class="status-value" id="system-status">运行中</span>
        <div class="status-light running"></div>
      </div>
    </div>
    
    <!-- 主控制面板 -->
    <div class="panel-content">
      <!-- 左侧:PLC参数和状态展示 -->
      <div class="panel-section plc-status">
        <div class="section-header">
          <h3>PLC状态监控</h3>
          <div class="refresh-btn" id="refresh-status">
            <i class="refresh-icon">↻</i>
          </div>
        </div>
        <div class="metrics-grid">
          <div class="metric-item">
            <div class="metric-label">CPU负载</div>
            <div class="metric-value" id="cpu-load">45%</div>
            <div class="progress-bar">
              <div class="progress-fill" style="width: 45%"></div>
            </div>
          </div>
          <div class="metric-item">
            <div class="metric-label">内存使用</div>
            <div class="metric-value" id="memory-usage">67%</div>
            <div class="progress-bar">
              <div class="progress-fill" style="width: 67%"></div>
            </div>
          </div>
          <div class="metric-item">
            <div class="metric-label">通信状态</div>
            <div class="metric-value" id="comm-status">正常</div>
            <div class="status-indicator-small">
              <div class="status-light-small normal"></div>
            </div>
          </div>
          <div class="metric-item">
            <div class="metric-label">运行时间</div>
            <div class="metric-value" id="runtime">164小时</div>
          </div>
        </div>
      </div>
  
      <!-- 中间:交互式PLC逻辑流程图 -->
      <div class="panel-section plc-logic">
        <div class="section-header">
          <h3>PLC逻辑控制流程</h3>
          <div class="logic-controls">
            <button class="btn-small" id="zoom-in">+</button>
            <button class="btn-small" id="zoom-out">-</button>
            <button class="btn-small" id="reset-view">重置</button>
          </div>
        </div>
        <div class="logic-diagram" id="logic-diagram">
          <!-- 逻辑流程图将通过JavaScript绘制 -->
        </div>
      </div>
  
      <!-- 右侧:控制按钮和实时数据 -->
      <div class="panel-section control-panel">
        <div class="section-header">
          <h3>控制面板</h3>
        </div>
        <div class="control-buttons">
          <button class="control-btn start-btn" id="start-btn">启动系统</button>
          <button class="control-btn stop-btn" id="stop-btn">停止系统</button>
          <button class="control-btn reset-btn" id="reset-btn">重置系统</button>
          <button class="control-btn emergency-btn" id="emergency-btn">紧急停止</button>
        </div>
        <div class="divider"></div>
        <div class="realtime-data">
          <h4>实时数据</h4>
          <table class="data-table">
            <thead>
              <tr>
                <th>参数</th>
                <th>数值</th>
                <th>状态</th>
              </tr>
            </thead>
            <tbody id="realtime-data-table">
              <tr>
                <td>温度传感器</td>
                <td id="temp-value">78.5°C</td>
                <td><span class="status-dot warning"></span></td>
              </tr>
              <tr>
                <td>压力传感器</td>
                <td id="pressure-value">2.4 MPa</td>
                <td><span class="status-dot normal"></span></td>
              </tr>
              <tr>
                <td>流量计</td>
                <td id="flow-value">120 L/min</td>
                <td><span class="status-dot normal"></span></td>
              </tr>
              <tr>
                <td>电机速度</td>
                <td id="motor-speed">1750 RPM</td>
                <td><span class="status-dot normal"></span></td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  
    <!-- 底部报警和日志区域 -->
    <div class="panel-footer">
      <div class="alarm-section">
        <div class="alarm-header">
          <span class="alarm-title">系统报警</span>
          <span class="alarm-count" id="alarm-count">1</span>
        </div>
        <div class="alarm-list" id="alarm-list">
          <div class="alarm-item warning">
            <span class="alarm-time">08:45:12</span>
            <span class="alarm-message">温度传感器读数超过警戒值 (>75°C)</span>
          </div>
        </div>
      </div>
      <div class="log-section">
        <div class="log-header">
          <span class="log-title">操作日志</span>
          <button class="btn-small" id="clear-logs">清除</button>
        </div>
        <div class="log-list" id="log-list">
          <div class="log-item">
            <span class="log-time">08:30:05</span>
            <span class="log-message">系统启动成功</span>
          </div>
          <div class="log-item">
            <span class="log-time">08:42:18</span>
            <span class="log-message">参数设置已更新</span>
          </div>
          <div class="log-item">
            <span class="log-time">08:45:10</span>
            <span class="log-message">温度监控触发预警</span>
          </div>
        </div>
      </div>
    </div>
  </div> 
  <script src="script.js"></script>
</body>
</html> 

styles.css

javascript 复制代码
/* PLC控制面板工业风格样式 */
:root {
  --dark-gray: #202830;
  --medium-gray: #343c44;
  --light-gray: #4a5562;
  --highlight-blue: #00a8ff;
  --highlight-blue-dark: #0077b6;
  --warning-yellow: #ffc107;
  --danger-red: #ff3b30;
  --success-green: #34c759;
  --text-white: #f0f2f5;
  --text-light: #a7b6c2;
  --border-color: #2a343f;
  --shadow-color: rgba(0, 0, 0, 0.3);
}

/* 主容器 */
#plc-control-panel {
  font-family: 'Roboto', 'Arial', sans-serif;
  background-color: var(--dark-gray);
  color: var(--text-white);
  border-radius: 6px;
  box-shadow: 0 4px 16px var(--shadow-color);
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  min-height: 700px;
  overflow: hidden;
  border: 1px solid var(--border-color);
}

/* 标题栏样式 */
.panel-header {
  background-color: var(--medium-gray);
  padding: 12px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid var(--border-color);
}

.panel-title {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.status-indicator {
  display: flex;
  align-items: center;
  gap: 10px;
}

.status-text {
  color: var(--text-light);
  font-size: 14px;
}

.status-value {
  font-weight: 500;
}

.status-light {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}

.status-light.running {
  background-color: var(--success-green);
  box-shadow: 0 0 8px var(--success-green);
}

.status-light.warning {
  background-color: var(--warning-yellow);
  box-shadow: 0 0 8px var(--warning-yellow);
}

.status-light.error {
  background-color: var(--danger-red);
  box-shadow: 0 0 8px var(--danger-red);
}

.status-light.offline {
  background-color: var(--light-gray);
}

/* 主内容区域 */
.panel-content {
  display: flex;
  flex: 1;
  overflow: hidden;
}

/* 面板部分通用样式 */
.panel-section {
  background-color: var(--medium-gray);
  border-radius: 4px;
  margin: 10px;
  padding: 12px;
  box-shadow: 0 2px 8px var(--shadow-color);
  overflow: auto;
}

.plc-status {
  flex: 1;
  max-width: 25%;
}

.plc-logic {
  flex: 2;
  min-width: 45%;
  overflow: hidden;
}

.control-panel {
  flex: 1;
  max-width: 25%;
}

/* 章节标题 */
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-color);
}

.section-header h3 {
  font-size: 16px;
  font-weight: 500;
  margin: 0;
  color: var(--highlight-blue);
}

/* 刷新按钮 */
.refresh-btn {
  width: 28px;
  height: 28px;
  border-radius: 4px;
  background-color: var(--dark-gray);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s;
}

.refresh-btn:hover {
  background-color: var(--highlight-blue-dark);
}

.refresh-icon {
  font-style: normal;
  color: var(--text-light);
}

/* 指标网格 */
.metrics-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

.metric-item {
  background-color: var(--dark-gray);
  border-radius: 4px;
  padding: 10px;
  position: relative;
}

.metric-label {
  font-size: 12px;
  color: var(--text-light);
  margin-bottom: 6px;
}

.metric-value {
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 8px;
}

/* 进度条 */
.progress-bar {
  height: 6px;
  background-color: var(--light-gray);
  border-radius: 3px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background-color: var(--highlight-blue);
  border-radius: 3px;
  transition: width 0.5s ease;
}

/* 小状态指示器 */
.status-indicator-small {
  display: flex;
  align-items: center;
}

.status-light-small {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
}

.status-light-small.normal {
  background-color: var(--success-green);
  box-shadow: 0 0 5px var(--success-green);
}

.status-light-small.warning {
  background-color: var(--warning-yellow);
  box-shadow: 0 0 5px var(--warning-yellow);
}

.status-light-small.error {
  background-color: var(--danger-red);
  box-shadow: 0 0 5px var(--danger-red);
}

/* 逻辑流程图部分 */
.logic-controls {
  display: flex;
  gap: 5px;
}

.btn-small {
  background-color: var(--dark-gray);
  color: var(--text-white);
  border: 1px solid var(--light-gray);
  border-radius: 4px;
  padding: 2px 8px;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.2s;
}

.btn-small:hover {
  background-color: var(--light-gray);
}

.logic-diagram {
  background-color: var(--dark-gray);
  border-radius: 4px;
  height: calc(100% - 40px);
  min-height: 300px;
  position: relative;
  border: 1px solid var(--border-color);
  overflow: hidden;
}

/* 控制按钮 */
.control-buttons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 16px;
}

.control-btn {
  padding: 10px;
  border-radius: 4px;
  font-weight: 500;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
  border: none;
  color: var(--text-white);
  text-align: center;
}

.start-btn {
  background-color: var(--success-green);
}

.start-btn:hover {
  background-color: #2aa048;
}

.stop-btn {
  background-color: var(--warning-yellow);
  color: #212529;
}

.stop-btn:hover {
  background-color: #e6a800;
}

.reset-btn {
  background-color: var(--highlight-blue);
}

.reset-btn:hover {
  background-color: var(--highlight-blue-dark);
}

.emergency-btn {
  background-color: var(--danger-red);
}

.emergency-btn:hover {
  background-color: #e62e24;
}

.divider {
  height: 1px;
  background-color: var(--border-color);
  margin: 16px 0;
}

/* 实时数据表格 */
.realtime-data h4 {
  font-size: 14px;
  font-weight: 500;
  margin: 0 0 12px 0;
  color: var(--text-light);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.data-table th {
  text-align: left;
  color: var(--text-light);
  font-weight: 500;
  padding: 8px 4px;
  border-bottom: 1px solid var(--border-color);
}

.data-table td {
  padding: 8px 4px;
  border-bottom: 1px solid var(--border-color);
}

.data-table tr:last-child td {
  border-bottom: none;
}

.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.status-dot.normal {
  background-color: var(--success-green);
  box-shadow: 0 0 5px var(--success-green);
}

.status-dot.warning {
  background-color: var(--warning-yellow);
  box-shadow: 0 0 5px var(--warning-yellow);
}

.status-dot.error {
  background-color: var(--danger-red);
  box-shadow: 0 0 5px var(--danger-red);
}

/* 底部区域 */
.panel-footer {
  display: flex;
  background-color: var(--medium-gray);
  border-top: 2px solid var(--border-color);
  padding: 0;
  max-height: 180px;
  min-height: 180px;
}

.alarm-section, .log-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border-color);
}

.log-section {
  border-right: none;
}

.alarm-header, .log-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 15px;
  background-color: var(--dark-gray);
}

.alarm-title, .log-title {
  font-size: 14px;
  font-weight: 500;
}

.alarm-count {
  background-color: var(--danger-red);
  color: white;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 10px;
  font-weight: 600;
}

.alarm-list, .log-list {
  flex: 1;
  overflow-y: auto;
  padding: 8px 15px;
}

.alarm-item, .log-item {
  padding: 6px 0;
  border-bottom: 1px solid var(--border-color);
  font-size: 12px;
  display: flex;
  align-items: flex-start;
}

.alarm-item:last-child, .log-item:last-child {
  border-bottom: none;
}

.alarm-time, .log-time {
  min-width: 70px;
  color: var(--text-light);
  margin-right: 10px;
}

.alarm-item.warning .alarm-message {
  color: var(--warning-yellow);
}

.alarm-item.error .alarm-message {
  color: var(--danger-red);
}

/* 响应式调整 */
@media (max-width: 1200px) {
  .panel-content {
    flex-direction: column;
  }
  
  .plc-status, .plc-logic, .control-panel {
    max-width: 100%;
  }
  
  .panel-footer {
    flex-direction: column;
    max-height: 300px;
  }
  
  .alarm-section {
    border-right: none;
    border-bottom: 1px solid var(--border-color);
  }
} 

script.js

javascript 复制代码
// PLC控制面板JavaScript
(function() {
  // 初始化组件
  function initPLCControlPanel() {
    console.log("PLC控制面板初始化...");
    setupEventListeners();
    setupLogicDiagram();
    startDataSimulation();
  }

  // 设置事件监听器
  function setupEventListeners() {
    // 刷新按钮
    document.getElementById('refresh-status').addEventListener('click', refreshStatus);
    
    // 逻辑图控制按钮
    document.getElementById('zoom-in').addEventListener('click', () => zoomLogicDiagram(1.2));
    document.getElementById('zoom-out').addEventListener('click', () => zoomLogicDiagram(0.8));
    document.getElementById('reset-view').addEventListener('click', resetLogicDiagram);
    
    // 控制按钮
    document.getElementById('start-btn').addEventListener('click', startSystem);
    document.getElementById('stop-btn').addEventListener('click', stopSystem);
    document.getElementById('reset-btn').addEventListener('click', resetSystem);
    document.getElementById('emergency-btn').addEventListener('click', emergencyStop);
    
    // 清除日志按钮
    document.getElementById('clear-logs').addEventListener('click', clearLogs);
  }

  // 刷新状态
  function refreshStatus() {
    const refreshBtn = document.getElementById('refresh-status');
    refreshBtn.classList.add('rotating');
    
    // 模拟刷新延迟
    setTimeout(() => {
      updatePLCStatus();
      refreshBtn.classList.remove('rotating');
      addLogEntry('状态已刷新');
    }, 800);
  }

  // 更新PLC状态
  function updatePLCStatus() {
    const cpuLoad = getRandomValue(20, 85);
    const memoryUsage = getRandomValue(30, 90);
    const commStatus = (Math.random() > 0.9) ? '异常' : '正常';
    const runtime = Math.floor(100 + Math.random() * 100);
    
    // 更新DOM
    document.getElementById('cpu-load').textContent = cpuLoad + '%';
    document.querySelector('.plc-status .progress-fill').style.width = cpuLoad + '%';
    
    document.getElementById('memory-usage').textContent = memoryUsage + '%';
    document.querySelectorAll('.plc-status .progress-fill')[1].style.width = memoryUsage + '%';
    
    document.getElementById('comm-status').textContent = commStatus;
    const commLight = document.querySelector('.status-light-small');
    
    if (commStatus === '正常') {
      commLight.className = 'status-light-small normal';
    } else {
      commLight.className = 'status-light-small error';
      addAlarmEntry('通信异常,请检查网络连接');
    }
    
    document.getElementById('runtime').textContent = runtime + '小时';
  }

  // 缩放逻辑图
  function zoomLogicDiagram(factor) {
    const diagram = document.getElementById('logic-diagram');
    const currentScale = diagram.style.transform ? 
      parseFloat(diagram.style.transform.replace('scale(', '').replace(')', '')) : 1;
    
    const newScale = currentScale * factor;
    if (newScale >= 0.5 && newScale <= 2.5) {
      diagram.style.transform = `scale(${newScale})`;
    }
  }

  // 重置逻辑图视图
  function resetLogicDiagram() {
    const diagram = document.getElementById('logic-diagram');
    diagram.style.transform = 'scale(1)';
    addLogEntry('流程图视图已重置');
  }

  // 创建PLC逻辑流程图
  function setupLogicDiagram() {
    const diagram = document.getElementById('logic-diagram');
    
    // 清空现有内容
    diagram.innerHTML = '';
    
    // SVG命名空间
    const svgNS = "http://www.w3.org/2000/svg";
    
    // 创建SVG元素
    const svg = document.createElementNS(svgNS, "svg");
    svg.setAttribute("width", "100%");
    svg.setAttribute("height", "100%");
    svg.setAttribute("viewBox", "0 0 800 400");
    diagram.appendChild(svg);
    
    // 定义PLC组件
    const components = [
      { id: 'input1', type: 'input', x: 50, y: 80, label: '输入模块 1' },
      { id: 'input2', type: 'input', x: 50, y: 220, label: '输入模块 2' },
      { id: 'cpu', type: 'cpu', x: 300, y: 150, label: 'CPU控制器' },
      { id: 'output1', type: 'output', x: 550, y: 80, label: '输出模块 1' },
      { id: 'output2', type: 'output', x: 550, y: 220, label: '输出模块 2' }
    ];
    
    // 定义连接线
    const connections = [
      { from: 'input1', to: 'cpu', state: 'active' },
      { from: 'input2', to: 'cpu', state: 'inactive' },
      { from: 'cpu', to: 'output1', state: 'active' },
      { from: 'cpu', to: 'output2', state: 'active' }
    ];
    
    // 绘制组件
    components.forEach(component => {
      drawComponent(svg, svgNS, component);
    });
    
    // 绘制连接线
    connections.forEach(conn => {
      const fromComp = components.find(c => c.id === conn.from);
      const toComp = components.find(c => c.id === conn.to);
      
      if (fromComp && toComp) {
        drawConnection(svg, svgNS, fromComp, toComp, conn.state);
      }
    });
  }
  
  // 绘制PLC组件
  function drawComponent(svg, svgNS, component) {
    const group = document.createElementNS(svgNS, "g");
    group.setAttribute("id", component.id);
    group.setAttribute("class", "component " + component.type);
    
    // 创建矩形
    const rect = document.createElementNS(svgNS, "rect");
    rect.setAttribute("x", component.x);
    rect.setAttribute("y", component.y);
    rect.setAttribute("width", "200");
    rect.setAttribute("height", "100");
    rect.setAttribute("rx", "5");
    rect.setAttribute("ry", "5");
    
    // 根据类型设置样式
    switch(component.type) {
      case 'input':
        rect.setAttribute("fill", "#2E4053");
        rect.setAttribute("stroke", "#00a8ff");
        break;
      case 'cpu':
        rect.setAttribute("fill", "#1C2833");
        rect.setAttribute("stroke", "#3498DB");
        break;
      case 'output':
        rect.setAttribute("fill", "#2E4053");
        rect.setAttribute("stroke", "#27AE60");
        break;
    }
    
    rect.setAttribute("stroke-width", "2");
    group.appendChild(rect);
    
    // 创建标签文本
    const text = document.createElementNS(svgNS, "text");
    text.setAttribute("x", component.x + 100);
    text.setAttribute("y", component.y + 50);
    text.setAttribute("text-anchor", "middle");
    text.setAttribute("dominant-baseline", "middle");
    text.setAttribute("fill", "#f0f2f5");
    text.setAttribute("font-size", "16");
    text.textContent = component.label;
    group.appendChild(text);
    
    // 绘制状态指示器
    const statusCircle = document.createElementNS(svgNS, "circle");
    statusCircle.setAttribute("cx", component.x + 100);
    statusCircle.setAttribute("cy", component.y + 80);
    statusCircle.setAttribute("r", "6");
    statusCircle.setAttribute("fill", "#34c759");
    group.appendChild(statusCircle);
    
    // 添加到SVG
    svg.appendChild(group);
  }
  
  // 绘制连接线
  function drawConnection(svg, svgNS, fromComp, toComp, state) {
    // 计算起点和终点
    const startX = fromComp.x + 200;
    const startY = fromComp.y + 50;
    const endX = toComp.x;
    const endY = toComp.y + 50;
    
    // 计算中间点(用于创建平滑的曲线)
    const midX = (startX + endX) / 2;
    
    // 创建路径元素
    const path = document.createElementNS(svgNS, "path");
    path.setAttribute("d", `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`);
    path.setAttribute("fill", "none");
    path.setAttribute("stroke", state === 'active' ? "#00a8ff" : "#616A6B");
    path.setAttribute("stroke-width", "3");
    path.setAttribute("class", "connection " + state);
    
    // 添加到SVG
    svg.appendChild(path);
    
    // 添加数据流动画(如果连接是活动的)
    if (state === 'active') {
      const circle = document.createElementNS(svgNS, "circle");
      circle.setAttribute("r", "4");
      circle.setAttribute("fill", "#00a8ff");
      circle.setAttribute("class", "data-point");
      
      // 添加动画
      const animateMotion = document.createElementNS(svgNS, "animateMotion");
      animateMotion.setAttribute("dur", "2s");
      animateMotion.setAttribute("repeatCount", "indefinite");
      animateMotion.setAttribute("path", `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`);
      
      circle.appendChild(animateMotion);
      svg.appendChild(circle);
    }
  }

  // 系统控制函数
  function startSystem() {
    updateSystemStatus('运行中', 'running');
    addLogEntry('系统已启动');
  }
  
  function stopSystem() {
    updateSystemStatus('已停止', 'warning');
    addLogEntry('系统已停止');
  }
  
  function resetSystem() {
    updateSystemStatus('重置中', 'warning');
    addLogEntry('系统正在重置');
    
    // 模拟重置过程
    setTimeout(() => {
      updateSystemStatus('运行中', 'running');
      addLogEntry('系统重置完成');
    }, 3000);
  }
  
  function emergencyStop() {
    updateSystemStatus('紧急停止', 'error');
    addAlarmEntry('紧急停止已触发!请检查系统安全', 'error');
    addLogEntry('紧急停止按钮已触发');
  }
  
  // 更新系统状态显示
  function updateSystemStatus(text, className) {
    document.getElementById('system-status').textContent = text;
    const light = document.querySelector('.status-light');
    light.className = 'status-light ' + className;
  }
  
  // 添加报警条目
  function addAlarmEntry(message, type = 'warning') {
    const alarmList = document.getElementById('alarm-list');
    const time = getCurrentTime();
    
    const alarmItem = document.createElement('div');
    alarmItem.className = 'alarm-item ' + type;
    
    const timeSpan = document.createElement('span');
    timeSpan.className = 'alarm-time';
    timeSpan.textContent = time;
    
    const messageSpan = document.createElement('span');
    messageSpan.className = 'alarm-message';
    messageSpan.textContent = message;
    
    alarmItem.appendChild(timeSpan);
    alarmItem.appendChild(messageSpan);
    
    alarmList.insertBefore(alarmItem, alarmList.firstChild);
    
    // 更新报警计数
    updateAlarmCount();
  }
  
  // 更新报警计数
  function updateAlarmCount() {
    const count = document.getElementById('alarm-list').children.length;
    document.getElementById('alarm-count').textContent = count;
  }
  
  // 添加日志条目
  function addLogEntry(message) {
    const logList = document.getElementById('log-list');
    const time = getCurrentTime();
    
    const logItem = document.createElement('div');
    logItem.className = 'log-item';
    
    const timeSpan = document.createElement('span');
    timeSpan.className = 'log-time';
    timeSpan.textContent = time;
    
    const messageSpan = document.createElement('span');
    messageSpan.className = 'log-message';
    messageSpan.textContent = message;
    
    logItem.appendChild(timeSpan);
    logItem.appendChild(messageSpan);
    
    logList.insertBefore(logItem, logList.firstChild);
  }
  
  // 清除日志
  function clearLogs() {
    document.getElementById('log-list').innerHTML = '';
    addLogEntry('日志已清除');
  }
  
  // 获取当前时间 (HH:MM:SS)
  function getCurrentTime() {
    const now = new Date();
    const hours = String(now.getHours()).padStart(2, '0');
    const minutes = String(now.getMinutes()).padStart(2, '0');
    const seconds = String(now.getSeconds()).padStart(2, '0');
    return `${hours}:${minutes}:${seconds}`;
  }
  
  // 获取随机值 (min与max之间)
  function getRandomValue(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
  
  // 启动数据模拟
  function startDataSimulation() {
    // 定期更新实时数据
    setInterval(() => {
      simulateRealtimeData();
    }, 3000);
    
    // 随机触发报警
    setInterval(() => {
      if (Math.random() < 0.3) {
        simulateRandomAlarm();
      }
    }, 30000);
  }
  
  // 模拟实时数据更新
  function simulateRealtimeData() {
    const tempValue = (70 + Math.random() * 20).toFixed(1);
    const pressureValue = (2 + Math.random() * 0.8).toFixed(1);
    const flowValue = Math.floor(100 + Math.random() * 50);
    const motorSpeed = Math.floor(1700 + Math.random() * 100);
    
    document.getElementById('temp-value').textContent = tempValue + '°C';
    document.getElementById('pressure-value').textContent = pressureValue + ' MPa';
    document.getElementById('flow-value').textContent = flowValue + ' L/min';
    document.getElementById('motor-speed').textContent = motorSpeed + ' RPM';
    
    // 根据温度值决定状态
    const tempStatus = document.querySelector('tr:nth-child(1) .status-dot');
    if (tempValue > 80) {
      tempStatus.className = 'status-dot warning';
      
      // 如果温度过高,添加报警
      if (tempValue > 85) {
        addAlarmEntry(`温度传感器读数严重超过警戒值 (${tempValue}°C)`, 'error');
      }
    } else {
      tempStatus.className = 'status-dot normal';
    }
    
    // 添加日志
    addLogEntry('传感器数据已更新');
  }
  
  // 模拟随机报警
  function simulateRandomAlarm() {
    const alarms = [
      '压力传感器读数波动异常',
      '电机转速下降',
      '通信延迟超出阈值',
      'PLC数据缓存接近上限',
      '输入模块2响应超时'
    ];
    
    const randomAlarm = alarms[Math.floor(Math.random() * alarms.length)];
    addAlarmEntry(randomAlarm);
  }
  
  // 添加一个CSS类用于旋转刷新图标
  const style = document.createElement('style');
  style.textContent = `
    @keyframes spin {
      from { transform: rotate(0deg); }
      to { transform: rotate(360deg); }
    }
    .rotating {
      animation: spin 0.8s linear;
    }
  `;
  document.head.appendChild(style);

  // Appsmith自定义组件加载完成时调用初始化函数
  if (document.readyState === 'complete') {
    initPLCControlPanel();
  } else {
    window.addEventListener('load', initPLCControlPanel);
  }
})(); 
相关推荐
夏子曦2 小时前
webpack 的工作流程
前端·webpack·node.js
麻芝汤圆2 小时前
在 Sheel 中运行 Spark:开启高效数据处理之旅
大数据·前端·javascript·hadoop·分布式·ajax·spark
独行soc2 小时前
2025年渗透测试面试题总结-某战队红队实习面经(附回答)(题目+回答)
linux·运维·服务器·学习·面试·职场和发展·渗透测试
yrldjsbk2 小时前
uniapp开发09-设置一个tabbar底部导航栏且配置icon图标
前端·uni-app
sunbyte3 小时前
Three.js + React 实战系列 - 项目展示区开发详解 Projects 组件(3D 模型 + 动效 + 状态切换)✨
javascript·react.js·3d
源码方舟3 小时前
【HTML5】显示-隐藏法 实现网页轮播图效果
前端·javascript·html·css3·html5
星川皆无恙3 小时前
大数据产品销售数据分析:基于Python机器学习产品销售数据爬虫可视化分析预测系统设计与实现
大数据·运维·爬虫·python·机器学习·数据分析·系统架构
二川bro3 小时前
依赖注入详解与案例(前端篇)
前端
还是大剑师兰特4 小时前
vue源代码采用的设计模式分解
javascript·vue.js·设计模式
战族狼魂4 小时前
用html+js+css实现的战略小游戏
javascript·css·html