5、开放式PLC梯形图编程组件 - /自动化与控制组件/open-plc-programming

76个工业组件库示例汇总

开放式PLC编程环境

这是一个开放式PLC编程环境的自定义组件,提供了一个面向智能仓储堆垛机控制的开放式PLC编程环境。该组件采用苹果科技风格设计,支持多厂商PLC硬件,具有直观的界面和丰富的功能。

功能特点

  1. 多语言编程支持:梯形图(LD)、结构化文本(ST)和功能块图(FBD)三种PLC编程语言
  2. 多厂商硬件兼容:支持西门子、AB、三菱、欧姆龙和施耐德等主流PLC厂商
  3. 苹果科技风格界面:简洁美观的UI设计,符合现代工业审美
  4. 专业编程工具:集成Monaco编辑器,提供代码高亮、自动完成和错误检查等功能
  5. 实时变量监控:在线查看和跟踪PLC变量状态变化
  6. 堆垛机可视化:直观展示堆垛机运行状态和位置信息
  7. 告警管理系统:实时显示系统告警信息和处理状态
  8. 自适应布局:响应式设计,适应不同屏幕尺寸
  9. 动态交互效果:流畅的动画效果,提升用户体验

界面区域说明

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

  1. 顶部工具栏:包含编程语言选择、PLC厂商选择和文件操作选项
  2. 编程区域
    • 梯形图编辑器:可视化梯形图编程环境
    • 文本编辑器:用于ST语言和FBD编程
    • 标签页管理:多程序文件的标签页切换
  3. 变量监控区
    • 变量列表:显示和筛选当前PLC变量
    • 实时值更新:动态显示变量的当前值
  4. 堆垛机状态区
    • 可视化动画:显示堆垛机位置和动作
    • 实时指标:当前速度、位置和载重等关键参数
  5. 告警信息区
    • 告警显示:实时系统告警和错误信息
    • 告警处理:告警确认和处理功能

连接实际硬件

要将组件连接到实际的PLC硬件,请按照以下步骤操作:

  1. 点击顶部菜单按钮,打开硬件配置对话框
  2. 选择相应的PLC型号和通信参数
  3. 配置I/O模块和通信地址
  4. 保存配置后重新连接

组件默认使用模拟数据。要连接实际硬件,需要修改script.js中的initVariableSimulation函数,实现与实际PLC的通信。

编程示例

组件内置了几个堆垛机控制的编程示例:

  1. 主程序:主控制循环和基本功能
  2. 子程序1:堆垛机位置控制逻辑
  3. 配置:系统参数和硬件配置

这些示例可以作为开发实际应用程序的起点。

自定义选项

您可以通过修改组件代码来自定义以下内容:

  • 界面主题:在CSS中修改颜色变量,调整组件的视觉风格
  • 编程功能:添加新的编程工具或语言支持
  • 变量监控:调整变量的显示方式和更新频率
  • 堆垛机动画:根据实际设备特性调整可视化效果
  • 告警规则:定制告警触发条件和处理流程

浏览器兼容性

该组件使用了现代Web技术,建议在以下浏览器版本中使用:

  • Chrome 60+
  • Firefox 55+
  • Safari 11+
  • Edge 16+

注意事项

  • 组件默认使用模拟数据,实际应用时需要连接到真实的PLC数据源
  • 为保证最佳性能,请在实际部署环境中优化数据刷新频率
  • 使用前请确认所选PLC厂商的通信驱动是否可用

项目结构

在这里插入图片描述

效果展示

在这里插入图片描述

源码

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>开放式PLC编程环境</title><link rel="stylesheet" href="styles.css"><!-- 添加Monaco编辑器 --><script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/loader.js"></script>
</head>
<body><div id="plc-programming-environment"><header class="ppe-header"><div class="ppe-logo"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21 7V17C21 19.2091 19.2091 21 17 21H7C4.79086 21 3 19.2091 3 17V7C3 4.79086 4.79086 3 7 3H17C19.2091 3 21 4.79086 21 7Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M7 9H10M7 12H13M7 15H10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M15 9L17 9M15 12L17 12M15 15L17 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg><span>智能仓储堆垛机控制</span></div><div class="ppe-actions"><button class="ppe-btn ppe-btn-primary" id="deploy-btn"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg>部署程序</button><button class="ppe-btn" id="menu-btn"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 18H21M3 12H21M3 6H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>菜单</button></div></header><div class="ppe-content"><!-- 工具栏 --><div class="ppe-toolbar"><div class="ppe-toolbar-group"><button class="ppe-tool-btn active" data-mode="ladder"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 5v14M20 5v14M4 12h16M8 5v7M16 12v7"></path></svg>梯形图</button><button class="ppe-tool-btn" data-mode="st"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3z"></path></svg>ST语言</button><button class="ppe-tool-btn" data-mode="fbd"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="8" width="6" height="8" rx="2"></rect><rect x="15" y="8" width="6" height="8" rx="2"></rect><path d="M9 12h6"></path></svg>功能块</button></div><div class="ppe-toolbar-group"><select id="plc-vendor" class="ppe-select"><option value="siemens">西门子S7</option><option value="ab">AB CompactLogix</option><option value="mitsubishi">三菱FX5U</option><option value="omron">欧姆龙NX</option><option value="schneider">施耐德M340</option></select></div><div class="ppe-toolbar-group"><button class="ppe-tool-btn" id="save-btn"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></svg>保存</button><button class="ppe-tool-btn" id="download-btn"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>下载</button></div></div><!-- 主面板区域 --><div class="ppe-main-panels"><!-- 编程区域 --><div class="ppe-panel ppe-editor-panel"><div class="ppe-panel-header"><h2>程序编辑区</h2><div class="ppe-panel-actions"><button class="ppe-btn ppe-btn-sm" id="verify-btn">验证</button><button class="ppe-btn ppe-btn-sm" id="format-btn">格式化</button></div></div><div class="ppe-panel-body"><div class="ppe-tabs"><div class="ppe-tab active" data-tab="main">主程序</div><div class="ppe-tab" data-tab="subr1">子程序1</div><div class="ppe-tab" data-tab="config">配置</div><div class="ppe-tab-add">+</div></div><div class="ppe-editor-container"><div id="monaco-editor" class="ppe-editor-area"></div><div class="ppe-ladder-editor" style="display: none;"><div class="ppe-ladder-toolbar"><button class="ppe-ladder-btn" data-element="contact-no">常开触点</button><button class="ppe-ladder-btn" data-element="contact-nc">常闭触点</button><button class="ppe-ladder-btn" data-element="coil">线圈</button><button class="ppe-ladder-btn" data-element="timer">定时器</button><button class="ppe-ladder-btn" data-element="counter">计数器</button></div><div class="ppe-ladder-grid" id="ladder-grid"><!-- 梯形图网格区域 --></div></div></div></div></div><!-- 右侧面板 --><div class="ppe-side-panels"><!-- 变量监控面板 --><div class="ppe-panel ppe-variables-panel"><div class="ppe-panel-header"><h2>变量监控</h2><div class="ppe-panel-actions"><button class="ppe-btn ppe-btn-sm" id="refresh-vars-btn">刷新</button></div></div><div class="ppe-panel-body"><div class="ppe-search-bar"><input type="text" placeholder="搜索变量..." class="ppe-search-input"></div><div class="ppe-variables-list"><div class="ppe-variable-item"><div class="ppe-variable-name">S_StartButton</div><div class="ppe-variable-type">BOOL</div><div class="ppe-variable-value">TRUE</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">S_StopButton</div><div class="ppe-variable-type">BOOL</div><div class="ppe-variable-value">FALSE</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">Position_X</div><div class="ppe-variable-type">REAL</div><div class="ppe-variable-value">145.32</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">Position_Y</div><div class="ppe-variable-type">REAL</div><div class="ppe-variable-value">87.65</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">Position_Z</div><div class="ppe-variable-type">REAL</div><div class="ppe-variable-value">22.41</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">Speed_X</div><div class="ppe-variable-type">REAL</div><div class="ppe-variable-value">0.75</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">CurrentShelf</div><div class="ppe-variable-type">INT</div><div class="ppe-variable-value">12</div></div><div class="ppe-variable-item"><div class="ppe-variable-name">TargetShelf</div><div class="ppe-variable-type">INT</div><div class="ppe-variable-value">18</div></div></div></div></div><!-- 堆垛机监控面板 --><div class="ppe-panel ppe-stacker-panel"><div class="ppe-panel-header"><h2>堆垛机状态</h2><span class="ppe-status-badge status-normal">运行中</span></div><div class="ppe-panel-body"><div class="ppe-stacker-animation"><div class="ppe-storage-rack"><!-- 储物架动画区域 --></div><div class="ppe-stacker" id="stacker-animation"><!-- 堆垛机动画 --></div></div><div class="ppe-stacker-metrics"><div class="ppe-metric"><div class="ppe-metric-value">5.4 m/s</div><div class="ppe-metric-label">当前速度</div></div><div class="ppe-metric"><div class="ppe-metric-value">B12-34</div><div class="ppe-metric-label">当前位置</div></div><div class="ppe-metric"><div class="ppe-metric-value">45 kg</div><div class="ppe-metric-label">载重</div></div></div></div></div></div></div><!-- 底部告警区域 --><div class="ppe-panel ppe-alerts-panel"><div class="ppe-panel-header"><h2>系统告警</h2><div class="ppe-badge">2</div></div><div class="ppe-panel-body"><div id="alerts-list" class="ppe-alerts-list"><div class="ppe-alert-item alert-warning"><div class="ppe-alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg></div><div class="ppe-alert-content"><div class="ppe-alert-message">堆垛机X轴接近极限位置,请检查程序逻辑</div><div class="ppe-alert-time">10:45:21</div></div><div class="ppe-alert-actions"><button class="ppe-alert-btn">查看</button></div></div><div class="ppe-alert-item alert-error"><div class="ppe-alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg></div><div class="ppe-alert-content"><div class="ppe-alert-message">PLC通信中断,请检查网络连接</div><div class="ppe-alert-time">10:42:53</div></div><div class="ppe-alert-actions"><button class="ppe-alert-btn">查看</button></div></div></div></div></div></div><!-- 模态框 - 硬件配置 --><div id="hardware-config-modal" class="ppe-modal"><div class="ppe-modal-content"><div class="ppe-modal-header"><h3>PLC硬件配置</h3><button class="ppe-modal-close">&times;</button></div><div class="ppe-modal-body"><div class="ppe-form-group"><label>PLC型号</label><select class="ppe-select"><option>西门子 S7-1200</option><option>西门子 S7-1500</option><option>AB CompactLogix 5380</option><option>AB ControlLogix 5580</option><option>三菱 FX5U-32M</option></select></div><div class="ppe-form-group"><label>IP地址</label><input type="text" class="ppe-input" value="192.168.1.100"></div><div class="ppe-form-group"><label>通信端口</label><input type="number" class="ppe-input" value="102"></div><div class="ppe-form-group"><label>刷新率 (ms)</label><input type="number" class="ppe-input" value="100"></div><div class="ppe-hardware-modules"><h4>I/O模块</h4><div class="ppe-module-list"><div class="ppe-module-item"><div class="ppe-module-header"><span>数字量输入模块 DI16</span><span>Slot 1</span></div><div class="ppe-module-body">16通道,24V DC</div></div><div class="ppe-module-item"><div class="ppe-module-header"><span>数字量输出模块 DO16</span><span>Slot 2</span></div><div class="ppe-module-body">16通道,继电器输出</div></div><div class="ppe-module-item"><div class="ppe-module-header"><span>模拟量输入模块 AI8</span><span>Slot 3</span></div><div class="ppe-module-body">8通道,±10V/4-20mA</div></div><div class="ppe-module-item"><div class="ppe-module-header"><span>模拟量输出模块 AO4</span><span>Slot 4</span></div><div class="ppe-module-body">4通道,±10V/4-20mA</div></div></div><button class="ppe-btn ppe-btn-sm ppe-btn-add">添加模块</button></div></div><div class="ppe-modal-footer"><button class="ppe-btn ppe-btn-secondary modal-close-btn">取消</button><button class="ppe-btn ppe-btn-primary">保存配置</button></div></div></div></div><script src="script.js"></script>
</body>
</html> 

styles.css

/* 开放式PLC编程环境 - 苹果科技风格 */:root {/* 颜色变量 - 苹果风格 */--background: #F5F5F7;--card-bg: #FFFFFF;--primary-text: #1D1D1F;--secondary-text: #86868B;--accent-blue: #0066CC;--accent-green: #34C759;--accent-orange: #FF9500;--accent-red: #FF3B30;--accent-purple: #5E5CE6;--border-color: #D2D2D7;--grid-color: #E8E8ED;/* 阴影 */--card-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);--header-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);/* 尺寸变量 */--header-height: 60px;--toolbar-height: 48px;--panels-gap: 16px;--border-radius: 10px;--input-height: 32px;
}/* 基础样式 */
#plc-programming-environment {font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', sans-serif;color: var(--primary-text);background-color: var(--background);display: flex;flex-direction: column;height: 100%;width: 100%;position: relative;box-sizing: border-box;margin: 0;padding: 0;overflow: hidden;
}#plc-programming-environment * {box-sizing: border-box;
}/* 顶部导航栏 */
.ppe-header {height: var(--header-height);background-color: var(--card-bg);border-bottom: 1px solid var(--border-color);display: flex;justify-content: space-between;align-items: center;padding: 0 20px;box-shadow: var(--header-shadow);z-index: 10;
}.ppe-logo {display: flex;align-items: center;gap: 10px;font-weight: 500;
}.ppe-logo svg {color: var(--accent-blue);
}.ppe-actions {display: flex;gap: 12px;
}/* 主内容区域 */
.ppe-content {display: flex;flex-direction: column;flex: 1;height: calc(100% - var(--header-height));overflow: hidden;
}/* 工具栏 */
.ppe-toolbar {height: var(--toolbar-height);background-color: var(--card-bg);border-bottom: 1px solid var(--border-color);display: flex;align-items: center;padding: 0 16px;gap: 20px;
}.ppe-toolbar-group {display: flex;align-items: center;gap: 8px;
}.ppe-toolbar-group:not(:last-child) {padding-right: 20px;border-right: 1px solid var(--border-color);
}.ppe-tool-btn {display: flex;align-items: center;gap: 6px;padding: 5px 10px;border-radius: 6px;border: none;background-color: transparent;color: var(--primary-text);font-size: 13px;cursor: pointer;transition: all 0.2s;
}.ppe-tool-btn:hover {background-color: rgba(0, 0, 0, 0.05);
}.ppe-tool-btn.active {background-color: rgba(0, 102, 204, 0.1);color: var(--accent-blue);
}.ppe-tool-btn svg {color: var(--secondary-text);
}.ppe-tool-btn.active svg {color: var(--accent-blue);
}/* 选择器样式 */
.ppe-select {height: var(--input-height);padding: 0 12px;border-radius: 6px;border: 1px solid var(--border-color);background-color: var(--card-bg);color: var(--primary-text);font-size: 13px;outline: none;appearance: none;background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2386868B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");background-repeat: no-repeat;background-position: right 12px center;padding-right: 32px;
}.ppe-select:hover {border-color: var(--secondary-text);
}.ppe-select:focus {border-color: var(--accent-blue);box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}/* 主面板区域 - 使用Grid布局 */
.ppe-main-panels {display: grid;grid-template-columns: 1fr 320px;grid-gap: var(--panels-gap);padding: var(--panels-gap);flex: 1;overflow: hidden;
}/* 面板样式 */
.ppe-panel {background-color: var(--card-bg);border-radius: var(--border-radius);box-shadow: var(--card-shadow);display: flex;flex-direction: column;overflow: hidden;
}.ppe-panel-header {display: flex;justify-content: space-between;align-items: center;padding: 12px 16px;border-bottom: 1px solid var(--border-color);
}.ppe-panel-header h2 {margin: 0;font-size: 14px;font-weight: 600;
}.ppe-panel-actions {display: flex;gap: 8px;
}.ppe-panel-body {flex: 1;overflow: hidden;display: flex;flex-direction: column;
}/* 编辑器面板 */
.ppe-editor-panel {height: 100%;
}/* 标签页样式 */
.ppe-tabs {display: flex;border-bottom: 1px solid var(--border-color);background-color: #FAFAFA;
}.ppe-tab {padding: 8px 16px;font-size: 13px;cursor: pointer;border-right: 1px solid var(--border-color);transition: background-color 0.2s;
}.ppe-tab:hover {background-color: rgba(0, 0, 0, 0.02);
}.ppe-tab.active {background-color: var(--card-bg);border-bottom: 2px solid var(--accent-blue);
}.ppe-tab-add {padding: 8px 12px;font-size: 14px;cursor: pointer;color: var(--secondary-text);display: flex;align-items: center;justify-content: center;
}.ppe-tab-add:hover {color: var(--accent-blue);background-color: rgba(0, 0, 0, 0.02);
}/* 编辑器容器 */
.ppe-editor-container {flex: 1;overflow: hidden;position: relative;
}.ppe-editor-area {height: 100%;width: 100%;
}/* 梯形图编辑器 */
.ppe-ladder-editor {display: flex;flex-direction: column;height: 100%;max-height: 100%;
}.ppe-ladder-toolbar {display: flex;padding: 8px;border-bottom: 1px solid var(--border-color);gap: 8px;background-color: var(--card-bg);flex-shrink: 0;
}.ppe-ladder-btn {padding: 4px 8px;font-size: 12px;border: 1px solid var(--border-color);border-radius: 4px;background-color: var(--card-bg);cursor: pointer;transition: all 0.2s;
}.ppe-ladder-btn:hover {background-color: var(--background);border-color: var(--secondary-text);
}.ppe-ladder-grid {flex: 1;overflow-y: auto;overflow-x: auto;background-color: var(--card-bg);background-image: linear-gradient(var(--grid-color) 1px, transparent 1px),linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);background-size: 20px 20px;padding: 8px;max-height: 400px; /* 设置最大高度为400px */position: relative;
}/* 梯形图元素样式 */
.ladder-element {position: absolute;width: 100px;height: 40px;display: flex;align-items: center;justify-content: center;
}.ladder-function-block {padding: 5px 10px;border: 1px solid var(--primary-text);border-radius: 4px;background-color: var(--background);font-size: 12px;min-width: 80px;text-align: center;
}.ladder-label {position: absolute;font-size: 10px;top: -15px;width: 100%;text-align: center;color: var(--secondary-text);
}/* 右侧面板 */
.ppe-side-panels {display: grid;grid-template-rows: 1fr 1fr;grid-gap: var(--panels-gap);height: 100%;max-height: 100%;
}/* 变量监控面板 */
.ppe-variables-panel {max-height: 100%;
}.ppe-search-bar {padding: 12px 16px;border-bottom: 1px solid var(--border-color);
}.ppe-search-input {width: 100%;height: var(--input-height);padding: 0 12px;border-radius: 6px;border: 1px solid var(--border-color);background-color: var(--background);font-size: 13px;outline: none;
}.ppe-search-input:focus {border-color: var(--accent-blue);box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}.ppe-variables-list {overflow-y: auto;padding: 8px;max-height: calc(100% - 57px);
}.ppe-variable-item {display: flex;padding: 8px 12px;border-radius: 6px;background-color: var(--background);margin-bottom: 6px;transition: all 0.2s;
}.ppe-variable-item:hover {background-color: rgba(0, 102, 204, 0.05);
}.ppe-variable-name {flex: 3;font-size: 13px;font-weight: 500;
}.ppe-variable-type {flex: 1;font-size: 12px;color: var(--secondary-text);text-align: center;
}.ppe-variable-value {flex: 1;font-size: 13px;text-align: right;font-weight: 500;color: var(--accent-blue);
}/* 堆垛机状态面板 */
.ppe-stacker-panel {display: flex;flex-direction: column;
}.ppe-stacker-animation {flex: 1;position: relative;background-color: #F0F2F5;overflow: hidden;min-height: 140px;
}.ppe-storage-rack {position: absolute;top: 10px;left: 10px;right: 10px;bottom: 10px;background-image: repeating-linear-gradient(to right,rgba(0, 0, 0, 0.1) 0px,rgba(0, 0, 0, 0.1) 1px,transparent 1px,transparent 40px),repeating-linear-gradient(to bottom,rgba(0, 0, 0, 0.1) 0px,rgba(0, 0, 0, 0.1) 1px,transparent 1px,transparent 40px);border: 1px solid rgba(0, 0, 0, 0.2);
}.ppe-stacker {position: absolute;width: 30px;height: 40px;background-color: var(--accent-blue);border-radius: 4px;left: 150px;top: 60px;transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}.ppe-stacker:before {content: '';position: absolute;width: 10px;height: 5px;background-color: #FFD700;bottom: 0;left: 10px;border-radius: 2px 2px 0 0;transform-origin: bottom center;animation: move-fork 2s ease-in-out infinite;
}@keyframes move-fork {0%, 100% { transform: scaleY(1); }50% { transform: scaleY(2); }
}.ppe-stacker-metrics {display: flex;justify-content: space-between;padding: 12px 16px;background-color: var(--card-bg);border-top: 1px solid var(--border-color);
}.ppe-metric {text-align: center;flex: 1;
}.ppe-metric-value {font-size: 16px;font-weight: 600;
}.ppe-metric-label {font-size: 12px;color: var(--secondary-text);margin-top: 2px;
}/* 按钮样式 */
.ppe-btn {display: flex;align-items: center;gap: 6px;padding: 8px 12px;border-radius: 6px;border: 1px solid var(--border-color);background-color: var(--card-bg);color: var(--primary-text);font-size: 14px;cursor: pointer;transition: all 0.2s;
}.ppe-btn:hover {background-color: var(--background);
}.ppe-btn-sm {padding: 4px 10px;font-size: 13px;
}.ppe-btn-primary {background-color: var(--accent-blue);border-color: var(--accent-blue);color: white;
}.ppe-btn-primary:hover {background-color: #0055B3;border-color: #0055B3;
}.ppe-btn-secondary {background-color: var(--background);
}.ppe-btn-add {background-color: transparent;color: var(--accent-blue);border-style: dashed;
}.ppe-btn-add:hover {background-color: rgba(0, 102, 204, 0.05);
}/* 告警面板 */
.ppe-alerts-panel {margin: 0 var(--panels-gap) var(--panels-gap);height: 120px;flex-shrink: 0;
}.ppe-alerts-list {display: flex;flex-direction: column;gap: 8px;padding: 8px;overflow-y: auto;max-height: 100%;
}/* 告警项 */
.ppe-alert-item {display: flex;align-items: center;gap: 10px;background-color: var(--background);border-radius: 6px;padding: 10px 12px;
}.alert-warning {border-left: 3px solid var(--accent-orange);
}.alert-error {border-left: 3px solid var(--accent-red);
}.alert-info {border-left: 3px solid var(--accent-blue);
}.ppe-alert-icon {color: var(--accent-orange);
}.alert-error .ppe-alert-icon {color: var(--accent-red);
}.alert-info .ppe-alert-icon {color: var(--accent-blue);
}.ppe-alert-content {flex: 1;
}.ppe-alert-message {font-size: 13px;
}.ppe-alert-time {font-size: 12px;color: var(--secondary-text);margin-top: 2px;
}.ppe-alert-actions {flex-shrink: 0;
}.ppe-alert-btn {padding: 3px 8px;border-radius: 4px;font-size: 12px;border: 1px solid var(--border-color);background-color: transparent;color: var(--accent-blue);cursor: pointer;
}.ppe-alert-btn:hover {background-color: var(--accent-blue);color: white;
}/* 状态徽章 */
.ppe-status-badge, .ppe-badge {font-size: 12px;font-weight: 500;padding: 2px 8px;border-radius: 10px;
}.ppe-badge {background-color: var(--accent-red);color: white;min-width: 24px;text-align: center;
}.status-normal {background-color: rgba(52, 199, 89, 0.1);color: var(--accent-green);
}.status-warning {background-color: rgba(255, 149, 0, 0.1);color: var(--accent-orange);
}.status-error {background-color: rgba(255, 59, 48, 0.1);color: var(--accent-red);
}/* 输入和表单样式 */
.ppe-input {height: var(--input-height);padding: 0 12px;border-radius: 6px;border: 1px solid var(--border-color);width: 100%;font-size: 13px;outline: none;
}.ppe-input:focus {border-color: var(--accent-blue);box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}.ppe-form-group {margin-bottom: 16px;
}.ppe-form-group label {display: block;font-size: 13px;font-weight: 500;margin-bottom: 6px;
}/* 模态框 */
.ppe-modal {display: none;position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);z-index: 1000;justify-content: center;align-items: center;
}.ppe-modal.active {display: flex;
}.ppe-modal-content {background-color: var(--card-bg);border-radius: var(--border-radius);width: 90%;max-width: 600px;max-height: 90vh;display: flex;flex-direction: column;overflow: hidden;box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}.ppe-modal-header {display: flex;justify-content: space-between;align-items: center;padding: 15px 20px;border-bottom: 1px solid var(--border-color);
}.ppe-modal-header h3 {margin: 0;font-size: 16px;font-weight: 600;
}.ppe-modal-close {background: none;border: none;font-size: 20px;cursor: pointer;color: var(--secondary-text);
}.ppe-modal-body {padding: 20px;overflow-y: auto;flex: 1;
}.ppe-modal-footer {display: flex;justify-content: flex-end;gap: 10px;padding: 15px 20px;border-top: 1px solid var(--border-color);
}/* 硬件模块列表 */
.ppe-hardware-modules {margin-top: 20px;
}.ppe-hardware-modules h4 {font-size: 14px;margin-bottom: 10px;
}.ppe-module-list {display: flex;flex-direction: column;gap: 8px;margin-bottom: 12px;
}.ppe-module-item {border: 1px solid var(--border-color);border-radius: 6px;overflow: hidden;
}.ppe-module-header {padding: 8px 12px;background-color: var(--background);display: flex;justify-content: space-between;font-size: 13px;font-weight: 500;
}.ppe-module-body {padding: 8px 12px;font-size: 12px;color: var(--secondary-text);
}/* 动画效果 */
@keyframes pulse {0% { transform: scale(1); }50% { transform: scale(1.05); }100% { transform: scale(1); }
}@keyframes blink {0%, 100% { opacity: 1; }50% { opacity: 0.5; }
}.animate-pulse {animation: pulse 2s infinite;
}.animate-blink {animation: blink 1.5s infinite;
}/* 响应式布局调整 */
@media (max-width: 992px) {.ppe-main-panels {grid-template-columns: 1fr;grid-template-rows: 1fr auto;}.ppe-side-panels {grid-template-rows: auto auto;}.ppe-stacker-panel {min-height: 250px;}
}@media (max-width: 768px) {.ppe-toolbar {overflow-x: auto;justify-content: flex-start;}.ppe-toolbar-group {flex-shrink: 0;}
} 

script.js

// 开放式PLC编程环境 - JavaScript// 全局变量
let editor = null; // Monaco编辑器实例
let currentMode = 'ladder'; // 当前编程模式:ladder(梯形图), st(结构化文本), fbd(功能块)
let currentPLCVendor = 'siemens'; // 当前选择的PLC厂商
let simulationRunning = true; // 模拟运行状态
let stackerPosition = { x: 150, y: 60 }; // 堆垛机位置
let alertHistory = []; // 告警历史记录
let variableValues = {}; // 变量值记录
let plcConnected = true; // PLC连接状态// 初始示例代码 - ST语言
const sampleSTCode = `// 堆垛机控制程序 - 结构化文本
PROGRAM Main
VARS_StartButton AT %I0.0 : BOOL;   // 启动按钮S_StopButton AT %I0.1 : BOOL;    // 停止按钮S_ResetButton AT %I0.2 : BOOL;   // 复位按钮S_EmergencyStop AT %I0.3 : BOOL; // 紧急停止按钮Position_X AT %MW10 : REAL;      // X轴位置Position_Y AT %MW14 : REAL;      // Y轴位置Position_Z AT %MW18 : REAL;      // Z轴位置Speed_X AT %MW22 : REAL;         // X轴速度Speed_Y AT %MW26 : REAL;         // Y轴速度Speed_Z AT %MW30 : REAL;         // Z轴速度CurrentShelf AT %MW34 : INT;     // 当前货架位置TargetShelf AT %MW36 : INT;      // 目标货架位置RunStatus AT %Q0.0 : BOOL;       // 运行状态FaultStatus AT %Q0.1 : BOOL;     // 故障状态PLC_Cycle : TIME := T#10MS;      // PLC周期时间
END_VAR// 主程序循环
RunStatus := S_StartButton AND NOT S_StopButton AND NOT S_EmergencyStop;
FaultStatus := S_EmergencyStop OR (Position_X > 2000.0);// 如果系统处于运行状态,执行堆垛机控制
IF RunStatus THEN// 简单移动逻辑 - 向目标位置移动IF CurrentShelf <> TargetShelf THEN// 计算目标位置坐标Position_X := Position_X + Speed_X * PLC_Cycle;Position_Y := Position_Y + Speed_Y * PLC_Cycle;// 检查是否到达目标位置IF ABS(Position_X - TargetShelf * 100.0) < 1.0 THENCurrentShelf := TargetShelf;Speed_X := 0.0;Speed_Y := 0.0;END_IF;END_IF;
END_IF;// 复位操作
IF S_ResetButton THENPosition_X := 0.0;Position_Y := 0.0;Position_Z := 0.0;Speed_X := 0.0;Speed_Y := 0.0;Speed_Z := 0.0;CurrentShelf := 0;
END_IF;END_PROGRAM`;// 梯形图示例数据
const ladderData = [{ type: 'rung_start', x: 0, y: 0 },{ type: 'contact_no', x: 1, y: 0, label: 'S_StartButton' },{ type: 'contact_nc', x: 2, y: 0, label: 'S_StopButton' },{ type: 'contact_nc', x: 3, y: 0, label: 'S_EmergencyStop' },{ type: 'coil', x: 4, y: 0, label: 'RunStatus' },{ type: 'rung_end', x: 5, y: 0 },{ type: 'rung_start', x: 0, y: 1 },{ type: 'contact_no', x: 1, y: 1, label: 'S_EmergencyStop' },{ type: 'coil', x: 4, y: 1, label: 'FaultStatus' },{ type: 'rung_end', x: 5, y: 1 },{ type: 'rung_start', x: 0, y: 2 },{ type: 'contact_no', x: 1, y: 2, label: 'RunStatus' },{ type: 'contact_no', x: 2, y: 2, label: 'TargetNotReached' },{ type: 'function_block', x: 3, y: 2, label: 'MOVE', inputs: ['Speed_X', 'PLC_Cycle'], outputs: ['Position_X'] },{ type: 'rung_end', x: 5, y: 2 },{ type: 'rung_start', x: 0, y: 3 },{ type: 'contact_no', x: 1, y: 3, label: 'S_ResetButton' },{ type: 'function_block', x: 3, y: 3, label: 'RESET', inputs: [], outputs: ['Position_X', 'Position_Y', 'Position_Z'] },{ type: 'rung_end', x: 5, y: 3 },
];// 初始化函数
function initPLCProgrammingEnvironment() {console.log('初始化PLC编程环境...');// 初始化Monaco编辑器initMonacoEditor();// 初始化梯形图编辑器initLadderEditor();// 初始化事件监听器setupEventListeners();// 初始化堆垛机动画initStackerAnimation();// 初始化变量模拟initVariableSimulation();// 更新界面状态updateUIState();console.log('PLC编程环境初始化完成');
}// 初始化Monaco编辑器
function initMonacoEditor() {require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs' }});require(['vs/editor/editor.main'], function() {// 注册PLC编程语言monaco.languages.register({ id: 'structuredtext' });// 配置语法高亮monaco.languages.setMonarchTokensProvider('structuredtext', {defaultToken: '',ignoreCase: true,tokenizer: {root: [[/\/\/.*$/, 'comment'],[/\b(PROGRAM|END_PROGRAM|VAR|END_VAR|IF|THEN|ELSE|ELSIF|END_IF|FOR|TO|BY|DO|END_FOR|WHILE|END_WHILE|REPEAT|UNTIL|END_REPEAT|CASE|OF|END_CASE|RETURN|EXIT)\b/, 'keyword'],[/\b(BOOL|INT|DINT|REAL|TIME|STRING|ARRAY|STRUCT|END_STRUCT)\b/, 'type'],[/\b(TRUE|FALSE|NULL)\b/, 'constant'],[/\b(AT|ANY|FROM)\b/, 'modifier'],[/\b(ABS|SQRT|LOG|EXP|SIN|COS|TAN|ASIN|ACOS|ATAN|ADD|SUB|MUL|DIV|MOD)\b/, 'function'],[/\b(AND|OR|NOT|XOR|GT|GE|LT|LE|EQ|NE)\b/, 'operator'],[/\b\d+\.\d*([eE][-+]?\d+)?\b/, 'number.float'],[/\b\d+\b/, 'number'],[/T#[0-9]+[smhd]/, 'timevalue'],[/\%[IQM][XBWDL]?[0-9\.]+/, 'address'],[/"([^"\\]|\\.)*$/, 'string.invalid'],[/"/, { token: 'string.quote', bracket: '@open', next: '@string' }],],string: [[/[^\\"]+/, 'string'],[/\\./, 'string.escape'],[/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }]]}});// 创建编辑器editor = monaco.editor.create(document.getElementById('monaco-editor'), {value: sampleSTCode,language: 'structuredtext',theme: 'vs-light',automaticLayout: true,minimap: {enabled: false}});// 隐藏编辑器,显示梯形图编辑器document.getElementById('monaco-editor').style.display = 'none';document.querySelector('.ppe-ladder-editor').style.display = 'flex';});
}// 初始化梯形图编辑器
function initLadderEditor() {const ladderGrid = document.getElementById('ladder-grid');// 清空现有内容ladderGrid.innerHTML = '';// 创建网格背景const gridSize = 20;const cols = 10;const rows = 15;// 绘制梯形图ladderData.forEach(element => {const elementDiv = document.createElement('div');elementDiv.className = `ladder-element ladder-${element.type}`;elementDiv.style.left = `${element.x * gridSize * 5}px`;elementDiv.style.top = `${element.y * gridSize * 3}px`;if (element.label) {const labelDiv = document.createElement('div');labelDiv.className = 'ladder-label';labelDiv.textContent = element.label;elementDiv.appendChild(labelDiv);}// 根据类型设置具体内容if (element.type === 'contact_no') {elementDiv.innerHTML += `<svg viewBox="0 0 50 20" xmlns="http://www.w3.org/2000/svg"><line x1="0" y1="10" x2="15" y2="10" stroke="black" /><line x1="35" y1="10" x2="50" y2="10" stroke="black" /><line x1="15" y1="0" x2="15" y2="20" stroke="black" /><line x1="35" y1="0" x2="35" y2="20" stroke="black" /></svg>`;} else if (element.type === 'contact_nc') {elementDiv.innerHTML += `<svg viewBox="0 0 50 20" xmlns="http://www.w3.org/2000/svg"><line x1="0" y1="10" x2="15" y2="10" stroke="black" /><line x1="35" y1="10" x2="50" y2="10" stroke="black" /><line x1="15" y1="0" x2="15" y2="20" stroke="black" /><line x1="35" y1="0" x2="35" y2="20" stroke="black" /><line x1="15" y1="5" x2="35" y2="5" stroke="black" /></svg>`;} else if (element.type === 'coil') {elementDiv.innerHTML += `<svg viewBox="0 0 50 20" xmlns="http://www.w3.org/2000/svg"><line x1="0" y1="10" x2="10" y2="10" stroke="black" /><line x1="40" y1="10" x2="50" y2="10" stroke="black" /><circle cx="25" cy="10" r="15" fill="none" stroke="black" /></svg>`;} else if (element.type === 'function_block') {const blockDiv = document.createElement('div');blockDiv.className = 'ladder-function-block';blockDiv.textContent = element.label || 'FUNC';elementDiv.appendChild(blockDiv);} else if (element.type === 'rung_start') {elementDiv.innerHTML += `<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><line x1="0" y1="10" x2="20" y2="10" stroke="black" stroke-width="2" /></svg>`;} else if (element.type === 'rung_end') {elementDiv.innerHTML += `<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><line x1="0" y1="10" x2="20" y2="10" stroke="black" stroke-width="2" /></svg>`;}ladderGrid.appendChild(elementDiv);});
}// 初始化事件监听器
function setupEventListeners() {// 编程模式切换document.querySelectorAll('.ppe-tool-btn[data-mode]').forEach(btn => {btn.addEventListener('click', function() {const mode = this.getAttribute('data-mode');switchProgrammingMode(mode);});});// PLC厂商选择document.getElementById('plc-vendor').addEventListener('change', function() {currentPLCVendor = this.value;console.log(`切换到PLC厂商: ${currentPLCVendor}`);});// 保存按钮document.getElementById('save-btn').addEventListener('click', function() {saveProgram();});// 下载按钮document.getElementById('download-btn').addEventListener('click', function() {downloadProgram();});// 验证按钮document.getElementById('verify-btn').addEventListener('click', function() {verifyProgram();});// 格式化按钮document.getElementById('format-btn').addEventListener('click', function() {formatProgram();});// 部署按钮document.getElementById('deploy-btn').addEventListener('click', function() {deployProgram();});// 标签页切换document.querySelectorAll('.ppe-tab').forEach(tab => {tab.addEventListener('click', function() {const tabId = this.getAttribute('data-tab');switchTab(tabId);});});// 刷新变量按钮document.getElementById('refresh-vars-btn').addEventListener('click', function() {refreshVariables();});// 变量搜索功能document.querySelector('.ppe-search-input').addEventListener('input', function() {filterVariables(this.value);});// 硬件配置模态框document.getElementById('menu-btn').addEventListener('click', function() {openHardwareConfig();});// 关闭模态框document.querySelector('.ppe-modal-close').addEventListener('click', function() {closeModal('hardware-config-modal');});document.querySelector('.modal-close-btn').addEventListener('click', function() {closeModal('hardware-config-modal');});
}// 切换编程模式
function switchProgrammingMode(mode) {// 更新全局变量currentMode = mode;// 更新按钮状态document.querySelectorAll('.ppe-tool-btn[data-mode]').forEach(btn => {btn.classList.remove('active');});document.querySelector(`.ppe-tool-btn[data-mode="${mode}"]`).classList.add('active');// 显示相应的编辑器if (mode === 'ladder') {document.getElementById('monaco-editor').style.display = 'none';document.querySelector('.ppe-ladder-editor').style.display = 'flex';} else {document.getElementById('monaco-editor').style.display = 'block';document.querySelector('.ppe-ladder-editor').style.display = 'none';// 更新Monaco编辑器语言if (editor) {if (mode === 'st') {monaco.editor.setModelLanguage(editor.getModel(), 'structuredtext');} else {monaco.editor.setModelLanguage(editor.getModel(), 'plaintext');}}}console.log(`切换到${mode}编程模式`);
}// 初始化堆垛机动画
function initStackerAnimation() {const stacker = document.getElementById('stacker-animation');if (!stacker) return;// 设置初始位置updateStackerPosition();// 设置动画setInterval(() => {if (simulationRunning) {// 随机移动堆垛机const newX = Math.max(10, Math.min(280, stackerPosition.x + (Math.random() * 40 - 20)));const newY = Math.max(10, Math.min(120, stackerPosition.y + (Math.random() * 20 - 10)));// 缓慢更新位置stackerPosition.x = stackerPosition.x + (newX - stackerPosition.x) * 0.1;stackerPosition.y = stackerPosition.y + (newY - stackerPosition.y) * 0.1;updateStackerPosition();updateStackerMetrics();}}, 500);
}// 更新堆垛机位置
function updateStackerPosition() {const stacker = document.getElementById('stacker-animation');if (!stacker) return;stacker.style.left = `${stackerPosition.x}px`;stacker.style.top = `${stackerPosition.y}px`;
}// 更新堆垛机指标
function updateStackerMetrics() {// 更新速度指标const speedElement = document.querySelector('.ppe-stacker-metrics .ppe-metric:nth-child(1) .ppe-metric-value');if (speedElement) {const speed = (Math.random() * 2 + 4).toFixed(1);speedElement.textContent = `${speed} m/s`;}// 更新位置指标const positionElement = document.querySelector('.ppe-stacker-metrics .ppe-metric:nth-child(2) .ppe-metric-value');if (positionElement) {const shelfX = String.fromCharCode(65 + Math.floor(stackerPosition.x / 40));const shelfY = Math.floor(stackerPosition.y / 30) + 10;positionElement.textContent = `${shelfX}${shelfY}`;}// 更新载重指标const loadElement = document.querySelector('.ppe-stacker-metrics .ppe-metric:nth-child(3) .ppe-metric-value');if (loadElement) {const load = Math.floor(Math.random() * 30 + 30);loadElement.textContent = `${load} kg`;}
}// 初始化变量模拟
function initVariableSimulation() {// 初始化变量值variableValues = {'S_StartButton': true,'S_StopButton': false,'S_ResetButton': false,'S_EmergencyStop': false,'Position_X': 145.32,'Position_Y': 87.65,'Position_Z': 22.41,'Speed_X': 0.75,'Speed_Y': 0.5,'Speed_Z': 0.3,'CurrentShelf': 12,'TargetShelf': 18,'RunStatus': true,'FaultStatus': false};// 定期更新变量值setInterval(() => {if (simulationRunning) {// 随机更新一些变量variableValues.Position_X += (Math.random() * 2 - 1) * 0.5;variableValues.Position_Y += (Math.random() * 2 - 1) * 0.3;variableValues.Position_Z += (Math.random() * 2 - 1) * 0.1;variableValues.Speed_X = Math.max(0, Math.min(2, variableValues.Speed_X + (Math.random() * 0.2 - 0.1)));// 检测异常if (variableValues.Position_X > 200 && Math.random() < 0.05) {addAlert('warning', '堆垛机X轴接近极限位置,请检查程序逻辑');}// 随机PLC连接状态if (Math.random() < 0.01) {plcConnected = !plcConnected;if (!plcConnected) {addAlert('error', 'PLC通信中断,请检查网络连接');} else {addAlert('info', 'PLC通信已恢复');}updateUIState();}// 更新变量显示updateVariableDisplay();}}, 2000);
}// 更新变量显示
function updateVariableDisplay() {document.querySelectorAll('.ppe-variable-item').forEach(item => {const name = item.querySelector('.ppe-variable-name').textContent;const valueElement = item.querySelector('.ppe-variable-value');if (name in variableValues) {const value = variableValues[name];if (typeof value === 'boolean') {valueElement.textContent = value ? 'TRUE' : 'FALSE';} else if (typeof value === 'number') {if (Number.isInteger(value)) {valueElement.textContent = value.toString();} else {valueElement.textContent = value.toFixed(2);}} else {valueElement.textContent = value.toString();}}});
}// 过滤变量
function filterVariables(query) {if (!query) {// 显示所有变量document.querySelectorAll('.ppe-variable-item').forEach(item => {item.style.display = 'flex';});return;}query = query.toLowerCase();document.querySelectorAll('.ppe-variable-item').forEach(item => {const name = item.querySelector('.ppe-variable-name').textContent.toLowerCase();if (name.includes(query)) {item.style.display = 'flex';} else {item.style.display = 'none';}});
}// 刷新变量
function refreshVariables() {console.log('刷新变量...');// 添加闪烁动画效果document.querySelectorAll('.ppe-variable-item').forEach(item => {item.classList.add('animate-blink');setTimeout(() => {item.classList.remove('animate-blink');}, 1000);});updateVariableDisplay();
}// 添加告警
function addAlert(type, message) {const alert = {id: Date.now(),type: type,message: message,time: new Date()};// 添加到告警历史alertHistory.unshift(alert);// 限制最大历史记录数if (alertHistory.length > 5) {alertHistory.pop();}// 更新告警显示updateAlertDisplay();// 更新告警徽章const badge = document.querySelector('.ppe-badge');if (badge) {badge.textContent = alertHistory.length;}
}// 更新告警显示
function updateAlertDisplay() {const alertsList = document.getElementById('alerts-list');if (!alertsList) return;// 清空当前告警alertsList.innerHTML = '';// 添加告警alertHistory.forEach(alert => {let alertClass = 'alert-info';let iconSvg = '';if (alert.type === 'warning') {alertClass = 'alert-warning';iconSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>';} else if (alert.type === 'error') {alertClass = 'alert-error';iconSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>';} else {iconSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>';}// 格式化时间const time = new Intl.DateTimeFormat('zh-CN', {hour: '2-digit',minute: '2-digit',second: '2-digit'}).format(alert.time);const alertHtml = `<div class="ppe-alert-item ${alertClass}"><div class="ppe-alert-icon">${iconSvg}</div><div class="ppe-alert-content"><div class="ppe-alert-message">${alert.message}</div><div class="ppe-alert-time">${time}</div></div><div class="ppe-alert-actions"><button class="ppe-alert-btn">查看</button></div></div>`;alertsList.innerHTML += alertHtml;});// 添加查看按钮事件document.querySelectorAll('.ppe-alert-btn').forEach((btn, index) => {btn.addEventListener('click', function() {console.log(`查看告警: ${alertHistory[index].message}`);// 这里可以添加弹出详情的逻辑});});
}// 保存程序
function saveProgram() {console.log('保存程序...');addAlert('info', '程序已保存');
}// 下载程序
function downloadProgram() {console.log('下载程序...');let content = '';let filename = '';if (currentMode === 'ladder') {content = JSON.stringify(ladderData, null, 2);filename = 'ladder_program.json';} else {if (editor) {content = editor.getValue();filename = currentMode === 'st' ? 'program.st' : 'program.txt';}}if (content) {const blob = new Blob([content], { type: 'text/plain' });const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = filename;a.click();URL.revokeObjectURL(url);addAlert('info', `程序已下载为 ${filename}`);}
}// 验证程序
function verifyProgram() {console.log('验证程序...');// 模拟验证过程setTimeout(() => {if (Math.random() < 0.7) {addAlert('info', '程序验证通过');} else {addAlert('warning', '程序验证发现潜在问题');}}, 500);
}// 格式化程序
function formatProgram() {console.log('格式化程序...');if (currentMode !== 'ladder' && editor) {// 模拟格式化过程editor.getAction('editor.action.formatDocument').run();addAlert('info', '程序已格式化');} else {addAlert('info', '梯形图程序无需格式化');}
}// 部署程序
function deployProgram() {console.log('部署程序...');// 检查PLC连接状态if (!plcConnected) {addAlert('error', '无法部署程序,PLC未连接');return;}// 模拟部署过程const deployBtn = document.getElementById('deploy-btn');if (deployBtn) {deployBtn.disabled = true;deployBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" class="animate-spin"></svg> 部署中...';}setTimeout(() => {if (deployBtn) {deployBtn.disabled = false;deployBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg> 部署程序';}if (Math.random() < 0.9) {addAlert('info', '程序已成功部署到PLC');} else {addAlert('error', '程序部署失败,请检查PLC连接');}}, 2000);
}// 切换标签页
function switchTab(tabId) {document.querySelectorAll('.ppe-tab').forEach(tab => {tab.classList.remove('active');});document.querySelector(`.ppe-tab[data-tab="${tabId}"]`).classList.add('active');console.log(`切换到标签页: ${tabId}`);// 这里可以添加加载不同标签页内容的逻辑
}// 打开硬件配置对话框
function openHardwareConfig() {document.getElementById('hardware-config-modal').style.display = 'flex';
}// 关闭模态框
function closeModal(modalId) {document.getElementById(modalId).style.display = 'none';
}// 更新UI状态
function updateUIState() {// 更新PLC连接状态const statusBadge = document.querySelector('.ppe-status-badge');if (statusBadge) {if (plcConnected) {statusBadge.className = 'ppe-status-badge status-normal';statusBadge.textContent = '运行中';} else {statusBadge.className = 'ppe-status-badge status-error';statusBadge.textContent = '已断开';}}
}// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {initPLCProgrammingEnvironment();
});// 导出全局对象
window.plcProgramming = {switchMode: switchProgrammingMode,deployProgram: deployProgram,refreshVariables: refreshVariables,addAlert: addAlert
}; 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内网和外网怎么互通?外网访问内网的几种简单方式

在企业或家庭网络中&#xff0c;经常会遇到不同内网环境下网络互通问题。例如&#xff0c;当公司本地局域网内有个办公OA网站&#xff0c;在办公室内电脑上网可以登录使用&#xff0c;但在家带宽下就无法直接通信访问到。这就需要我们采取一些实用的内外网互通技巧来解决这个问…

使用大语言模型进行机器人规划(Robot planning with LLMs)

李升伟 编译 长期规划在机器人学领域可以从经典控制方法与大型语言模型在现实世界知识能力的结合中获益。 在20世纪80年代&#xff0c;机器人学和人工智能&#xff08;AI&#xff09;领域的专家提出了莫雷奇悖论&#xff0c;观察到人类看似简单的涉及移动和感知的任务&#x…

【计算机视觉】OpenCV实战项目: opencv-text-deskew:实时文本图像校正

opencv-text-deskew&#xff1a;基于OpenCV的实时文本图像校正 一、项目概述与技术背景1.1 核心功能与创新点1.2 技术指标对比1.3 技术演进路线 二、环境配置与算法原理2.1 硬件要求2.2 软件部署2.3 核心算法流程 三、核心算法解析3.1 文本区域定位3.2 角度检测优化3.3 仿射变换…

可视化图解算法33:判断是不是平衡二叉树

1. 题目 描述 输入一棵节点数为 n 的二叉树&#xff0c;判断该二叉树是否是平衡二叉树。 在这里&#xff0c;我们只需要考虑其平衡性&#xff0c;不需要考虑其是不是排序二叉树 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;&#xff0c;具有以下性质&#xff1…

【Linux网络】应用层自定义协议与序列化

应用层自定义协议与序列化 应用层 我们程序员写的一个个解决我们实际问题,满足我们日常需求的网络程序,都是在应用层. 协议是一种"约定".Socket的接口,在读写数据时,都是按"字符串"的方式来发送接收的.如果我们要传输一些"结构化的数据"怎么办…

MySQL + Elasticsearch:为什么要使用ES,使用场景与架构设计详解

MySQL Elasticsearch&#xff1a;为什么要使用ES&#xff0c;使用场景与架构设计详解 前言一、MySQL Elasticsearch的背景与需求1.1 为什么要使用Elasticsearch&#xff08;ES&#xff09;&#xff1f;1.2 为什么MySQL在某些场景下不足以满足需求&#xff1f;1.3 MySQL Elas…

PPL困惑度的计算

1. 公式 PPL&#xff08;Perplexity&#xff09;困惑度 是自然语言处理&#xff08;NLP&#xff09;中常用的评估语言模型&#xff08;Language Model&#xff09;性能的指标。PPL 用于衡量语言模型对语言序列的预测能力&#xff0c;数值越小&#xff0c;说明模型的预测能力越…

MegaCLI Raid管理工具

整理在CentOS 7.9和Ubuntu 24.04上&#xff0c;MegaCLI 工具的安装与常用命令。 1. 参考 下载和安装MegaCLI工具 MegaCli RAID管理工具 Megacli 批量磁盘巡检 ubuntu24.04 No such file libncursesw.so.5 dell服务器硬盘的状态变成外来&#xff08;foreign&#xff09;命…

HTML9:页面结构分析

页面结构分析 元素名描述header标题头部区域的内容&#xff08;用于页面或页面中的一块区域&#xff09;footer标记脚部区域的内容&#xff08;用于整个页面或页面的一块区域&#xff09;sectionWeb页面的一块独立区域article独立的文章内容aside相关的内容或应用&#xff08;…

分布式处理架构

分布式处理架构是一种将计算任务分散到多台计算机或服务器上协同完成的系统设计方法。这种架构通过将工作负载分配到多个节点&#xff08;可以是物理机、虚拟机或容器&#xff09;来提高性能、可靠性和可扩展性。下面我将从多个角度详细解释这一概念&#xff1a; 分布式架构的…

算法每日一题 | 入门-分支结构-Apples Prologue/苹果和虫子

Apples Prologue/苹果和虫子 题目描述 小 B 喜欢吃苹果。她现在有 m m m&#xff08;1 ≤ m ≤100&#xff09;个苹果&#xff0c;吃完一个苹果需要花费 t t t&#xff08;0 ≤ t≤ 100&#xff09;分钟&#xff0c;吃完一个后立刻开始吃下一个。 现在时间过去了 s s s&a…

RT Thread Studio创建软件和硬件RTC工程

MCU型号&#xff1a;STM32F103RET6 一.配置软件模拟RTC 1.生成一个带串口输出的工程文件&#xff0c;新建RT-Thread项目工程文件。 2.查看电路图中的串口输出管脚&#xff0c;根据STMCubeMx软件可知此串口为USART1&#xff0c;选择芯片型号为STM32F103RET6&#xff0c;控制台…

STC32G12K128-旋转编码器-软件去抖

STC32G12K128-旋转编码器-软件去抖 简介代码 简介 EC11旋转编码器是一种可以连续旋转的器件A,B,C为旋转编码引脚&#xff0c;带按键的有D,E引脚。引脚功能&#xff1a; A&#xff1a;编码器A相&#xff1b;B&#xff1a;编码器B相&#xff1b;C&#xff1a;公共端-一般接到GN…

配置Jupyter Notebook环境及Token认证(Linux服务器)

配置Jupyter Notebook环境及Token认证&#xff08;Linux服务器&#xff09; 背景 在Ubuntu 18.04.6 LTS服务器&#xff08;IP: 39.105.167.2&#xff09;上&#xff0c;基于虚拟环境pytorch_env&#xff0c;通过Mac终端&#xff08;SSH&#xff09;配置Jupyter Notebook环境&…

从零开始学Flink:开启实时计算的魔法之旅

在凌晨三点的数据监控大屏前&#xff0c;某电商平台的技术负责人突然发现一个异常波动&#xff1a;支付成功率骤降15%。传统的数据仓库此时还在沉睡&#xff0c;而基于Flink搭建的实时风控系统早已捕捉到这个信号&#xff0c;自动触发预警机制。当运维团队赶到时&#xff0c;系…

基于k8s的Jenkins CI/CD平台部署实践(三):集成ArgoCD实现持续部署

基于k8s的Jenkins CI/CD平台部署实践&#xff08;三&#xff09;&#xff1a;集成ArgoCD实现持续部署 文章目录 基于k8s的Jenkins CI/CD平台部署实践&#xff08;三&#xff09;&#xff1a;集成ArgoCD实现持续部署一、Argocd简介二、安装Helm三、Helm安装ArgoCD实战1. 添加Arg…

[C++类和对象]类和对象的引入

面向过程和面向对象 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用来逐步解决问题 C是基于面向对象的,关注的是对象,将一件事情分成不同的对象,靠对象之间完成交互 类的引入 C语言结构体中只能定义变量,在C中,结构体不仅仅可以定义变量,而且可以定义函…

AWS之存储服务

目录 一、传统存储术语 二、传统存储与云存储的关系 三、云存储之AWS 使用场景 文件存储 数据块存储 对象存储 EBS、EFS、S3对比 EBS块存储 S3对象存储 S3 使用案例 S3 存储类 EFS文件存储 一、传统存储术语 分类 接口/技术类型 应用场景特点 关系及区别 机械硬…

WPDRRC 模型:构建动态闭环的信息安全防御体系

WPDRRC 模型是一种信息安全整体架构设计模型&#xff0c;由预警&#xff08;Warning&#xff09;、保护&#xff08;Protection&#xff09;、检测&#xff08;Detection&#xff09;、反应&#xff08;Reaction&#xff09;、恢复&#xff08;Recovery&#xff09;和反击&…

Redis 数据类型详解(二):Hash 类型全解析

文章目录 一、什么是 Redis 的 Hash 类型&#xff1f;二、Hash为什么在有些时候比String好用三、常见命令1.HSET key field value2.HGET key field3.HMSET4.HMGET5.HGETALL6.HKEYS7.HVALS8.HINCRBY9.HSETNX 四、应用场景五、性能优势六、注意事项总结 提示&#xff1a;以下是本…