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

3、食品包装控制系统 - /自动化与控制组件/food-packaging-control

76个工业组件库示例汇总

食品包装线控制系统

这是一个用于食品包装线控制系统的自定义组件,提供了食品包装生产线的可视化监控与控制界面。组件采用工业风格设计,包含生产流程控制、实时数据监控和逻辑编程三个主要功能区域。

功能特点

  1. 工业风格UI设计:深色主题,高对比度,符合工业控制系统的视觉风格
  2. 生产流程可视化:直观展示从上料到码垛的完整包装生产线工艺流程
  3. 实时数据监控:支持产量统计、质量数据和效率分析等多种数据可视化
  4. 逻辑控制编程:提供可视化编程、代码编辑和时序图三种逻辑开发方式
  5. 灵活的配置选项:可调整生产速度、包装规格等关键参数
  6. 告警系统:实时显示系统告警信息,支持告警确认和处理
  7. 系统状态监控:展示CPU负载、内存使用和通信状态等系统指标
  8. 响应式设计:适应不同屏幕尺寸,确保在各种设备上正常显示

主要区域说明

组件包含以下主要功能区域:

  1. 顶部控制栏:显示系统名称、状态和基本控制按钮(启动、停止、紧急停止)
  2. 生产流程控制区
    • 流程可视化:展示6个工站(上料、称重、包装、贴标、检测、码垛)的状态和连接
    • 生产参数控制:提供速度、包装规格和批次号等参数调整
  3. 实时数据监控区
    • 数据图表:多种图表类型,展示产量、质量和效率数据
    • 关键指标卡片:总产量、合格率、设备效率和运行时间等核心指标
  4. 逻辑编程控制区
    • 可视化编程:拖拽式逻辑流程创建
    • 代码编辑:支持梯形图、顺序功能图和结构化文本三种PLC编程语言
    • 时序图:工站操作的时序关系可视化
  5. 底部状态栏:显示告警信息、系统资源使用情况和当前时间

自定义选项

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

  • 颜色主题:在CSS中修改:root中的颜色变量
  • 工站配置:在HTML的process-line区域添加或修改工站
  • 图表类型:在JavaScript的updateChart函数中修改图表样式和数据
  • 告警阈值:在JavaScript的checkAlarms函数中调整告警触发条件
  • 逻辑编程界面:修改工具箱中的逻辑元件和操作类型

连接实际设备

组件目前使用模拟数据进行演示。要连接实际的生产线设备,需要:

  1. 替换JavaScript中的数据模拟函数,连接到实际的数据源
  2. 调整控制按钮的事件处理函数,使其发送实际的控制命令
  3. 实现数据持久化存储,保存历史数据和生产记录

项目结构

在这里插入图片描述

效果展示

在这里插入图片描述

源码

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 id="food-packaging-control"><!-- 顶部控制栏 --><div class="control-header"><div class="logo-section"><div class="system-logo">食品包装线控制系统</div><div class="system-version">v1.0</div></div><div class="control-status"><div class="status-item"><span class="status-label">系统状态:</span><span class="status-value" id="system-status">运行中</span><div class="status-indicator running"></div></div><div class="status-item"><span class="status-label">当前产量:</span><span class="status-value" id="current-output">1,245/小时</span></div><div class="control-actions"><button class="control-btn start-btn" id="start-system">启动系统</button><button class="control-btn stop-btn" id="stop-system">停止系统</button><button class="control-btn emergency-btn" id="emergency-stop">紧急停止</button></div></div></div><!-- 主内容区域 --><div class="main-content"><!-- 左侧:流程控制区 --><div class="panel process-panel"><div class="panel-header"><h3>生产流程控制</h3><div class="panel-actions"><button class="panel-btn" id="process-settings" title="流程设置"><i class="btn-icon"></i></button><button class="panel-btn" id="process-expand" title="展开视图"><i class="btn-icon"></i></button></div></div><div class="panel-body"><div class="process-visualization" id="process-visual"><!-- 包装流程可视化区域 --><div class="process-line"><div class="process-station" id="station-1"><div class="station-icon feeding"></div><div class="station-label">上料站</div><div class="station-status active" id="station-1-status"></div></div><div class="process-connection"><div class="process-flow" id="flow-1-2"></div></div><div class="process-station" id="station-2"><div class="station-icon weighing"></div><div class="station-label">称重站</div><div class="station-status active" id="station-2-status"></div></div><div class="process-connection"><div class="process-flow" id="flow-2-3"></div></div><div class="process-station" id="station-3"><div class="station-icon packaging"></div><div class="station-label">包装站</div><div class="station-status active" id="station-3-status"></div></div><div class="process-connection"><div class="process-flow" id="flow-3-4"></div></div><div class="process-station" id="station-4"><div class="station-icon labeling"></div><div class="station-label">贴标站</div><div class="station-status active" id="station-4-status"></div></div><div class="process-connection"><div class="process-flow" id="flow-4-5"></div></div><div class="process-station" id="station-5"><div class="station-icon inspection"></div><div class="station-label">检测站</div><div class="station-status active" id="station-5-status"></div></div><div class="process-connection"><div class="process-flow" id="flow-5-6"></div></div><div class="process-station" id="station-6"><div class="station-icon palletizing"></div><div class="station-label">码垛站</div><div class="station-status active" id="station-6-status"></div></div></div></div><div class="process-controls"><div class="control-group"><div class="control-label">生产速度:</div><div class="control-input"><input type="range" id="speed-control" min="50" max="150" value="100"><span id="speed-value">100%</span></div></div><div class="control-group"><div class="control-label">包装规格:</div><div class="control-input"><select id="package-type"><option value="small">小包装 (100g)</option><option value="medium" selected>中包装 (250g)</option><option value="large">大包装 (500g)</option><option value="bulk">散装 (1kg)</option></select></div></div><div class="control-group"><div class="control-label">批次号:</div><div class="control-input"><input type="text" id="batch-number" value="B202504091"></div></div></div></div></div><!-- 中间:实时数据与监控 --><div class="panel monitoring-panel"><div class="panel-header"><h3>实时数据监控</h3><div class="panel-actions"><select id="chart-selector"><option value="production">产量统计</option><option value="quality">质量数据</option><option value="efficiency">效率分析</option></select><button class="panel-btn" id="export-data" title="导出数据"><i class="btn-icon"></i></button></div></div><div class="panel-body"><div class="chart-container"><div class="chart-header"><span id="chart-title">当日产量数据</span><div class="chart-legend"><div class="legend-item"><span class="legend-color" style="background-color: #2196F3;"></span><span class="legend-text">实际产量</span></div><div class="legend-item"><span class="legend-color" style="background-color: #4CAF50;"></span><span class="legend-text">目标产量</span></div><div class="legend-item"><span class="legend-color" style="background-color: #FF5722;"></span><span class="legend-text">不良品率</span></div></div></div><div class="chart-wrapper" id="chart-area"><!-- 图表将由JavaScript渲染 --></div></div><div class="monitor-grid"><div class="monitor-card"><div class="card-label">总产量</div><div class="card-value" id="total-output">24,589</div><div class="card-unit">/</div></div><div class="monitor-card"><div class="card-label">合格率</div><div class="card-value" id="quality-rate">99.7%</div><div class="card-trend positive">0.2%</div></div><div class="monitor-card"><div class="card-label">设备效率</div><div class="card-value" id="equipment-efficiency">94.3%</div><div class="card-trend positive">1.5%</div></div><div class="monitor-card"><div class="card-label">运行时间</div><div class="card-value" id="running-time">06:42:15</div><div class="card-unit">::</div></div></div></div></div><!-- 右侧:逻辑编程区 --><div class="panel logic-panel"><div class="panel-header"><h3>逻辑控制编程</h3><div class="panel-actions"><button class="panel-btn" id="new-logic" title="新建"><i class="btn-icon">+</i></button><button class="panel-btn" id="save-logic" title="保存"><i class="btn-icon"></i></button><button class="panel-btn" id="deploy-logic" title="部署"><i class="btn-icon"></i></button></div></div><div class="panel-body"><div class="tab-header"><div class="tab active" data-tab="visual-programming">可视化编程</div><div class="tab" data-tab="code-editor">代码编辑</div><div class="tab" data-tab="sequence">时序图</div></div><div class="tab-content"><div class="tab-pane active" id="visual-programming-pane"><div class="logic-toolbox"><div class="toolbox-section"><div class="toolbox-title">控制元件</div><div class="toolbox-items"><div class="logic-item" draggable="true" data-type="start">开始</div><div class="logic-item" draggable="true" data-type="decision">判断</div><div class="logic-item" draggable="true" data-type="action">动作</div><div class="logic-item" draggable="true" data-type="delay">延时</div><div class="logic-item" draggable="true" data-type="parallel">并行</div><div class="logic-item" draggable="true" data-type="end">结束</div></div></div><div class="toolbox-section"><div class="toolbox-title">工站操作</div><div class="toolbox-items"><div class="logic-item" draggable="true" data-type="feeding">上料</div><div class="logic-item" draggable="true" data-type="weighing">称重</div><div class="logic-item" draggable="true" data-type="packaging">包装</div><div class="logic-item" draggable="true" data-type="labeling">贴标</div><div class="logic-item" draggable="true" data-type="inspection">检测</div><div class="logic-item" draggable="true" data-type="palletizing">码垛</div></div></div></div><div class="logic-canvas" id="logic-canvas"><!-- 逻辑流程图将由JavaScript渲染 --><div class="placeholder-text">拖放控制元件到此区域创建逻辑流程</div><div class="workflow-container" id="workflow-container"><!-- 工作流将在这里动态创建 --></div></div></div><div class="tab-pane" id="code-editor-pane"><div class="editor-toolbar"><select id="language-selector"><option value="ladder">梯形图</option><option value="sfc">顺序功能图</option><option value="st">结构化文本</option></select><button class="editor-btn" id="check-syntax">检查语法</button><button class="editor-btn" id="format-code">格式化</button></div><div class="code-editor" id="code-editor"><pre class="code-content" id="code-content">// 食品包装线控制逻辑// 以下为示例梯形图代码PROGRAM PackagingControlVARStartButton AT %I0.0: BOOL;StopButton AT %I0.1: BOOL;EmergencyStop AT %I0.2: BOOL;ConveyorRunning AT %Q0.0: BOOL;FeedingSystem AT %Q0.1: BOOL;WeighingSystem AT %Q0.2: BOOL;PackagingSystem AT %Q0.3: BOOL;LabelingSystem AT %Q0.4: BOOL;InspectionSystem AT %Q0.5: BOOL;PalletizingSystem AT %Q0.6: BOOL;SystemRunning: BOOL;ProductDetected AT %I0.3: BOOL;WeightOK AT %I0.4: BOOL;PackageSealed AT %I0.5: BOOL;LabelApplied AT %I0.6: BOOL;QualityCheck AT %I0.7: BOOL;END_VAR// 主控制逻辑SystemRunning := StartButton AND NOT StopButton AND NOT EmergencyStop;ConveyorRunning := SystemRunning;// 各工站控制逻辑FeedingSystem := SystemRunning AND (FeedingSystem OR NOT WeighingSystem);WeighingSystem := SystemRunning AND ProductDetected AND WeightOK;PackagingSystem := SystemRunning AND WeighingSystem AND WeightOK;LabelingSystem := SystemRunning AND PackageSealed;InspectionSystem := SystemRunning AND LabelApplied;PalletizingSystem := SystemRunning AND QualityCheck;END_PROGRAM</pre></div></div><div class="tab-pane" id="sequence-pane"><div class="sequence-diagram" id="sequence-diagram"><!-- 时序图将由JavaScript渲染 --><div class="placeholder-text">选择工站查看详细时序图</div></div><div class="sequence-controls"><div class="sequence-station-selector"><label for="station-selector">选择工站:</label><select id="station-selector"><option value="all">整体流程</option><option value="station-1">上料站</option><option value="station-2">称重站</option><option value="station-3">包装站</option><option value="station-4">贴标站</option><option value="station-5">检测站</option><option value="station-6">码垛站</option></select></div><div class="sequence-time-scale"><label for="time-scale">时间尺度:</label><select id="time-scale"><option value="1x">1x (实时)</option><option value="2x">2x (加速)</option><option value="0.5x">0.5x (减速)</option></select></div></div></div></div></div></div></div><!-- 底部状态栏 --><div class="footer-bar"><div class="alarm-section"><div class="alarm-icon" id="alarm-icon"></div><div class="alarm-count" id="alarm-count">0</div><div class="alarm-message" id="current-alarm">无告警信息</div></div><div class="system-metrics"><div class="metric"><span class="metric-label">CPU负载:</span><span class="metric-value" id="cpu-load">32%</span></div><div class="metric"><span class="metric-label">内存使用:</span><span class="metric-value" id="memory-usage">1.2GB/4GB</span></div><div class="metric"><span class="metric-label">通信状态:</span><span class="metric-value connected" id="comm-status">已连接</span></div></div><div class="system-time" id="system-time">2025-04-09 08:51:22</div></div><!-- 告警弹窗 --><div class="alarm-modal" id="alarm-modal"><div class="alarm-modal-header"><div class="alarm-modal-title">系统告警</div><div class="alarm-modal-close" id="close-alarm-modal">×</div></div><div class="alarm-modal-body"><div class="alarm-list" id="alarm-list"><!-- 告警列表将由JavaScript动态生成 --></div></div><div class="alarm-modal-footer"><button class="alarm-btn" id="acknowledge-all">确认所有</button><button class="alarm-btn" id="close-modal">关闭</button></div></div></div> <script src="script.js"></script>
</body>
</html> 

styles.css

/* 食品包装线控制系统 - 样式表 */
:root {/* 颜色变量 */--primary-dark: #1a2b42;  /* 主要深色背景 */--primary-medium: #253952; /* 次要深色背景 */--primary-light: #2d4263;  /* 浅色背景 */--accent-blue: #0288d1;    /* 蓝色强调 */--accent-green: #2e7d32;   /* 绿色强调 */--accent-red: #d32f2f;     /* 红色强调/告警 */--accent-orange: #f57c00;  /* 橙色强调/警告 */--text-primary: #ffffff;   /* 主要文本 */--text-secondary: #b0bec5; /* 次要文本 */--border-color: #37474f;   /* 边框颜色 */--hover-color: rgba(255, 255, 255, 0.08); /* 悬停效果 *//* 尺寸变量 */--header-height: 60px;--footer-height: 40px;--panel-gap: 12px;--border-radius: 4px;
}/* 基础样式 */
#food-packaging-control {font-family: 'Roboto', 'Arial', sans-serif;color: var(--text-primary);background-color: var(--primary-dark);display: flex;flex-direction: column;height: 100vh;overflow: hidden;box-sizing: border-box;margin: 0;padding: 0;user-select: none;
}#food-packaging-control * {box-sizing: border-box;
}/* 顶部控制栏 */
.control-header {height: var(--header-height);background-color: var(--primary-medium);border-bottom: 1px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;padding: 0 16px;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);z-index: 10;
}.logo-section {display: flex;flex-direction: column;align-items: flex-start;
}.system-logo {font-size: 1.2rem;font-weight: bold;letter-spacing: 0.5px;
}.system-version {font-size: 0.7rem;color: var(--text-secondary);
}.control-status {display: flex;align-items: center;gap: 24px;
}.status-item {display: flex;align-items: center;gap: 8px;
}.status-label {color: var(--text-secondary);font-size: 0.85rem;
}.status-value {font-weight: bold;font-size: 0.9rem;
}.status-indicator {width: 10px;height: 10px;border-radius: 50%;
}.status-indicator.running {background-color: var(--accent-green);box-shadow: 0 0 8px var(--accent-green);
}.status-indicator.warning {background-color: var(--accent-orange);box-shadow: 0 0 8px var(--accent-orange);
}.status-indicator.error {background-color: var(--accent-red);box-shadow: 0 0 8px var(--accent-red);
}.status-indicator.idle {background-color: var(--text-secondary);
}.control-actions {display: flex;gap: 8px;
}.control-btn {padding: 8px 12px;border: none;border-radius: var(--border-radius);font-weight: bold;font-size: 0.85rem;cursor: pointer;transition: all 0.2s;
}.start-btn {background-color: var(--accent-green);color: white;
}.start-btn:hover {background-color: #388e3c;
}.stop-btn {background-color: #455a64;color: white;
}.stop-btn:hover {background-color: #546e7a;
}.emergency-btn {background-color: var(--accent-red);color: white;
}.emergency-btn:hover {background-color: #e53935;
}/* 主内容区域 */
.main-content {flex: 1;display: flex;gap: var(--panel-gap);padding: var(--panel-gap);height: calc(100vh - var(--header-height) - var(--footer-height));overflow: hidden;
}/* 面板通用样式 */
.panel {background-color: var(--primary-medium);border-radius: var(--border-radius);box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);overflow: hidden;display: flex;flex-direction: column;
}.process-panel {flex: 1;
}.monitoring-panel {flex: 1.5;
}.logic-panel {flex: 1.5;
}.panel-header {display: flex;justify-content: space-between;align-items: center;padding: 12px 16px;background-color: var(--primary-light);border-bottom: 1px solid var(--border-color);
}.panel-header h3 {margin: 0;font-size: 1rem;font-weight: 500;letter-spacing: 0.5px;
}.panel-actions {display: flex;gap: 8px;align-items: center;
}.panel-btn {width: 30px;height: 30px;border: none;border-radius: 4px;background-color: var(--primary-medium);color: var(--text-primary);cursor: pointer;display: flex;align-items: center;justify-content: center;transition: background-color 0.2s;
}.panel-btn:hover {background-color: var(--hover-color);
}.btn-icon {font-style: normal;
}.panel-body {flex: 1;overflow: auto;padding: 16px;display: flex;flex-direction: column;gap: 16px;
}/* 流程控制面板 */
.process-visualization {background-color: var(--primary-dark);border-radius: var(--border-radius);padding: 16px;min-height: 180px;overflow: auto;
}.process-line {display: flex;align-items: center;justify-content: space-between;padding: 8px 0;
}.process-station {position: relative;width: 80px;display: flex;flex-direction: column;align-items: center;gap: 8px;z-index: 2;
}.station-icon {width: 50px;height: 50px;border-radius: 8px;background-color: var(--primary-light);border: 2px solid var(--border-color);display: flex;align-items: center;justify-content: center;position: relative;transition: all 0.3s;
}.station-icon::before {font-family: Arial, sans-serif;font-size: 24px;font-weight: bold;
}.station-icon.feeding::before { content: "F"; color: #64b5f6; }
.station-icon.weighing::before { content: "W"; color: #81c784; }
.station-icon.packaging::before { content: "P"; color: #ffb74d; }
.station-icon.labeling::before { content: "L"; color: #ba68c8; }
.station-icon.inspection::before { content: "I"; color: #4fc3f7; }
.station-icon.palletizing::before { content: "S"; color: #f06292; }.station-label {font-size: 0.8rem;text-align: center;
}.station-status {position: absolute;width: 10px;height: 10px;border-radius: 50%;top: -5px;right: 12px;
}.station-status.active {background-color: var(--accent-green);box-shadow: 0 0 5px var(--accent-green);
}.station-status.warning {background-color: var(--accent-orange);box-shadow: 0 0 5px var(--accent-orange);
}.station-status.error {background-color: var(--accent-red);box-shadow: 0 0 5px var(--accent-red);
}.station-status.idle {background-color: var(--text-secondary);
}.process-connection {flex: 1;display: flex;align-items: center;justify-content: center;padding: 0 4px;z-index: 1;
}.process-flow {height: 4px;background-color: var(--accent-blue);width: 100%;position: relative;overflow: hidden;
}.process-flow.active::after {content: '';position: absolute;top: 0;left: -20%;height: 100%;width: 20%;background-color: rgba(255, 255, 255, 0.7);animation: flow 1.5s linear infinite;
}@keyframes flow {0% { left: -20%; }100% { left: 100%; }
}.process-controls {display: flex;flex-direction: column;gap: 12px;margin-top: 16px;
}.control-group {display: flex;justify-content: space-between;align-items: center;
}.control-label {font-size: 0.85rem;color: var(--text-secondary);
}.control-input {flex: 1;max-width: 200px;display: flex;align-items: center;gap: 8px;
}input[type="range"] {flex: 1;cursor: pointer;
}input[type="text"], select {background-color: var(--primary-dark);border: 1px solid var(--border-color);border-radius: 4px;color: var(--text-primary);padding: 6px 10px;width: 100%;font-size: 0.85rem;
}select {appearance: none;background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23b0bec5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat: no-repeat;background-position: right 8px center;padding-right: 30px;
}/* 监控面板 */
.chart-container {background-color: var(--primary-dark);border-radius: var(--border-radius);padding: 16px;min-height: 240px;display: flex;flex-direction: column;gap: 12px;
}.chart-header {display: flex;justify-content: space-between;align-items: center;flex-wrap: wrap;gap: 8px;
}#chart-title {font-weight: 500;font-size: 0.9rem;
}.chart-legend {display: flex;gap: 12px;
}.legend-item {display: flex;align-items: center;gap: 4px;font-size: 0.8rem;
}.legend-color {width: 12px;height: 12px;border-radius: 2px;
}.chart-wrapper {flex: 1;min-height: 180px;position: relative;display: flex;align-items: center;justify-content: center;
}.monitor-grid {display: grid;grid-template-columns: repeat(4, 1fr);gap: 12px;
}.monitor-card {background-color: var(--primary-dark);border-radius: var(--border-radius);padding: 12px;display: flex;flex-direction: column;align-items: center;text-align: center;
}.card-label {font-size: 0.8rem;color: var(--text-secondary);margin-bottom: 4px;
}.card-value {font-size: 1.5rem;font-weight: bold;line-height: 1;margin-bottom: 4px;
}.card-unit {font-size: 0.75rem;color: var(--text-secondary);
}.card-trend {font-size: 0.75rem;font-weight: 500;
}.card-trend.positive {color: var(--accent-green);
}.card-trend.negative {color: var(--accent-red);
}/* 逻辑编程面板 */
.tab-header {display: flex;border-bottom: 1px solid var(--border-color);margin-bottom: 16px;margin-top: -8px;
}.tab {padding: 8px 16px;font-size: 0.85rem;cursor: pointer;border-bottom: 2px solid transparent;transition: all 0.2s;
}.tab:hover {background-color: var(--hover-color);
}.tab.active {border-bottom: 2px solid var(--accent-blue);color: var(--accent-blue);
}.tab-content {flex: 1;overflow: hidden;display: flex;
}.tab-pane {flex: 1;display: none;flex-direction: column;height: 100%;overflow: hidden;
}.tab-pane.active {display: flex;
}/* 可视化编程区域 */
.logic-toolbox {background-color: var(--primary-dark);border-radius: var(--border-radius);padding: 12px;display: flex;flex-wrap: wrap;gap: 12px;margin-bottom: 12px;
}.toolbox-section {flex: 1;min-width: 150px;
}.toolbox-title {font-size: 0.8rem;color: var(--text-secondary);margin-bottom: 8px;
}.toolbox-items {display: flex;flex-wrap: wrap;gap: 8px;
}.logic-item {background-color: var(--primary-light);border: 1px solid var(--border-color);border-radius: var(--border-radius);padding: 6px 10px;font-size: 0.85rem;cursor: grab;transition: all 0.2s;
}.logic-item:hover {background-color: var(--hover-color);border-color: var(--accent-blue);
}.logic-canvas {flex: 1;background-color: var(--primary-dark);border-radius: var(--border-radius);overflow: auto;position: relative;min-height: 250px;
}.placeholder-text {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);color: var(--text-secondary);font-size: 0.9rem;text-align: center;opacity: 0.7;
}.workflow-container {padding: 20px;min-height: 100%;position: relative;
}/* 代码编辑区 */
.editor-toolbar {display: flex;gap: 8px;margin-bottom: 12px;
}.editor-btn {padding: 6px 12px;font-size: 0.85rem;border: none;background-color: var(--primary-dark);color: var(--text-primary);border-radius: var(--border-radius);cursor: pointer;transition: all 0.2s;
}.editor-btn:hover {background-color: var(--hover-color);
}.code-editor {flex: 1;background-color: var(--primary-dark);border-radius: var(--border-radius);overflow: auto;
}.code-content {font-family: 'Consolas', 'Monaco', monospace;font-size: 0.9rem;line-height: 1.5;padding: 12px;margin: 0;color: var(--text-primary);overflow: auto;tab-size: 4;
}/* 时序图区域 */
.sequence-diagram {flex: 1;background-color: var(--primary-dark);border-radius: var(--border-radius);overflow: auto;position: relative;min-height: 250px;
}.sequence-controls {display: flex;justify-content: space-between;margin-top: 12px;padding: 8px 12px;background-color: var(--primary-dark);border-radius: var(--border-radius);
}.sequence-station-selector,
.sequence-time-scale {display: flex;align-items: center;gap: 8px;font-size: 0.85rem;
}.sequence-station-selector label,
.sequence-time-scale label {color: var(--text-secondary);
}.sequence-station-selector select,
.sequence-time-scale select {width: auto;
}/* 底部状态栏 */
.footer-bar {height: var(--footer-height);background-color: var(--primary-medium);border-top: 1px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;padding: 0 16px;font-size: 0.8rem;
}.alarm-section {display: flex;align-items: center;gap: 8px;
}.alarm-icon {color: var(--accent-orange);font-size: 1rem;
}.alarm-icon.active {animation: blink 1s infinite;
}@keyframes blink {0%, 100% { opacity: 1; }50% { opacity: 0.5; }
}.alarm-count {background-color: var(--accent-red);color: white;border-radius: 10px;padding: 0 6px;min-width: 18px;font-size: 0.7rem;text-align: center;display: inline-block;
}.alarm-message {color: var(--text-secondary);
}.system-metrics {display: flex;gap: 16px;
}.metric {display: flex;align-items: center;gap: 4px;
}.metric-label {color: var(--text-secondary);
}.metric-value {font-weight: 500;
}.connected {color: var(--accent-green);
}.disconnected {color: var(--accent-red);
}.system-time {color: var(--text-secondary);
}/* 告警弹窗 */
.alarm-modal {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);background-color: var(--primary-medium);border-radius: var(--border-radius);box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);z-index: 1000;width: 500px;max-width: 90vw;max-height: 80vh;display: none;flex-direction: column;
}.alarm-modal.show {display: flex;
}.alarm-modal-header {padding: 12px 16px;background-color: var(--primary-light);border-bottom: 1px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;
}.alarm-modal-title {font-weight: 500;font-size: 1rem;
}.alarm-modal-close {font-size: 1.2rem;cursor: pointer;width: 24px;height: 24px;display: flex;align-items: center;justify-content: center;border-radius: 50%;
}.alarm-modal-close:hover {background-color: var(--hover-color);
}.alarm-modal-body {padding: 16px;overflow-y: auto;flex: 1;
}.alarm-list {display: flex;flex-direction: column;gap: 8px;
}.alarm-item {display: flex;align-items: flex-start;gap: 12px;padding: 8px;border-radius: var(--border-radius);background-color: var(--primary-dark);
}.alarm-item-icon {font-size: 1.2rem;margin-top: 2px;
}.alarm-item-icon.critical {color: var(--accent-red);
}.alarm-item-icon.warning {color: var(--accent-orange);
}.alarm-item-icon.info {color: var(--accent-blue);
}.alarm-item-content {flex: 1;
}.alarm-item-title {font-weight: 500;font-size: 0.9rem;
}.alarm-item-desc {font-size: 0.8rem;color: var(--text-secondary);margin-top: 4px;
}.alarm-item-time {font-size: 0.75rem;color: var(--text-secondary);margin-top: 4px;
}.alarm-modal-footer {padding: 12px 16px;border-top: 1px solid var(--border-color);display: flex;justify-content: flex-end;gap: 8px;
}.alarm-btn {padding: 6px 12px;border: none;border-radius: var(--border-radius);font-size: 0.85rem;cursor: pointer;background-color: var(--primary-dark);color: var(--text-primary);transition: all 0.2s;
}.alarm-btn:hover {background-color: var(--hover-color);
}/* 响应式布局 */
@media (max-width: 1200px) {.main-content {flex-direction: column;overflow-y: auto;height: calc(100vh - var(--header-height) - var(--footer-height));}.process-panel, .monitoring-panel, .logic-panel {width: 100%;flex: none;}.panel {max-height: 600px;}.monitor-grid {grid-template-columns: repeat(2, 1fr);}.control-status {flex-wrap: wrap;}
}@media (max-width: 768px) {.control-header {flex-direction: column;height: auto;padding: 12px;gap: 12px;}.logo-section {align-items: center;}.control-status {width: 100%;justify-content: center;}.footer-bar {flex-direction: column;height: auto;padding: 8px;gap: 8px;text-align: center;}.system-metrics {flex-wrap: wrap;justify-content: center;}.monitor-grid {grid-template-columns: 1fr;}
} 

script.js

/*** 食品包装线控制系统 - JavaScript控制逻辑* 本文件实现了食品包装线控制系统的各项功能,包括:* - 系统状态管理* - 工站控制和状态监控* - 实时数据可视化* - 逻辑编程界面交互* - 告警管理*/// 全局变量
let systemRunning = true;          // 系统运行状态
let productionSpeed = 100;         // 生产速度(%)
let currentPackageType = 'medium'; // 当前包装类型
let batchNumber = 'B202504091';    // 当前批次号
let runningTime = 0;               // 运行时间(秒)
let totalProduction = 24589;       // 总产量
let alarmCount = 0;                // 告警数量
let alarms = [];                   // 告警列表
let stationStatus = {              // 各工站状态'station-1': 'active','station-2': 'active','station-3': 'active','station-4': 'active','station-5': 'active','station-6': 'active'
};
let flowStatus = {                 // 物料流状态'flow-1-2': true,'flow-2-3': true,'flow-3-4': true,'flow-4-5': true,'flow-5-6': true
};// 计时器
let runningTimeInterval;
let dataUpdateInterval;
let chartUpdateInterval;
let flowAnimationInterval;
let systemMetricsInterval;// 图表数据
let productionData = {labels: [...Array(12).keys()].map(i => `${8 + Math.floor(i/2)}:${i % 2 ? '30' : '00'}`),actual: [920, 1050, 1150, 1230, 1320, 1260, 1200, 1310, 1400, 1380, 1420, 1245],target: [1000, 1000, 1200, 1200, 1300, 1300, 1300, 1300, 1400, 1400, 1400, 1400],defects: [2.1, 1.8, 1.9, 0.8, 0.5, 0.7, 0.9, 0.6, 0.4, 0.5, 0.3, 0.3]
};let qualityData = {labels: ['100g', '250g', '500g', '1kg'],pass: [99.2, 99.7, 99.5, 99.1],weight: [99.5, 99.8, 99.6, 99.4],seal: [99.7, 99.9, 99.8, 99.5],label: [99.3, 99.6, 99.4, 99.2]
};let efficiencyData = {labels: [...Array(8).keys()].map(i => `${i + 8}:00`),oee: [91.2, 92.5, 93.8, 94.2, 94.5, 94.7, 94.3, 93.6],uptime: [97.5, 98.2, 98.5, 98.7, 98.8, 98.5, 98.1, 97.8],performance: [94.5, 95.2, 96.1, 96.3, 96.5, 96.7, 96.3, 95.8]
};// 工作流元素数据
let workflowElements = [];/*** 初始化函数 - 页面加载完成后执行*/
function initializeSystem() {// 绑定事件监听器bindEventListeners();// 启动系统模拟startSystemSimulation();// 初始化图表updateChart('production');// 初始化逻辑编程界面initializeLogicProgramming();// 更新系统时间updateSystemTime();console.log('食品包装线控制系统初始化完成');
}/*** 绑定所有UI元素的事件监听器*/
function bindEventListeners() {// 系统控制按钮document.getElementById('start-system').addEventListener('click', startSystem);document.getElementById('stop-system').addEventListener('click', stopSystem);document.getElementById('emergency-stop').addEventListener('click', emergencyStop);// 流程控制面板document.getElementById('process-settings').addEventListener('click', showProcessSettings);document.getElementById('process-expand').addEventListener('click', expandProcessView);document.getElementById('speed-control').addEventListener('input', updateSpeed);document.getElementById('package-type').addEventListener('change', updatePackageType);document.getElementById('batch-number').addEventListener('change', updateBatchNumber);// 监控面板document.getElementById('chart-selector').addEventListener('change', (e) => updateChart(e.target.value));document.getElementById('export-data').addEventListener('click', exportMonitoringData);// 逻辑编程面板document.querySelectorAll('.tab').forEach(tab => {tab.addEventListener('click', switchProgrammingTab);});document.getElementById('new-logic').addEventListener('click', createNewLogic);document.getElementById('save-logic').addEventListener('click', saveLogic);document.getElementById('deploy-logic').addEventListener('click', deployLogic);document.querySelectorAll('.logic-item').forEach(item => {item.addEventListener('dragstart', handleDragStart);});document.getElementById('logic-canvas').addEventListener('dragover', handleDragOver);document.getElementById('logic-canvas').addEventListener('drop', handleDrop);document.getElementById('check-syntax').addEventListener('click', checkCodeSyntax);document.getElementById('format-code').addEventListener('click', formatCode);document.getElementById('station-selector').addEventListener('change', updateSequenceDiagram);// 告警相关document.getElementById('alarm-icon').addEventListener('click', showAlarmModal);document.getElementById('close-alarm-modal').addEventListener('click', hideAlarmModal);document.getElementById('close-modal').addEventListener('click', hideAlarmModal);document.getElementById('acknowledge-all').addEventListener('click', acknowledgeAllAlarms);
}/*** 启动系统模拟*/
function startSystemSimulation() {// 设置运行时间计时器runningTimeInterval = setInterval(updateRunningTime, 1000);// 设置数据更新计时器dataUpdateInterval = setInterval(updateRealTimeData, 2000);// 设置图表更新计时器chartUpdateInterval = setInterval(() => {if (document.getElementById('chart-selector').value === 'production') {updateProductionData();updateChart('production');}}, 10000);// 设置流动动画flowAnimationInterval = setInterval(updateFlowAnimation, 5000);// 设置系统指标更新systemMetricsInterval = setInterval(updateSystemMetrics, 3000);
}/*** 停止系统模拟*/
function stopSystemSimulation() {clearInterval(runningTimeInterval);clearInterval(dataUpdateInterval);clearInterval(chartUpdateInterval);clearInterval(flowAnimationInterval);clearInterval(systemMetricsInterval);
}/*** 启动系统*/
function startSystem() {if (!systemRunning) {systemRunning = true;document.getElementById('system-status').textContent = '运行中';document.querySelector('.status-indicator').className = 'status-indicator running';startSystemSimulation();// 更新各工站状态Object.keys(stationStatus).forEach(station => {stationStatus[station] = 'active';document.getElementById(station + '-status').className = 'station-status active';});// 更新流动状态Object.keys(flowStatus).forEach(flow => {flowStatus[flow] = true;document.getElementById(flow).className = 'process-flow active';});addAlarm('info', '系统已启动', '操作员启动了系统');}
}/*** 停止系统*/
function stopSystem() {if (systemRunning) {systemRunning = false;document.getElementById('system-status').textContent = '已停止';document.querySelector('.status-indicator').className = 'status-indicator idle';stopSystemSimulation();// 更新各工站状态Object.keys(stationStatus).forEach(station => {stationStatus[station] = 'idle';document.getElementById(station + '-status').className = 'station-status idle';});// 更新流动状态Object.keys(flowStatus).forEach(flow => {flowStatus[flow] = false;document.getElementById(flow).className = 'process-flow';});addAlarm('info', '系统已停止', '操作员停止了系统');}
}/*** 紧急停止*/
function emergencyStop() {systemRunning = false;document.getElementById('system-status').textContent = '紧急停止';document.querySelector('.status-indicator').className = 'status-indicator error';stopSystemSimulation();// 更新各工站状态Object.keys(stationStatus).forEach(station => {stationStatus[station] = 'error';document.getElementById(station + '-status').className = 'station-status error';});// 更新流动状态Object.keys(flowStatus).forEach(flow => {flowStatus[flow] = false;document.getElementById(flow).className = 'process-flow';});addAlarm('critical', '系统紧急停止', '触发紧急停止按钮');
}/*** 显示流程设置*/
function showProcessSettings() {// 此处可实现流程设置弹窗console.log('显示流程设置');
}/*** 展开流程视图*/
function expandProcessView() {// 此处可实现流程图的全屏展示console.log('展开流程视图');
}/*** 更新速度设置*/
function updateSpeed(event) {productionSpeed = event.target.value;document.getElementById('speed-value').textContent = productionSpeed + '%';console.log('生产速度已更新:', productionSpeed + '%');
}/*** 更新包装类型*/
function updatePackageType(event) {currentPackageType = event.target.value;console.log('包装规格已更新:', currentPackageType);
}/*** 更新批次号*/
function updateBatchNumber(event) {batchNumber = event.target.value;console.log('批次号已更新:', batchNumber);
}/*** 导出监控数据*/
function exportMonitoringData() {console.log('导出监控数据');// 此处可实现数据导出功能
}/*** 更新图表显示*/
function updateChart(chartType) {const chartArea = document.getElementById('chart-area');let chartTitle = '';// 清空图表区域chartArea.innerHTML = '';// 根据图表类型显示不同的图表switch(chartType) {case 'production':chartTitle = '当日产量数据';renderProductionChart(chartArea);break;case 'quality':chartTitle = '产品质量数据';renderQualityChart(chartArea);break;case 'efficiency':chartTitle = '生产效率分析';renderEfficiencyChart(chartArea);break;}document.getElementById('chart-title').textContent = chartTitle;
}/*** 渲染产量图表*/
function renderProductionChart(container) {try {// 尝试使用Chart.js绘制图表if (typeof Chart !== 'undefined') {const canvas = document.createElement('canvas');container.appendChild(canvas);new Chart(canvas, {type: 'line',data: {labels: productionData.labels,datasets: [{label: '实际产量',data: productionData.actual,borderColor: '#2196F3',backgroundColor: 'rgba(33, 150, 243, 0.1)',tension: 0.3,fill: true},{label: '目标产量',data: productionData.target,borderColor: '#4CAF50',backgroundColor: 'transparent',borderDash: [5, 5],tension: 0.1},{label: '不良品率(%)',data: productionData.defects,borderColor: '#FF5722',backgroundColor: 'transparent',yAxisID: 'y1'}]},options: {scales: {y: {beginAtZero: false,title: {display: true,text: '产量 (件/小时)'}},y1: {position: 'right',beginAtZero: true,max: 5,title: {display: true,text: '不良品率 (%)'}}}}});} else {// 使用简单SVG绘制图表renderSimpleProductionChart(container);}} catch (error) {console.error('图表渲染失败:', error);renderSimpleProductionChart(container);}
}/*** 渲染简单SVG产量图表(当Chart.js不可用时)*/
function renderSimpleProductionChart(container) {const svgNS = "http://www.w3.org/2000/svg";const width = container.clientWidth;const height = 250;const svg = document.createElementNS(svgNS, "svg");svg.setAttribute("width", width);svg.setAttribute("height", height);svg.style.backgroundColor = "var(--primary-dark)";const maxValue = Math.max(...productionData.actual, ...productionData.target);const xStep = width / (productionData.labels.length - 1);const yScale = (height - 40) / maxValue;// 绘制坐标轴const axis = document.createElementNS(svgNS, "path");axis.setAttribute("d", `M 30 10 V ${height - 30} H ${width - 10}`);axis.setAttribute("stroke", "var(--border-color)");axis.setAttribute("fill", "none");svg.appendChild(axis);// 绘制实际产量线const actualLine = document.createElementNS(svgNS, "path");let path = `M ${30} ${height - 30 - productionData.actual[0] * yScale}`;for (let i = 1; i < productionData.actual.length; i++) {path += ` L ${30 + i * xStep} ${height - 30 - productionData.actual[i] * yScale}`;}actualLine.setAttribute("d", path);actualLine.setAttribute("stroke", "#2196F3");actualLine.setAttribute("stroke-width", "2");actualLine.setAttribute("fill", "none");svg.appendChild(actualLine);// 绘制目标产量线(虚线)const targetLine = document.createElementNS(svgNS, "path");path = `M ${30} ${height - 30 - productionData.target[0] * yScale}`;for (let i = 1; i < productionData.target.length; i++) {path += ` L ${30 + i * xStep} ${height - 30 - productionData.target[i] * yScale}`;}targetLine.setAttribute("d", path);targetLine.setAttribute("stroke", "#4CAF50");targetLine.setAttribute("stroke-width", "2");targetLine.setAttribute("stroke-dasharray", "5,5");targetLine.setAttribute("fill", "none");svg.appendChild(targetLine);container.appendChild(svg);
}/*** 渲染质量图表*/
function renderQualityChart(container) {try {// 尝试使用Chart.js绘制图表if (typeof Chart !== 'undefined') {const canvas = document.createElement('canvas');container.appendChild(canvas);new Chart(canvas, {type: 'bar',data: {labels: qualityData.labels,datasets: [{label: '合格率',data: qualityData.pass,backgroundColor: '#4CAF50'},{label: '重量合格',data: qualityData.weight,backgroundColor: '#2196F3'},{label: '密封合格',data: qualityData.seal,backgroundColor: '#FF9800'},{label: '标签合格',data: qualityData.label,backgroundColor: '#9C27B0'}]},options: {scales: {y: {min: 95,max: 100,title: {display: true,text: '合格率 (%)'}}}}});} else {// 使用简单SVG绘制图表renderSimpleQualityChart(container);}} catch (error) {console.error('图表渲染失败:', error);renderSimpleQualityChart(container);}
}/*** 渲染简单SVG质量图表(当Chart.js不可用时)*/
function renderSimpleQualityChart(container) {const svgNS = "http://www.w3.org/2000/svg";const width = container.clientWidth;const height = 250;const svg = document.createElementNS(svgNS, "svg");svg.setAttribute("width", width);svg.setAttribute("height", height);svg.style.backgroundColor = "var(--primary-dark)";const barWidth = 15;const groupWidth = barWidth * 4 + 20;const xStep = width / (qualityData.labels.length + 1);const yScale = (height - 40) / 5; // 5% scale (95-100%)// 绘制坐标轴const axis = document.createElementNS(svgNS, "path");axis.setAttribute("d", `M 30 10 V ${height - 30} H ${width - 10}`);axis.setAttribute("stroke", "var(--border-color)");axis.setAttribute("fill", "none");svg.appendChild(axis);// 绘制柱状图const colors = ['#4CAF50', '#2196F3', '#FF9800', '#9C27B0'];for (let i = 0; i < qualityData.labels.length; i++) {const x = 50 + i * xStep;// 绘制4种不同质量数据的柱状图for (let j = 0; j < 4; j++) {const dataKey = ['pass', 'weight', 'seal', 'label'][j];const value = qualityData[dataKey][i];const barHeight = (value - 95) * yScale;const bar = document.createElementNS(svgNS, "rect");bar.setAttribute("x", x + j * (barWidth + 5));bar.setAttribute("y", height - 30 - barHeight);bar.setAttribute("width", barWidth);bar.setAttribute("height", barHeight);bar.setAttribute("fill", colors[j]);svg.appendChild(bar);}// 绘制标签const text = document.createElementNS(svgNS, "text");text.setAttribute("x", x + groupWidth / 2 - 15);text.setAttribute("y", height - 10);text.setAttribute("fill", "var(--text-secondary)");text.setAttribute("font-size", "12");text.textContent = qualityData.labels[i];svg.appendChild(text);}container.appendChild(svg);
}/*** 渲染效率图表*/
function renderEfficiencyChart(container) {try {// 尝试使用Chart.js绘制图表if (typeof Chart !== 'undefined') {const canvas = document.createElement('canvas');container.appendChild(canvas);new Chart(canvas, {type: 'line',data: {labels: efficiencyData.labels,datasets: [{label: 'OEE',data: efficiencyData.oee,borderColor: '#2196F3',backgroundColor: 'rgba(33, 150, 243, 0.1)',tension: 0.3,fill: true},{label: '运行时间',data: efficiencyData.uptime,borderColor: '#4CAF50',backgroundColor: 'transparent',tension: 0.3},{label: '性能效率',data: efficiencyData.performance,borderColor: '#FF9800',backgroundColor: 'transparent',tension: 0.3}]},options: {scales: {y: {min: 85,max: 100,title: {display: true,text: '效率 (%)'}}}}});} else {// 使用简单SVG绘制图表renderSimpleEfficiencyChart(container);}} catch (error) {console.error('图表渲染失败:', error);renderSimpleEfficiencyChart(container);}
}/*** 渲染简单SVG效率图表(当Chart.js不可用时)*/
function renderSimpleEfficiencyChart(container) {const svgNS = "http://www.w3.org/2000/svg";const width = container.clientWidth;const height = 250;const svg = document.createElementNS(svgNS, "svg");svg.setAttribute("width", width);svg.setAttribute("height", height);svg.style.backgroundColor = "var(--primary-dark)";const xStep = width / (efficiencyData.labels.length + 1);const yScale = (height - 40) / 15; // 15% scale (85-100%)// 绘制坐标轴const axis = document.createElementNS(svgNS, "path");axis.setAttribute("d", `M 30 10 V ${height - 30} H ${width - 10}`);axis.setAttribute("stroke", "var(--border-color)");axis.setAttribute("fill", "none");svg.appendChild(axis);// 绘制OEE线const oeeLine = document.createElementNS(svgNS, "path");let path = `M ${50} ${height - 30 - (efficiencyData.oee[0] - 85) * yScale}`;for (let i = 1; i < efficiencyData.oee.length; i++) {path += ` L ${50 + i * xStep} ${height - 30 - (efficiencyData.oee[i] - 85) * yScale}`;}oeeLine.setAttribute("d", path);oeeLine.setAttribute("stroke", "#2196F3");oeeLine.setAttribute("stroke-width", "2");oeeLine.setAttribute("fill", "none");svg.appendChild(oeeLine);// 绘制运行时间线const uptimeLine = document.createElementNS(svgNS, "path");path = `M ${50} ${height - 30 - (efficiencyData.uptime[0] - 85) * yScale}`;for (let i = 1; i < efficiencyData.uptime.length; i++) {path += ` L ${50 + i * xStep} ${height - 30 - (efficiencyData.uptime[i] - 85) * yScale}`;}uptimeLine.setAttribute("d", path);uptimeLine.setAttribute("stroke", "#4CAF50");uptimeLine.setAttribute("stroke-width", "2");uptimeLine.setAttribute("fill", "none");svg.appendChild(uptimeLine);// 绘制性能效率线const perfLine = document.createElementNS(svgNS, "path");path = `M ${50} ${height - 30 - (efficiencyData.performance[0] - 85) * yScale}`;for (let i = 1; i < efficiencyData.performance.length; i++) {path += ` L ${50 + i * xStep} ${height - 30 - (efficiencyData.performance[i] - 85) * yScale}`;}perfLine.setAttribute("d", path);perfLine.setAttribute("stroke", "#FF9800");perfLine.setAttribute("stroke-width", "2");perfLine.setAttribute("fill", "none");svg.appendChild(perfLine);container.appendChild(svg);
}/*** 更新实时数据*/
function updateRealTimeData() {if (!systemRunning) return;// 模拟产量数据const hourlyRate = Math.floor(1200 * (productionSpeed / 100) * (Math.random() * 0.2 + 0.9));document.getElementById('current-output').textContent = hourlyRate.toLocaleString() + ' 件/小时';// 更新各工站状态updateStationStatus();// 模拟产品质量数据const qualityRate = (99.5 + Math.random() * 0.5).toFixed(1);document.getElementById('quality-rate').textContent = qualityRate + '%';const qualityTrend = Math.random() > 0.7 ? -0.1 : 0.2;const trendEl = document.getElementById('quality-rate').nextElementSibling;trendEl.textContent = (qualityTrend >= 0 ? '↑' : '↓') + Math.abs(qualityTrend).toFixed(1) + '%';trendEl.className = 'card-trend ' + (qualityTrend >= 0 ? 'positive' : 'negative');// 模拟设备效率数据const efficiency = (94.0 + Math.random() * 1.0).toFixed(1);document.getElementById('equipment-efficiency').textContent = efficiency + '%';const efficiencyTrend = Math.random() > 0.3 ? 1.5 : -0.8;const effTrendEl = document.getElementById('equipment-efficiency').nextElementSibling;effTrendEl.textContent = (efficiencyTrend >= 0 ? '↑' : '↓') + Math.abs(efficiencyTrend).toFixed(1) + '%';effTrendEl.className = 'card-trend ' + (efficiencyTrend >= 0 ? 'positive' : 'negative');// 更新总产量if (Math.random() > 0.7) {const increment = Math.floor(Math.random() * 20 + 10);totalProduction += increment;document.getElementById('total-output').textContent = totalProduction.toLocaleString();}// 检查是否需要产生告警checkAlarms();
}/*** 更新各工站状态*/
function updateStationStatus() {if (!systemRunning) return;// 随机选择一个工站可能出现临时警告if (Math.random() < 0.05) { // 5%概率出现警告const stationId = 'station-' + Math.ceil(Math.random() * 6);if (stationStatus[stationId] === 'active') {stationStatus[stationId] = 'warning';document.getElementById(stationId + '-status').className = 'station-status warning';// 添加警告信息const stationName = document.querySelector('#' + stationId + ' .station-label').textContent;addAlarm('warning', stationName + '状态异常', '检测到性能波动,请注意监控');// 2-5秒后自动恢复setTimeout(() => {if (systemRunning && stationStatus[stationId] === 'warning') {stationStatus[stationId] = 'active';document.getElementById(stationId + '-status').className = 'station-status active';}}, 2000 + Math.random() * 3000);}}
}/*** 更新流动动画*/
function updateFlowAnimation() {if (!systemRunning) return;// 更新流动状态动画Object.keys(flowStatus).forEach(flow => {if (flowStatus[flow]) {const el = document.getElementById(flow);// 切换动画状态来重启动画el.classList.remove('active');setTimeout(() => {if (systemRunning && flowStatus[flow]) {el.classList.add('active');}}, 50);}});
}/*** 更新系统时间*/
function updateSystemTime() {const now = new Date();const timeStr = now.toLocaleString('zh-CN', {year: 'numeric',month: '2-digit',day: '2-digit',hour: '2-digit',minute: '2-digit',second: '2-digit',hour12: false});document.getElementById('system-time').textContent = timeStr;setTimeout(updateSystemTime, 1000);
}/*** 更新运行时间*/
function updateRunningTime() {if (!systemRunning) return;runningTime++;const hours = Math.floor(runningTime / 3600).toString().padStart(2, '0');const minutes = Math.floor((runningTime % 3600) / 60).toString().padStart(2, '0');const seconds = (runningTime % 60).toString().padStart(2, '0');document.getElementById('running-time').textContent = `${hours}:${minutes}:${seconds}`;
}/*** 更新系统指标数据*/
function updateSystemMetrics() {if (!systemRunning) return;// 更新CPU负载const cpuLoad = Math.floor(20 + Math.random() * 25);document.getElementById('cpu-load').textContent = cpuLoad + '%';// 更新内存使用const memoryUsage = (1.0 + Math.random() * 0.5).toFixed(1);document.getElementById('memory-usage').textContent = memoryUsage + 'GB/4GB';// 更新通信状态if (Math.random() < 0.02) { // 2%概率通信状态变化const commStatus = document.getElementById('comm-status');if (commStatus.textContent === '已连接') {commStatus.textContent = '重连中...';commStatus.className = 'metric-value disconnected';// 添加通信中断告警addAlarm('warning', '通信状态异常', '系统正在尝试重新建立连接');// 1-3秒后恢复setTimeout(() => {if (systemRunning) {commStatus.textContent = '已连接';commStatus.className = 'metric-value connected';addAlarm('info', '通信已恢复', '系统通信连接已重新建立');}}, 1000 + Math.random() * 2000);}}
}/*** 更新产量数据(模拟数据变化)*/
function updateProductionData() {if (!systemRunning) return;// 移除第一个时间点数据productionData.labels.shift();productionData.actual.shift();productionData.target.shift();productionData.defects.shift();// 添加新的时间点数据const lastTime = productionData.labels[productionData.labels.length - 1];const [hour, minute] = lastTime.split(':').map(Number);let newHour = hour;let newMinute = minute + 30;if (newMinute >= 60) {newMinute = 0;newHour += 1;}const newTimeStr = `${newHour}:${newMinute === 0 ? '00' : '30'}`;productionData.labels.push(newTimeStr);// 添加新的产量数据const lastTarget = productionData.target[productionData.target.length - 1];const newTarget = lastTarget + (Math.random() > 0.7 ? 100 : 0);const baseProduction = newTarget * (productionSpeed / 100);const newProduction = Math.floor(baseProduction * (0.9 + Math.random() * 0.2));const newDefect = Math.max(0.1, Math.min(3.0, productionData.defects[productionData.defects.length - 1] + (Math.random() - 0.5))).toFixed(1);productionData.target.push(newTarget);productionData.actual.push(newProduction);productionData.defects.push(parseFloat(newDefect));
}/*** 检查是否产生告警*/
function checkAlarms() {if (!systemRunning) return;// 随机触发告警(低概率)if (Math.random() < 0.04) { // 4%概率产生新告警const alarmTypes = [{ level: 'info', title: '批次变更通知', desc: '系统已自动更新至新批次:' + batchNumber },{ level: 'info', title: '日常维护提醒', desc: '设备1号传送带已运行720小时,建议进行日常维护检查' },{ level: 'warning', title: '质量波动', desc: '最近30分钟内包装质量合格率出现轻微下降' },{ level: 'warning', title: '能耗异常', desc: '3号包装工位能耗高于正常值15%,建议检查' },{ level: 'warning', title: '备料不足', desc: '备料区原材料库存低于预警阈值,请及时补充' },{ level: 'critical', title: '设备故障', desc: '标签打印机5分钟内多次出现卡纸现象,需要检修' },{ level: 'critical', title: '温度超限', desc: '热封区温度超出工艺范围,当前值: 185°C (标准:160±10°C)' }];const alarm = alarmTypes[Math.floor(Math.random() * alarmTypes.length)];addAlarm(alarm.level, alarm.title, alarm.desc);}
}/*** 添加新告警*/
function addAlarm(level, title, description) {// 创建新告警const alarm = {id: Date.now(),level: level,title: title,description: description,time: new Date(),acknowledged: false};// 添加到告警列表alarms.unshift(alarm);// 限制告警列表最大长度if (alarms.length > 50) {alarms.pop();}// 更新告警计数updateAlarmCount();// 更新当前告警信息document.getElementById('current-alarm').textContent = title;// 更新告警图标状态const alarmIcon = document.getElementById('alarm-icon');alarmIcon.classList.add('active');// 如果是严重告警,弹出告警窗口if (level === 'critical') {showAlarmModal();}// 更新告警列表(如果弹窗已显示)if (document.querySelector('.alarm-modal.show')) {updateAlarmList();}
}/*** 更新告警计数*/
function updateAlarmCount() {// 统计未确认告警数量const unacknowledgedCount = alarms.filter(alarm => !alarm.acknowledged).length;alarmCount = unacknowledgedCount;// 更新告警计数显示document.getElementById('alarm-count').textContent = alarmCount;// 更新告警图标状态const alarmIcon = document.getElementById('alarm-icon');if (alarmCount > 0) {alarmIcon.classList.add('active');} else {alarmIcon.classList.remove('active');}
}/*** 显示告警弹窗*/
function showAlarmModal() {// 更新告警列表updateAlarmList();// 显示弹窗const alarmModal = document.getElementById('alarm-modal');alarmModal.classList.add('show');
}/*** 隐藏告警弹窗*/
function hideAlarmModal() {const alarmModal = document.getElementById('alarm-modal');alarmModal.classList.remove('show');
}/*** 更新告警列表*/
function updateAlarmList() {const alarmList = document.getElementById('alarm-list');alarmList.innerHTML = '';if (alarms.length === 0) {const emptyMessage = document.createElement('div');emptyMessage.className = 'empty-alarm';emptyMessage.textContent = '无告警信息';alarmList.appendChild(emptyMessage);return;}// 创建告警列表项alarms.forEach(alarm => {const alarmItem = document.createElement('div');alarmItem.className = `alarm-item ${alarm.level} ${alarm.acknowledged ? 'acknowledged' : ''}`;alarmItem.dataset.id = alarm.id;// 告警时间const time = document.createElement('div');time.className = 'alarm-time';time.textContent = formatTime(alarm.time);// 告警图标const icon = document.createElement('div');icon.className = 'alarm-icon';icon.innerHTML = alarm.level === 'critical' ? '⚠' : (alarm.level === 'warning' ? '⚠' : 'ℹ');// 告警内容const content = document.createElement('div');content.className = 'alarm-content';const title = document.createElement('div');title.className = 'alarm-title';title.textContent = alarm.title;const description = document.createElement('div');description.className = 'alarm-description';description.textContent = alarm.description;content.appendChild(title);content.appendChild(description);// 确认按钮const ackBtn = document.createElement('button');ackBtn.className = 'alarm-ack-btn';ackBtn.textContent = '确认';ackBtn.addEventListener('click', () => acknowledgeAlarm(alarm.id));if (!alarm.acknowledged) {alarmItem.appendChild(icon);alarmItem.appendChild(time);alarmItem.appendChild(content);alarmItem.appendChild(ackBtn);} else {alarmItem.appendChild(icon);alarmItem.appendChild(time);alarmItem.appendChild(content);const ackMark = document.createElement('div');ackMark.className = 'alarm-ack-mark';ackMark.textContent = '已确认';alarmItem.appendChild(ackMark);}alarmList.appendChild(alarmItem);});
}/*** 确认单个告警*/
function acknowledgeAlarm(id) {const alarm = alarms.find(a => a.id === id);if (alarm) {alarm.acknowledged = true;// 更新告警计数updateAlarmCount();// 更新告警列表updateAlarmList();}
}/*** 确认所有告警*/
function acknowledgeAllAlarms() {alarms.forEach(alarm => {alarm.acknowledged = true;});// 更新告警计数updateAlarmCount();// 更新告警列表updateAlarmList();// 如果没有未确认的告警,自动关闭弹窗setTimeout(hideAlarmModal, 500);
}/*** 格式化时间显示*/
function formatTime(date) {const hours = date.getHours().toString().padStart(2, '0');const minutes = date.getMinutes().toString().padStart(2, '0');const seconds = date.getSeconds().toString().padStart(2, '0');return `${hours}:${minutes}:${seconds}`;
}/*** 切换逻辑编程区域的Tab*/
function switchProgrammingTab(event) {// 获取当前点击的Tabconst clickedTab = event.currentTarget;// 获取目标Tab的IDconst targetTabId = clickedTab.dataset.tab;// 移除所有Tab的active类document.querySelectorAll('.tab').forEach(tab => {tab.classList.remove('active');});// 隐藏所有Tab内容document.querySelectorAll('.tab-pane').forEach(pane => {pane.classList.remove('active');});// 添加当前Tab的active类clickedTab.classList.add('active');// 显示对应的Tab内容document.getElementById(targetTabId + '-pane').classList.add('active');
}/*** 初始化逻辑编程界面*/
function initializeLogicProgramming() {// 设置Canvas的拖放区域setupDragAndDrop();// 初始化代码编辑器initializeCodeEditor();// 初始化时序图updateSequenceDiagram();
}/*** 设置拖放功能*/
function setupDragAndDrop() {const logicCanvas = document.getElementById('logic-canvas');// 添加提示文本const placeholderText = document.createElement('div');placeholderText.className = 'placeholder-text';placeholderText.textContent = '拖放控制元件到此区域创建逻辑流程';// 初始化工作流容器const workflowContainer = document.getElementById('workflow-container');if (!workflowContainer) {const container = document.createElement('div');container.id = 'workflow-container';container.className = 'workflow-container';logicCanvas.appendChild(container);}
}/*** 处理拖动开始事件*/
function handleDragStart(event) {// 存储被拖动元素的类型event.dataTransfer.setData('type', event.target.dataset.type);event.dataTransfer.effectAllowed = 'copy';
}/*** 处理拖动经过事件*/
function handleDragOver(event) {event.preventDefault();event.dataTransfer.dropEffect = 'copy';// 添加拖动经过的视觉效果event.currentTarget.classList.add('drag-over');
}/*** 处理拖放事件*/
function handleDrop(event) {event.preventDefault();// 移除拖动经过的视觉效果event.currentTarget.classList.remove('drag-over');// 获取被拖动元素的类型const type = event.dataTransfer.getData('type');// 获取放置位置const canvasRect = event.currentTarget.getBoundingClientRect();const x = event.clientX - canvasRect.left;const y = event.clientY - canvasRect.top;// 创建新的逻辑元素createLogicElement(type, x, y);
}/*** 创建新的逻辑元素*/
function createLogicElement(type, x, y) {// 元素配置const elementConfig = {start: { shape: 'circle', label: '开始', color: '#4CAF50' },decision: { shape: 'diamond', label: '判断', color: '#FF9800' },action: { shape: 'rect', label: '动作', color: '#2196F3' },delay: { shape: 'rect', label: '延时', color: '#9C27B0' },parallel: { shape: 'rect', label: '并行', color: '#00BCD4' },end: { shape: 'circle', label: '结束', color: '#F44336' },feeding: { shape: 'rect', label: '上料', color: '#2196F3' },weighing: { shape: 'rect', label: '称重', color: '#4CAF50' },packaging: { shape: 'rect', label: '包装', color: '#FF9800' },labeling: { shape: 'rect', label: '贴标', color: '#9C27B0' },inspection: { shape: 'rect', label: '检测', color: '#00BCD4' },palletizing: { shape: 'rect', label: '码垛', color: '#795548' }};const config = elementConfig[type] || { shape: 'rect', label: type, color: '#607D8B' };// 创建新元素const id = 'element-' + Date.now();const element = {id: id,type: type,label: config.label,shape: config.shape,color: config.color,x: x,y: y,connections: []};// 添加到元素列表workflowElements.push(element);// 渲染新元素renderLogicElement(element);
}/*** 渲染逻辑元素*/
function renderLogicElement(element) {const container = document.getElementById('workflow-container');// 创建元素DOMconst el = document.createElement('div');el.id = element.id;el.className = `workflow-element ${element.shape} ${element.type}`;el.style.left = element.x + 'px';el.style.top = element.y + 'px';el.style.backgroundColor = element.color;// 添加标签const label = document.createElement('div');label.className = 'element-label';label.textContent = element.label;el.appendChild(label);// 添加连接点const connectionPoints = document.createElement('div');connectionPoints.className = 'connection-points';['top', 'right', 'bottom', 'left'].forEach(position => {const point = document.createElement('div');point.className = `connection-point ${position}`;point.dataset.position = position;point.dataset.elementId = element.id;// 添加连接点的事件处理point.addEventListener('mousedown', startConnection);connectionPoints.appendChild(point);});el.appendChild(connectionPoints);// 添加拖动功能el.draggable = true;el.addEventListener('dragstart', handleElementDragStart);el.addEventListener('dragend', handleElementDragEnd);// 添加双击编辑功能el.addEventListener('dblclick', editElement);// 添加到容器container.appendChild(el);
}/*** 开始创建连接*/
function startConnection(event) {// 这里实现连接创建逻辑console.log('开始创建连接点,从元素:', event.target.dataset.elementId);
}/*** 处理元素拖动开始*/
function handleElementDragStart(event) {// 保存当前位置信息const elementId = event.target.id;const element = workflowElements.find(el => el.id === elementId);if (element) {event.dataTransfer.setData('elementId', elementId);// 设置拖动时的视觉效果event.target.classList.add('dragging');}
}/*** 处理元素拖动结束*/
function handleElementDragEnd(event) {// 移除拖动时的视觉效果event.target.classList.remove('dragging');
}/*** 编辑元素*/
function editElement(event) {const elementId = event.currentTarget.id;const element = workflowElements.find(el => el.id === elementId);if (element) {// 这里可以实现弹出编辑对话框等功能console.log('编辑元素:', element);}
}/*** 创建新的逻辑流程*/
function createNewLogic() {// 清空工作区const container = document.getElementById('workflow-container');container.innerHTML = '';// 清空元素列表workflowElements = [];console.log('创建新的逻辑流程');
}/*** 保存当前逻辑流程*/
function saveLogic() {// 这里可以实现保存逻辑,如导出JSON等console.log('保存逻辑流程:', workflowElements);
}/*** 部署逻辑流程*/
function deployLogic() {// 这里可以实现部署逻辑console.log('部署逻辑流程');// 显示部署成功消息addAlarm('info', '逻辑流程已部署', '新的控制逻辑已成功部署到系统');
}/*** 初始化代码编辑器*/
function initializeCodeEditor() {// 这里可以添加代码编辑器的高亮和自动完成等功能console.log('初始化代码编辑器');
}/*** 检查代码语法*/
function checkCodeSyntax() {const code = document.getElementById('code-content').textContent;// 这里可以实现语法检查逻辑console.log('检查代码语法');// 模拟语法检查结果const hasErrors = Math.random() < 0.3;if (hasErrors) {addAlarm('warning', '语法检查发现问题', '代码第25行可能存在语法错误,请检查');} else {addAlarm('info', '语法检查通过', '代码语法检查未发现问题');}
}/*** 格式化代码*/
function formatCode() {// 这里可以实现代码格式化逻辑console.log('格式化代码');// 添加操作提示addAlarm('info', '代码已格式化', '代码格式化操作已完成');
}/*** 更新时序图*/
function updateSequenceDiagram() {const selectedStation = document.getElementById('station-selector').value;const diagram = document.getElementById('sequence-diagram');// 清空时序图区域diagram.innerHTML = '';if (selectedStation === 'all') {renderFullSequenceDiagram(diagram);} else {renderStationSequenceDiagram(diagram, selectedStation);}
}/*** 渲染完整流程时序图*/
function renderFullSequenceDiagram(container) {// 这里可以实现完整流程时序图的渲染console.log('渲染完整流程时序图');// 示例简单时序图const svgNS = "http://www.w3.org/2000/svg";const width = container.clientWidth;const height = 300;const svg = document.createElementNS(svgNS, "svg");svg.setAttribute("width", width);svg.setAttribute("height", height);svg.style.backgroundColor = "var(--primary-dark)";// 绘制简单的时序线const stations = ['控制系统', '上料站', '称重站', '包装站', '贴标站', '检测站', '码垛站'];const xStep = width / (stations.length + 1);// 绘制垂直生命线stations.forEach((station, i) => {const x = (i + 1) * xStep;// 绘制站点标签const text = document.createElementNS(svgNS, "text");text.setAttribute("x", x);text.setAttribute("y", 20);text.setAttribute("text-anchor", "middle");text.setAttribute("fill", "var(--text-primary)");text.setAttribute("font-size", "12");text.textContent = station;svg.appendChild(text);// 绘制垂直线const line = document.createElementNS(svgNS, "line");line.setAttribute("x1", x);line.setAttribute("y1", 30);line.setAttribute("x2", x);line.setAttribute("y2", height - 10);line.setAttribute("stroke", "var(--border-color)");line.setAttribute("stroke-dasharray", "5,5");svg.appendChild(line);});// 绘制消息箭头const messages = [{ from: 0, to: 1, label: '启动指令', y: 60 },{ from: 1, to: 2, label: '物料传送', y: 90 },{ from: 2, to: 3, label: '重量数据', y: 120 },{ from: 3, to: 4, label: '包装完成', y: 150 },{ from: 4, to: 5, label: '贴标完成', y: 180 },{ from: 5, to: 6, label: '检测结果', y: 210 },{ from: 6, to: 0, label: '流程完成', y: 240 }];messages.forEach(msg => {const x1 = (msg.from + 1) * xStep;const x2 = (msg.to + 1) * xStep;const y = msg.y;// 绘制箭头线const arrow = document.createElementNS(svgNS, "line");arrow.setAttribute("x1", x1);arrow.setAttribute("y1", y);arrow.setAttribute("x2", x2);arrow.setAttribute("y2", y);arrow.setAttribute("stroke", "#4CAF50");arrow.setAttribute("stroke-width", "1.5");arrow.setAttribute("marker-end", "url(#arrow)");svg.appendChild(arrow);// 绘制消息标签const text = document.createElementNS(svgNS, "text");text.setAttribute("x", (x1 + x2) / 2);text.setAttribute("y", y - 5);text.setAttribute("text-anchor", "middle");text.setAttribute("fill", "var(--text-secondary)");text.setAttribute("font-size", "10");text.textContent = msg.label;svg.appendChild(text);});// 添加箭头标记定义const defs = document.createElementNS(svgNS, "defs");const marker = document.createElementNS(svgNS, "marker");marker.setAttribute("id", "arrow");marker.setAttribute("viewBox", "0 0 10 10");marker.setAttribute("refX", "9");marker.setAttribute("refY", "5");marker.setAttribute("markerWidth", "6");marker.setAttribute("markerHeight", "6");marker.setAttribute("orient", "auto");const path = document.createElementNS(svgNS, "path");path.setAttribute("d", "M 0 0 L 10 5 L 0 10 z");path.setAttribute("fill", "#4CAF50");marker.appendChild(path);defs.appendChild(marker);svg.appendChild(defs);container.appendChild(svg);
}/*** 渲染单个工站的时序图*/
function renderStationSequenceDiagram(container, stationId) {// 这里可以实现单个工站的时序图渲染console.log('渲染工站时序图:', stationId);// 添加提示信息const info = document.createElement('div');info.className = 'station-sequence-info';info.textContent = '显示 ' + document.querySelector('#' + stationId + ' .station-label').textContent + ' 的详细时序操作';container.appendChild(info);
}// 页面加载完成后初始化系统
document.addEventListener('DOMContentLoaded', initializeSystem); 

相关文章:

  • 162558-52-3,MCLA的化学发光是一种的超氧化物检测手段
  • Hadoop客户端环境准备
  • 【大模型面试每日一题】Day 11:参数高效微调方法(如LoRA、Adapter)的核心思想是什么?相比全参数微调有何优缺点?
  • OpenCV 中用于背景分割的一个类cv::bgsegm::BackgroundSubtractorGMG
  • FFmpeg(7.1版本)编译生成ffplay
  • Linux 系统命令使用指南1
  • 项目文档归档的最佳实践有哪些?
  • 数字电子技术基础(五十五)——D触发器
  • 微信小程序预览文件 兼容性苹果
  • 【C++】类和对象(下)
  • 桥接模式(Bridge)
  • ubuntu nobel + qt5.15.2 设置qss语法识别正确
  • 应用 | AI 自动化某讯会议转录与摘要生成系统
  • 使用英伟达 Riva 和 OpenAI 构建 AI 聊天机器人
  • 游戏引擎学习第263天:添加调试帧滑块
  • 数据报(Datagram)与虚电路(Virtual Circuit)的区别
  • 无线局域网专题 | 第十一章 | AC+AP配置
  • 【Java学习笔记】封装
  • C# 语言介绍
  • 实变函数 第四章 可测函数
  • 巴基斯坦关闭全部领空
  • 网络主播直播泄机密,别让这些“小事”成威胁国家安全的“突破口”
  • 85后清华博士黄佐财任湖北咸宁市咸安区委副书记、代区长
  • Meta正为AI眼镜开发人脸识别功能
  • A股26家游戏企业去年营收近1900亿元:过半净利下滑,出海成为主流选择
  • 新消费观察| 零售品牌 “走出去” ,如何开辟“新蓝海”?