PLC控制系统监控面板
这是一个用于PLC控制系统监控面板的自定义组件,提供了PLC编程与自动化控制逻辑设计的可视化监控界面。组件采用工业风格设计,包含实时数据展示、系统状态监控、控制功能以及报警和日志记录等功能。
功能特点
- 工业风格UI设计:深色主题,高对比度,符合工业控制系统的视觉风格
- PLC状态监控:实时展示CPU负载、内存使用、通信状态等关键指标
- 交互式逻辑流程图:可视化展示PLC逻辑控制流程,支持缩放和重置视图
- 控制面板:包含启动、停止、重置和紧急停止等核心控制功能
- 实时数据展示:显示温度、压力、流量和电机速度等关键参数
- 报警系统:实时显示系统报警信息,自动更新报警计数
- 操作日志:记录系统操作和状态变化,支持日志清除
- 动态数据模拟:自动模拟传感器数据变化,提供真实的使用体验
- 响应式设计:适应不同屏幕尺寸,确保在各种设备上正常显示
主要区域说明
组件包含以下主要区域:
- 顶部标题和状态栏:显示系统名称和当前运行状态
- PLC状态监控区:展示PLC核心性能指标和运行状态
- 逻辑控制流程区:以交互式图形方式展示PLC逻辑流程
- 控制面板区:提供系统控制按钮和实时参数数据表格
- 报警和日志区:展示系统报警和操作日志信息
自定义选项
可以通过修改代码自定义以下内容:
- 颜色主题 :在CSS中修改
:root
中的颜色变量 - 数据更新频率 :在
startDataSimulation
函数中调整更新间隔 - PLC逻辑图 :在
setupLogicDiagram
函数中修改组件和连接定义 - 报警阈值 :在
simulateRealtimeData
函数中调整报警触发条件 - 添加新的监控参数:在HTML中添加新的表格行,并在JavaScript中更新对应数据
浏览器兼容性
组件使用了现代Web标准,包括SVG、CSS变量和ES6特性。建议在最新版本的Chrome、Firefox或Edge浏览器中使用,以获得最佳体验。
注意事项
- 组件中的数据为模拟数据,实际应用中需要连接到真实的PLC数据源
- 为了获得更好的性能,大型部署环境中应优化数据更新频率
- 如果需要连接实际的PLC系统,请替换
simulateRealtimeData
函数,使用适当的API调用
效果展示

源码
index.html
javascript
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PLC控制系统监控面板</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="plc-control-panel">
<!-- 顶部标题和状态栏 -->
<div class="panel-header">
<div class="panel-title">PLC控制系统监控面板</div>
<div class="status-indicator">
<span class="status-text">系统状态:</span>
<span class="status-value" id="system-status">运行中</span>
<div class="status-light running"></div>
</div>
</div>
<!-- 主控制面板 -->
<div class="panel-content">
<!-- 左侧:PLC参数和状态展示 -->
<div class="panel-section plc-status">
<div class="section-header">
<h3>PLC状态监控</h3>
<div class="refresh-btn" id="refresh-status">
<i class="refresh-icon">↻</i>
</div>
</div>
<div class="metrics-grid">
<div class="metric-item">
<div class="metric-label">CPU负载</div>
<div class="metric-value" id="cpu-load">45%</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 45%"></div>
</div>
</div>
<div class="metric-item">
<div class="metric-label">内存使用</div>
<div class="metric-value" id="memory-usage">67%</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 67%"></div>
</div>
</div>
<div class="metric-item">
<div class="metric-label">通信状态</div>
<div class="metric-value" id="comm-status">正常</div>
<div class="status-indicator-small">
<div class="status-light-small normal"></div>
</div>
</div>
<div class="metric-item">
<div class="metric-label">运行时间</div>
<div class="metric-value" id="runtime">164小时</div>
</div>
</div>
</div>
<!-- 中间:交互式PLC逻辑流程图 -->
<div class="panel-section plc-logic">
<div class="section-header">
<h3>PLC逻辑控制流程</h3>
<div class="logic-controls">
<button class="btn-small" id="zoom-in">+</button>
<button class="btn-small" id="zoom-out">-</button>
<button class="btn-small" id="reset-view">重置</button>
</div>
</div>
<div class="logic-diagram" id="logic-diagram">
<!-- 逻辑流程图将通过JavaScript绘制 -->
</div>
</div>
<!-- 右侧:控制按钮和实时数据 -->
<div class="panel-section control-panel">
<div class="section-header">
<h3>控制面板</h3>
</div>
<div class="control-buttons">
<button class="control-btn start-btn" id="start-btn">启动系统</button>
<button class="control-btn stop-btn" id="stop-btn">停止系统</button>
<button class="control-btn reset-btn" id="reset-btn">重置系统</button>
<button class="control-btn emergency-btn" id="emergency-btn">紧急停止</button>
</div>
<div class="divider"></div>
<div class="realtime-data">
<h4>实时数据</h4>
<table class="data-table">
<thead>
<tr>
<th>参数</th>
<th>数值</th>
<th>状态</th>
</tr>
</thead>
<tbody id="realtime-data-table">
<tr>
<td>温度传感器</td>
<td id="temp-value">78.5°C</td>
<td><span class="status-dot warning"></span></td>
</tr>
<tr>
<td>压力传感器</td>
<td id="pressure-value">2.4 MPa</td>
<td><span class="status-dot normal"></span></td>
</tr>
<tr>
<td>流量计</td>
<td id="flow-value">120 L/min</td>
<td><span class="status-dot normal"></span></td>
</tr>
<tr>
<td>电机速度</td>
<td id="motor-speed">1750 RPM</td>
<td><span class="status-dot normal"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 底部报警和日志区域 -->
<div class="panel-footer">
<div class="alarm-section">
<div class="alarm-header">
<span class="alarm-title">系统报警</span>
<span class="alarm-count" id="alarm-count">1</span>
</div>
<div class="alarm-list" id="alarm-list">
<div class="alarm-item warning">
<span class="alarm-time">08:45:12</span>
<span class="alarm-message">温度传感器读数超过警戒值 (>75°C)</span>
</div>
</div>
</div>
<div class="log-section">
<div class="log-header">
<span class="log-title">操作日志</span>
<button class="btn-small" id="clear-logs">清除</button>
</div>
<div class="log-list" id="log-list">
<div class="log-item">
<span class="log-time">08:30:05</span>
<span class="log-message">系统启动成功</span>
</div>
<div class="log-item">
<span class="log-time">08:42:18</span>
<span class="log-message">参数设置已更新</span>
</div>
<div class="log-item">
<span class="log-time">08:45:10</span>
<span class="log-message">温度监控触发预警</span>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
styles.css
javascript
/* PLC控制面板工业风格样式 */
:root {
--dark-gray: #202830;
--medium-gray: #343c44;
--light-gray: #4a5562;
--highlight-blue: #00a8ff;
--highlight-blue-dark: #0077b6;
--warning-yellow: #ffc107;
--danger-red: #ff3b30;
--success-green: #34c759;
--text-white: #f0f2f5;
--text-light: #a7b6c2;
--border-color: #2a343f;
--shadow-color: rgba(0, 0, 0, 0.3);
}
/* 主容器 */
#plc-control-panel {
font-family: 'Roboto', 'Arial', sans-serif;
background-color: var(--dark-gray);
color: var(--text-white);
border-radius: 6px;
box-shadow: 0 4px 16px var(--shadow-color);
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
min-height: 700px;
overflow: hidden;
border: 1px solid var(--border-color);
}
/* 标题栏样式 */
.panel-header {
background-color: var(--medium-gray);
padding: 12px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid var(--border-color);
}
.panel-title {
font-size: 18px;
font-weight: 600;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.status-indicator {
display: flex;
align-items: center;
gap: 10px;
}
.status-text {
color: var(--text-light);
font-size: 14px;
}
.status-value {
font-weight: 500;
}
.status-light {
width: 12px;
height: 12px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
.status-light.running {
background-color: var(--success-green);
box-shadow: 0 0 8px var(--success-green);
}
.status-light.warning {
background-color: var(--warning-yellow);
box-shadow: 0 0 8px var(--warning-yellow);
}
.status-light.error {
background-color: var(--danger-red);
box-shadow: 0 0 8px var(--danger-red);
}
.status-light.offline {
background-color: var(--light-gray);
}
/* 主内容区域 */
.panel-content {
display: flex;
flex: 1;
overflow: hidden;
}
/* 面板部分通用样式 */
.panel-section {
background-color: var(--medium-gray);
border-radius: 4px;
margin: 10px;
padding: 12px;
box-shadow: 0 2px 8px var(--shadow-color);
overflow: auto;
}
.plc-status {
flex: 1;
max-width: 25%;
}
.plc-logic {
flex: 2;
min-width: 45%;
overflow: hidden;
}
.control-panel {
flex: 1;
max-width: 25%;
}
/* 章节标题 */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.section-header h3 {
font-size: 16px;
font-weight: 500;
margin: 0;
color: var(--highlight-blue);
}
/* 刷新按钮 */
.refresh-btn {
width: 28px;
height: 28px;
border-radius: 4px;
background-color: var(--dark-gray);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
}
.refresh-btn:hover {
background-color: var(--highlight-blue-dark);
}
.refresh-icon {
font-style: normal;
color: var(--text-light);
}
/* 指标网格 */
.metrics-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
.metric-item {
background-color: var(--dark-gray);
border-radius: 4px;
padding: 10px;
position: relative;
}
.metric-label {
font-size: 12px;
color: var(--text-light);
margin-bottom: 6px;
}
.metric-value {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
}
/* 进度条 */
.progress-bar {
height: 6px;
background-color: var(--light-gray);
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: var(--highlight-blue);
border-radius: 3px;
transition: width 0.5s ease;
}
/* 小状态指示器 */
.status-indicator-small {
display: flex;
align-items: center;
}
.status-light-small {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 6px;
}
.status-light-small.normal {
background-color: var(--success-green);
box-shadow: 0 0 5px var(--success-green);
}
.status-light-small.warning {
background-color: var(--warning-yellow);
box-shadow: 0 0 5px var(--warning-yellow);
}
.status-light-small.error {
background-color: var(--danger-red);
box-shadow: 0 0 5px var(--danger-red);
}
/* 逻辑流程图部分 */
.logic-controls {
display: flex;
gap: 5px;
}
.btn-small {
background-color: var(--dark-gray);
color: var(--text-white);
border: 1px solid var(--light-gray);
border-radius: 4px;
padding: 2px 8px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.btn-small:hover {
background-color: var(--light-gray);
}
.logic-diagram {
background-color: var(--dark-gray);
border-radius: 4px;
height: calc(100% - 40px);
min-height: 300px;
position: relative;
border: 1px solid var(--border-color);
overflow: hidden;
}
/* 控制按钮 */
.control-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 16px;
}
.control-btn {
padding: 10px;
border-radius: 4px;
font-weight: 500;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
border: none;
color: var(--text-white);
text-align: center;
}
.start-btn {
background-color: var(--success-green);
}
.start-btn:hover {
background-color: #2aa048;
}
.stop-btn {
background-color: var(--warning-yellow);
color: #212529;
}
.stop-btn:hover {
background-color: #e6a800;
}
.reset-btn {
background-color: var(--highlight-blue);
}
.reset-btn:hover {
background-color: var(--highlight-blue-dark);
}
.emergency-btn {
background-color: var(--danger-red);
}
.emergency-btn:hover {
background-color: #e62e24;
}
.divider {
height: 1px;
background-color: var(--border-color);
margin: 16px 0;
}
/* 实时数据表格 */
.realtime-data h4 {
font-size: 14px;
font-weight: 500;
margin: 0 0 12px 0;
color: var(--text-light);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.data-table th {
text-align: left;
color: var(--text-light);
font-weight: 500;
padding: 8px 4px;
border-bottom: 1px solid var(--border-color);
}
.data-table td {
padding: 8px 4px;
border-bottom: 1px solid var(--border-color);
}
.data-table tr:last-child td {
border-bottom: none;
}
.status-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.normal {
background-color: var(--success-green);
box-shadow: 0 0 5px var(--success-green);
}
.status-dot.warning {
background-color: var(--warning-yellow);
box-shadow: 0 0 5px var(--warning-yellow);
}
.status-dot.error {
background-color: var(--danger-red);
box-shadow: 0 0 5px var(--danger-red);
}
/* 底部区域 */
.panel-footer {
display: flex;
background-color: var(--medium-gray);
border-top: 2px solid var(--border-color);
padding: 0;
max-height: 180px;
min-height: 180px;
}
.alarm-section, .log-section {
flex: 1;
display: flex;
flex-direction: column;
border-right: 1px solid var(--border-color);
}
.log-section {
border-right: none;
}
.alarm-header, .log-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: var(--dark-gray);
}
.alarm-title, .log-title {
font-size: 14px;
font-weight: 500;
}
.alarm-count {
background-color: var(--danger-red);
color: white;
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
font-weight: 600;
}
.alarm-list, .log-list {
flex: 1;
overflow-y: auto;
padding: 8px 15px;
}
.alarm-item, .log-item {
padding: 6px 0;
border-bottom: 1px solid var(--border-color);
font-size: 12px;
display: flex;
align-items: flex-start;
}
.alarm-item:last-child, .log-item:last-child {
border-bottom: none;
}
.alarm-time, .log-time {
min-width: 70px;
color: var(--text-light);
margin-right: 10px;
}
.alarm-item.warning .alarm-message {
color: var(--warning-yellow);
}
.alarm-item.error .alarm-message {
color: var(--danger-red);
}
/* 响应式调整 */
@media (max-width: 1200px) {
.panel-content {
flex-direction: column;
}
.plc-status, .plc-logic, .control-panel {
max-width: 100%;
}
.panel-footer {
flex-direction: column;
max-height: 300px;
}
.alarm-section {
border-right: none;
border-bottom: 1px solid var(--border-color);
}
}
script.js
javascript
// PLC控制面板JavaScript
(function() {
// 初始化组件
function initPLCControlPanel() {
console.log("PLC控制面板初始化...");
setupEventListeners();
setupLogicDiagram();
startDataSimulation();
}
// 设置事件监听器
function setupEventListeners() {
// 刷新按钮
document.getElementById('refresh-status').addEventListener('click', refreshStatus);
// 逻辑图控制按钮
document.getElementById('zoom-in').addEventListener('click', () => zoomLogicDiagram(1.2));
document.getElementById('zoom-out').addEventListener('click', () => zoomLogicDiagram(0.8));
document.getElementById('reset-view').addEventListener('click', resetLogicDiagram);
// 控制按钮
document.getElementById('start-btn').addEventListener('click', startSystem);
document.getElementById('stop-btn').addEventListener('click', stopSystem);
document.getElementById('reset-btn').addEventListener('click', resetSystem);
document.getElementById('emergency-btn').addEventListener('click', emergencyStop);
// 清除日志按钮
document.getElementById('clear-logs').addEventListener('click', clearLogs);
}
// 刷新状态
function refreshStatus() {
const refreshBtn = document.getElementById('refresh-status');
refreshBtn.classList.add('rotating');
// 模拟刷新延迟
setTimeout(() => {
updatePLCStatus();
refreshBtn.classList.remove('rotating');
addLogEntry('状态已刷新');
}, 800);
}
// 更新PLC状态
function updatePLCStatus() {
const cpuLoad = getRandomValue(20, 85);
const memoryUsage = getRandomValue(30, 90);
const commStatus = (Math.random() > 0.9) ? '异常' : '正常';
const runtime = Math.floor(100 + Math.random() * 100);
// 更新DOM
document.getElementById('cpu-load').textContent = cpuLoad + '%';
document.querySelector('.plc-status .progress-fill').style.width = cpuLoad + '%';
document.getElementById('memory-usage').textContent = memoryUsage + '%';
document.querySelectorAll('.plc-status .progress-fill')[1].style.width = memoryUsage + '%';
document.getElementById('comm-status').textContent = commStatus;
const commLight = document.querySelector('.status-light-small');
if (commStatus === '正常') {
commLight.className = 'status-light-small normal';
} else {
commLight.className = 'status-light-small error';
addAlarmEntry('通信异常,请检查网络连接');
}
document.getElementById('runtime').textContent = runtime + '小时';
}
// 缩放逻辑图
function zoomLogicDiagram(factor) {
const diagram = document.getElementById('logic-diagram');
const currentScale = diagram.style.transform ?
parseFloat(diagram.style.transform.replace('scale(', '').replace(')', '')) : 1;
const newScale = currentScale * factor;
if (newScale >= 0.5 && newScale <= 2.5) {
diagram.style.transform = `scale(${newScale})`;
}
}
// 重置逻辑图视图
function resetLogicDiagram() {
const diagram = document.getElementById('logic-diagram');
diagram.style.transform = 'scale(1)';
addLogEntry('流程图视图已重置');
}
// 创建PLC逻辑流程图
function setupLogicDiagram() {
const diagram = document.getElementById('logic-diagram');
// 清空现有内容
diagram.innerHTML = '';
// SVG命名空间
const svgNS = "http://www.w3.org/2000/svg";
// 创建SVG元素
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", "100%");
svg.setAttribute("height", "100%");
svg.setAttribute("viewBox", "0 0 800 400");
diagram.appendChild(svg);
// 定义PLC组件
const components = [
{ id: 'input1', type: 'input', x: 50, y: 80, label: '输入模块 1' },
{ id: 'input2', type: 'input', x: 50, y: 220, label: '输入模块 2' },
{ id: 'cpu', type: 'cpu', x: 300, y: 150, label: 'CPU控制器' },
{ id: 'output1', type: 'output', x: 550, y: 80, label: '输出模块 1' },
{ id: 'output2', type: 'output', x: 550, y: 220, label: '输出模块 2' }
];
// 定义连接线
const connections = [
{ from: 'input1', to: 'cpu', state: 'active' },
{ from: 'input2', to: 'cpu', state: 'inactive' },
{ from: 'cpu', to: 'output1', state: 'active' },
{ from: 'cpu', to: 'output2', state: 'active' }
];
// 绘制组件
components.forEach(component => {
drawComponent(svg, svgNS, component);
});
// 绘制连接线
connections.forEach(conn => {
const fromComp = components.find(c => c.id === conn.from);
const toComp = components.find(c => c.id === conn.to);
if (fromComp && toComp) {
drawConnection(svg, svgNS, fromComp, toComp, conn.state);
}
});
}
// 绘制PLC组件
function drawComponent(svg, svgNS, component) {
const group = document.createElementNS(svgNS, "g");
group.setAttribute("id", component.id);
group.setAttribute("class", "component " + component.type);
// 创建矩形
const rect = document.createElementNS(svgNS, "rect");
rect.setAttribute("x", component.x);
rect.setAttribute("y", component.y);
rect.setAttribute("width", "200");
rect.setAttribute("height", "100");
rect.setAttribute("rx", "5");
rect.setAttribute("ry", "5");
// 根据类型设置样式
switch(component.type) {
case 'input':
rect.setAttribute("fill", "#2E4053");
rect.setAttribute("stroke", "#00a8ff");
break;
case 'cpu':
rect.setAttribute("fill", "#1C2833");
rect.setAttribute("stroke", "#3498DB");
break;
case 'output':
rect.setAttribute("fill", "#2E4053");
rect.setAttribute("stroke", "#27AE60");
break;
}
rect.setAttribute("stroke-width", "2");
group.appendChild(rect);
// 创建标签文本
const text = document.createElementNS(svgNS, "text");
text.setAttribute("x", component.x + 100);
text.setAttribute("y", component.y + 50);
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.setAttribute("fill", "#f0f2f5");
text.setAttribute("font-size", "16");
text.textContent = component.label;
group.appendChild(text);
// 绘制状态指示器
const statusCircle = document.createElementNS(svgNS, "circle");
statusCircle.setAttribute("cx", component.x + 100);
statusCircle.setAttribute("cy", component.y + 80);
statusCircle.setAttribute("r", "6");
statusCircle.setAttribute("fill", "#34c759");
group.appendChild(statusCircle);
// 添加到SVG
svg.appendChild(group);
}
// 绘制连接线
function drawConnection(svg, svgNS, fromComp, toComp, state) {
// 计算起点和终点
const startX = fromComp.x + 200;
const startY = fromComp.y + 50;
const endX = toComp.x;
const endY = toComp.y + 50;
// 计算中间点(用于创建平滑的曲线)
const midX = (startX + endX) / 2;
// 创建路径元素
const path = document.createElementNS(svgNS, "path");
path.setAttribute("d", `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`);
path.setAttribute("fill", "none");
path.setAttribute("stroke", state === 'active' ? "#00a8ff" : "#616A6B");
path.setAttribute("stroke-width", "3");
path.setAttribute("class", "connection " + state);
// 添加到SVG
svg.appendChild(path);
// 添加数据流动画(如果连接是活动的)
if (state === 'active') {
const circle = document.createElementNS(svgNS, "circle");
circle.setAttribute("r", "4");
circle.setAttribute("fill", "#00a8ff");
circle.setAttribute("class", "data-point");
// 添加动画
const animateMotion = document.createElementNS(svgNS, "animateMotion");
animateMotion.setAttribute("dur", "2s");
animateMotion.setAttribute("repeatCount", "indefinite");
animateMotion.setAttribute("path", `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`);
circle.appendChild(animateMotion);
svg.appendChild(circle);
}
}
// 系统控制函数
function startSystem() {
updateSystemStatus('运行中', 'running');
addLogEntry('系统已启动');
}
function stopSystem() {
updateSystemStatus('已停止', 'warning');
addLogEntry('系统已停止');
}
function resetSystem() {
updateSystemStatus('重置中', 'warning');
addLogEntry('系统正在重置');
// 模拟重置过程
setTimeout(() => {
updateSystemStatus('运行中', 'running');
addLogEntry('系统重置完成');
}, 3000);
}
function emergencyStop() {
updateSystemStatus('紧急停止', 'error');
addAlarmEntry('紧急停止已触发!请检查系统安全', 'error');
addLogEntry('紧急停止按钮已触发');
}
// 更新系统状态显示
function updateSystemStatus(text, className) {
document.getElementById('system-status').textContent = text;
const light = document.querySelector('.status-light');
light.className = 'status-light ' + className;
}
// 添加报警条目
function addAlarmEntry(message, type = 'warning') {
const alarmList = document.getElementById('alarm-list');
const time = getCurrentTime();
const alarmItem = document.createElement('div');
alarmItem.className = 'alarm-item ' + type;
const timeSpan = document.createElement('span');
timeSpan.className = 'alarm-time';
timeSpan.textContent = time;
const messageSpan = document.createElement('span');
messageSpan.className = 'alarm-message';
messageSpan.textContent = message;
alarmItem.appendChild(timeSpan);
alarmItem.appendChild(messageSpan);
alarmList.insertBefore(alarmItem, alarmList.firstChild);
// 更新报警计数
updateAlarmCount();
}
// 更新报警计数
function updateAlarmCount() {
const count = document.getElementById('alarm-list').children.length;
document.getElementById('alarm-count').textContent = count;
}
// 添加日志条目
function addLogEntry(message) {
const logList = document.getElementById('log-list');
const time = getCurrentTime();
const logItem = document.createElement('div');
logItem.className = 'log-item';
const timeSpan = document.createElement('span');
timeSpan.className = 'log-time';
timeSpan.textContent = time;
const messageSpan = document.createElement('span');
messageSpan.className = 'log-message';
messageSpan.textContent = message;
logItem.appendChild(timeSpan);
logItem.appendChild(messageSpan);
logList.insertBefore(logItem, logList.firstChild);
}
// 清除日志
function clearLogs() {
document.getElementById('log-list').innerHTML = '';
addLogEntry('日志已清除');
}
// 获取当前时间 (HH:MM:SS)
function getCurrentTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
// 获取随机值 (min与max之间)
function getRandomValue(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 启动数据模拟
function startDataSimulation() {
// 定期更新实时数据
setInterval(() => {
simulateRealtimeData();
}, 3000);
// 随机触发报警
setInterval(() => {
if (Math.random() < 0.3) {
simulateRandomAlarm();
}
}, 30000);
}
// 模拟实时数据更新
function simulateRealtimeData() {
const tempValue = (70 + Math.random() * 20).toFixed(1);
const pressureValue = (2 + Math.random() * 0.8).toFixed(1);
const flowValue = Math.floor(100 + Math.random() * 50);
const motorSpeed = Math.floor(1700 + Math.random() * 100);
document.getElementById('temp-value').textContent = tempValue + '°C';
document.getElementById('pressure-value').textContent = pressureValue + ' MPa';
document.getElementById('flow-value').textContent = flowValue + ' L/min';
document.getElementById('motor-speed').textContent = motorSpeed + ' RPM';
// 根据温度值决定状态
const tempStatus = document.querySelector('tr:nth-child(1) .status-dot');
if (tempValue > 80) {
tempStatus.className = 'status-dot warning';
// 如果温度过高,添加报警
if (tempValue > 85) {
addAlarmEntry(`温度传感器读数严重超过警戒值 (${tempValue}°C)`, 'error');
}
} else {
tempStatus.className = 'status-dot normal';
}
// 添加日志
addLogEntry('传感器数据已更新');
}
// 模拟随机报警
function simulateRandomAlarm() {
const alarms = [
'压力传感器读数波动异常',
'电机转速下降',
'通信延迟超出阈值',
'PLC数据缓存接近上限',
'输入模块2响应超时'
];
const randomAlarm = alarms[Math.floor(Math.random() * alarms.length)];
addAlarmEntry(randomAlarm);
}
// 添加一个CSS类用于旋转刷新图标
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.rotating {
animation: spin 0.8s linear;
}
`;
document.head.appendChild(style);
// Appsmith自定义组件加载完成时调用初始化函数
if (document.readyState === 'complete') {
initPLCControlPanel();
} else {
window.addEventListener('load', initPLCControlPanel);
}
})();