// 查询堆垛机数据列表
function listDdj() {
request({
url: '/dev-api/dynamicMonitoring/list/ddjList',
method: 'get'
}).then(response => {
handleStackerDataSuccess(response)
}).catch(error => {
console.error('堆垛机数据获取失败:', error);
});
}
// 查询输送机数据列表
function listSsj() {
request({
url: '/dev-api/dynamicMonitoring/list/ssjList',
method: 'get'
}).then(response => {
handleConveyorDataSuccess(response)
}).catch(error => {
console.error('输送机数据获取失败:', error)
});
}
// 查询报警信息数据列表
function listAlarm() {
request({
url: '/dev-api/dynamicMonitoring/list/alarmList',
method: 'get'
}).then(response => {
console.log("报警信息:",response)
handleAlarmDataSuccess(response)
}).catch(error => {
handleAlarmDataError(response)
});
}
/**
* 2D可视化监控系统 - 数据同步和状态管理
* 负责配置同步、设备状态更新、实时数据获取等功能
*/
// ==================== 全局变量 ====================
/**
* 配置编辑模式标识
* @type {number} 1-编辑模式, 2-演示模式
*/
let configEditMode = 2;
// ==================== 配置同步功能 ====================
/**
* 获取系统配置并同步状态
* 从服务器获取最新配置,更新本地状态和UI
*/
function getConfig() {
$.ajax({
url: API_URLS.config,
type: 'GET',
dataType: 'json',
cache: false,
success: handleConfigSuccess,
error: handleConfigError
});
}
/**
* 处理配置获取成功
* @param {Object} data 配置数据
*/
function handleConfigSuccess(data) {
// 更新编辑模式状态
updateEditMode(data.editMode);
// 同步main.js中的全局配置
syncMainConfig(data);
// 更新组件拖拽状态
updateDraggableStatus(data.deviceDraggable);
}
/**
* 处理配置获取失败
* @param {Object} xhr XMLHttpRequest对象
* @param {string} status 状态信息
* @param {string} error 错误信息
*/
function handleConfigError(xhr, status, error) {
console.error('配置获取失败:', error);
}
/**
* 更新编辑模式状态
* @param {boolean} editMode 是否为编辑模式
*/
function updateEditMode(editMode) {
configEditMode = editMode ? 1 : 2;
const modeText = editMode ? '编辑模式' : '演示模式';
}
/**
* 同步main.js中的全局配置
* @param {Object} data 配置数据
*/
function syncMainConfig(data) {
if (!window.config) return;
// 更新所有配置项
window.config.deviceDraggable = data.deviceDraggable;
window.config.editMode = data.editMode;
window.config.allowBackendAccess = data.allowBackendAccess;
window.config.labels = data.labels || [];
window.config.conveyors = data.conveyors || [];
window.config.stackers = data.stackers || [];
window.config.tracks = data.tracks || [];
window.config.rollerConveyors = data.rollerConveyors || [];
window.config.titleSettings = data.titleSettings;
// 调用main.js中的更新函数
if (typeof window.updateDevicesDraggableState === 'function') {
window.updateDevicesDraggableState();
}
// 更新标签显示
if (typeof window.updateLabels === 'function') {
window.updateLabels();
}
// 更新页面标题样式
if (typeof window.updatePageTitleStyle === 'function' && data.titleSettings) {
window.updatePageTitleStyle(data.titleSettings);
}
}
// ==================== UI状态管理 ====================
/**
* 更新所有组件的拖拽状态
* 根据配置更新设备元素的鼠标样式和拖拽能力
* @param {boolean} isDraggable 是否允许拖拽
*/
function updateDraggableStatus(isDraggable) {
const draggableElements = document.querySelectorAll('.conveyor, .roller-conveyor, .stacker, .label');
draggableElements.forEach(element => {
// 根据元素类型设置不同的鼠标样式
const cursorStyle = isDraggable ?
(element.classList.contains('stacker') ? 'grab' : 'move') :
'default';
element.style.cursor = cursorStyle;
});
// 同步更新全局配置
if (window.config) {
window.config.deviceDraggable = isDraggable;
}
}
// ==================== 页面初始化 ====================
/**
* 页面加载完成后的初始化
*/
$(document).ready(function() {
// 立即执行一次数据获取
initializeData();
// 设置实时配置监听
setupConfigListeners();
// 启动定时器
startTimers();
});
/**
* 初始化数据获取
*/
function initializeData() {
getConfig();
updateConveyorStatus();
updateStackerStatus();
}
/**
* 设置配置变化监听器
*/
function setupConfigListeners() {
// 监听localStorage变化(about页面通知)
setupLocalStorageListener();
// 监听BroadcastChannel消息(跨标签页通信)
setupBroadcastChannelListener();
}
/**
* 设置localStorage监听器
*/
function setupLocalStorageListener() {
let lastRefreshTime = localStorage.getItem('refreshIndex');
setInterval(() => {
const currentRefreshTime = localStorage.getItem('refreshIndex');
if (currentRefreshTime && currentRefreshTime !== lastRefreshTime) {
lastRefreshTime = currentRefreshTime;
getConfig();
}
}, 100);
}
/**
* 设置BroadcastChannel监听器
*/
function setupBroadcastChannelListener() {
if (!window.BroadcastChannel) return;
const broadcastChannel = new BroadcastChannel('index_refresh_channel');
broadcastChannel.onmessage = (event) => {
if (event.data.refresh) {
getConfig();
}
};
}
// 报警更新定时器ID
let alarmUpdateTimer = null;
/**
* 启动定时器
*/
function startTimers() {
// 设备状态更新定时器
setInterval(updateConveyorStatus, 5000);
setInterval(updateStackerStatus, 5000);
setInterval(updateRgvStatus, 5000);
// 配置同步定时器
// setInterval(getConfig, 1000);
// 启动智能报警更新定时器
startSmartAlarmTimer();
}
/**
* 启动报警系统
*/
function startSmartAlarmTimer() {
// 立即执行一次
updateAlarmStatus();
console.log('报警系统已启动');
}
/**
* 安排下次报警更新
* @param {number} delay 延迟时间(毫秒)
*/
function scheduleNextAlarmUpdate(delay) {
// 清除现有定时器
if (alarmUpdateTimer) {
clearTimeout(alarmUpdateTimer);
}
alarmUpdateTimer = setTimeout(() => {
updateAlarmStatus();
// 递归安排下次更新
scheduleNextAlarmUpdate(3000);
}, delay);
}
// ==================== 设备状态更新 ====================
/**
* 更新输送机状态
* 从服务器获取输送机实时数据并更新UI显示
*/
function updateConveyorStatus() {
// 检查是否允许访问后台数据
if (!canAccessBackendData()) {
return;
}
listSsj()
}
/**
* 处理输送机数据获取成功
* @param {Object} response 服务器响应数据
*/
function handleConveyorDataSuccess(response) {
if (configEditMode !== 2) {
return;
}
const conveyors = document.querySelectorAll('.conveyor, .roller-conveyor');
conveyors.forEach(conveyor => {
const conveyorId = conveyor.getAttribute('id');
const deviceData = response.data.find(item => item.id === conveyorId);
if (deviceData) {
updateConveyorDisplay(conveyor, deviceData);
}
});
}
/**
* 更新单个输送机的显示状态
* @param {HTMLElement} conveyor 输送机DOM元素
* @param {Object} data 设备数据
*/
function updateConveyorDisplay(conveyor, data) {
// 更新中心方块显示状态
const centerBlock = conveyor.querySelector('.conveyor-center, .roller-conveyor-center');
if (centerBlock) {
centerBlock.style.display = data.dw === '1' ? 'block' : 'none';
}
// 更新设备状态指示(故障闪烁)
const hasError = parseInt(data.sbzt) > 3;
conveyor.classList.toggle('breathing-border', hasError);
}
// ==================== 辅助函数 ====================
/**
* 检查是否允许访问后台数据
* @returns {boolean} 是否允许访问
*/
function canAccessBackendData() {
if (window.config && window.config.allowBackendAccess === false) {
return false;
}
return true;
}
/**
* 验证服务器响应数据是否有效
* @param {Object} response 服务器响应
* @returns {boolean} 数据是否有效
*/
function isValidResponse(response) {
return response && response.code === 0 && response.data;
}
/**
* 更新堆垛机状态
* 从服务器获取堆垛机实时数据并更新UI显示
*/
function updateStackerStatus() {
// 检查是否允许访问后台数据
if (!canAccessBackendData()) {
return;
}
listDdj()
}
/**
* 处理堆垛机数据获取成功
* @param {Object} response 服务器响应数据
*/
function handleStackerDataSuccess(response) {
if (response.data.length==0 || configEditMode != 2) {
return;
}
const stackers = document.querySelectorAll('.stacker');
stackers.forEach(stacker => {
const stackerId = stacker.getAttribute('id');
const deviceData = response.data.find(item => item.id === stackerId);
if (deviceData) {
updateStackerDisplay(stacker, deviceData);
}
});
}
/**
* 更新单个堆垛机的显示状态
* @param {HTMLElement} stacker 堆垛机DOM元素
* @param {Object} data 设备数据
*/
function updateStackerDisplay(stacker, data) {
const centerBlock = stacker.querySelector('.stacker-center');
const executionPhase = data.zxjd;
// 根据执行阶段更新堆垛机状态
switch(executionPhase) {
case '1': // 移动到原位置,隐藏货物
updateStackerPosition(stacker, data.ywz, false);
break;
case '2': // 移动到原位置,显示货物
updateStackerPosition(stacker, data.ywz, true);
break;
case '3': // 移动到目标位置,显示货物
updateStackerPosition(stacker, data.mbwz, true);
break;
case '4':
case '5': // 移动到目标位置,隐藏货物
updateStackerPosition(stacker, data.mbwz, false);
break;
}
// 处理堆垛机报警状态(sbzt > 1000 时显示红光)
updateStackerAlarmStatus(stacker, data.sbzt);
}
/**
* 更新堆垛机报警状态
* @param {HTMLElement} stacker 堆垛机元素
* @param {string|number} sbzt 设备状态值
*/
function updateStackerAlarmStatus(stacker, sbzt) {
const statusValue = parseInt(sbzt);
if (statusValue > 1000) {
// 添加报警样式
stacker.classList.add('breathing-border');
console.log(`堆垛机 ${stacker.getAttribute('id')} 报警,状态值: ${statusValue}`);
} else {
// 移除报警样式
stacker.classList.remove('breathing-border');
}
}
/**
* 更新堆垛机位置和货物显示
* @param {HTMLElement} stacker 堆垛机元素
* @param {string} position 目标位置
* @param {boolean} showCargo 是否显示货物
*/
function updateStackerPosition(stacker, position, showCargo) {
const centerBlock = stacker.querySelector('.stacker-center');
// 更新货物显示状态
if (centerBlock) {
centerBlock.style.display = showCargo ? 'block' : 'none';
}
// 计算并移动到目标位置
const targetPosition = parseInt(position);
if (!isNaN(targetPosition)) {
// 获取配置信息以确定小方块总数
const boxesPerRow = window.config ? window.config.boxesPerRow : 60;
if (targetPosition === 0) {
// 位置为0时,移动到第一个小方块左侧
moveStackerToColumn(stacker, -1);
} else if (targetPosition === boxesPerRow + 1) {
// 位置为小方块数量+1时,移动到最后一个小方块右侧
moveStackerToColumn(stacker, boxesPerRow);
} else if (targetPosition > 0) {
// 正常位置处理
moveStackerToColumn(stacker, targetPosition - 1);
}
}
}
/**
* 移动堆垛机到指定列
* @param {HTMLElement} stacker 堆垛机元素
* @param {number} columnIndex 目标列索引
*/
function moveStackerToColumn(stacker, columnIndex) {
// 获取堆垛机的ID和对应的货架ID
const stackerId = stacker.getAttribute('id');
const stackerConfig = window.config.stackers.find(s => s.id === stackerId);
if (!stackerConfig || !stackerConfig.shelfId) {
console.warn(`堆垛机 ${stackerId} 未找到对应的货架ID`);
return;
}
// 获取对应行的配置
const rowConfig = window.config.boxRows.find(row => row.id === stackerConfig.shelfId);
if (!rowConfig) {
console.warn(`未找到货架ID ${stackerConfig.shelfId} 对应的方块行配置`);
return;
}
// 获取该行的方块数量
const boxesPerRow = rowConfig.boxCount;
let finalX;
if (columnIndex === -1) {
// 位置为0时,移动到第一个小方块左侧一个方块距离
const firstBox = document.querySelector(`.small-box[data-row="${stackerConfig.shelfId}"][data-column="0"]`);
if (!firstBox) {
console.warn(`未找到货架 ${stackerConfig.shelfId} 的第一个方块`);
return;
}
const firstBoxX = parseInt(firstBox.style.left);
const boxSize = parseInt(firstBox.style.width);
const stackerWidth = parseInt(stacker.style.width);
const centerOffset = (stackerWidth - boxSize) / 2;
finalX = firstBoxX - boxSize - centerOffset; // 向左偏移一个方块距离
} else if (columnIndex === boxesPerRow) {
// 位置为小方块数量+1时,移动到最后一个小方块右侧一个方块距离
const lastBox = document.querySelector(`.small-box[data-row="${stackerConfig.shelfId}"][data-column="${boxesPerRow - 1}"]`);
if (!lastBox) {
console.warn(`未找到货架 ${stackerConfig.shelfId} 的最后一个方块`);
return;
}
const lastBoxX = parseInt(lastBox.style.left);
const boxSize = parseInt(lastBox.style.width);
const stackerWidth = parseInt(stacker.style.width);
const centerOffset = (stackerWidth - boxSize) / 2;
finalX = lastBoxX + boxSize - centerOffset; // 向右偏移一个方块距离
} else {
// 正常位置处理
const targetBox = document.querySelector(`.small-box[data-row="${stackerConfig.shelfId}"][data-column="${columnIndex}"]`);
if (!targetBox) {
console.warn(`未找到货架 ${stackerConfig.shelfId} 的第 ${columnIndex} 个方块`);
return;
}
const targetX = parseInt(targetBox.style.left);
const stackerWidth = parseInt(stacker.style.width);
const boxSize = parseInt(targetBox.style.width);
const centerOffset = (stackerWidth - boxSize) / 2;
finalX = targetX - centerOffset;
}
// 添加平滑移动动画
stacker.style.transition = 'left 2s ease-in-out';
stacker.style.left = finalX + 'px';
}
// ==================== RGV状态管理 ====================
/**
* 更新RGV状态
*/
function updateRgvStatus() {
// 检查是否允许访问后台数据
if (!canAccessBackendData()) {
return;
}
$.ajax({
url: API_URLS.rgv,
type: 'GET',
dataType: 'json',
success: handleRgvDataSuccess,
error: (xhr, status, error) => console.error('RGV数据获取失败:', error)
});
}
/**
* 处理RGV数据获取成功
* @param {Object} response 服务器响应数据
*/
function handleRgvDataSuccess(response) {
if (!isValidResponse(response) || configEditMode !== 2) {
return;
}
const rgvs = document.querySelectorAll('.rgv');
rgvs.forEach(rgv => {
const rgvId = rgv.getAttribute('id');
const deviceData = response.data.find(item => item.id === rgvId);
if (deviceData) {
updateRgvDisplay(rgv, deviceData);
}
});
}
/**
* 更新RGV显示状态
* @param {HTMLElement} rgv RGV元素
* @param {Object} data RGV数据
*/
function updateRgvDisplay(rgv, data) {
const centerBlock = rgv.querySelector('.rgv-center');
const executionPhase = data.zxjd;
// 根据执行阶段更新RGV状态
switch(executionPhase) {
case '1': // 移动到原位置,隐藏货物
updateRgvPosition(rgv, data.ywz, false);
break;
case '2': // 移动到原位置,显示货物
updateRgvPosition(rgv, data.ywz, true);
break;
case '3': // 移动到目标位置,显示货物
updateRgvPosition(rgv, data.mbwz, true);
break;
case '4':
case '5': // 移动到目标位置,隐藏货物
updateRgvPosition(rgv, data.mbwz, false);
break;
}
// 处理RGV报警状态(sbzt > 1000 时显示红光)
updateRgvAlarmStatus(rgv, data.sbzt);
}
/**
* 更新RGV报警状态
* @param {HTMLElement} rgv RGV元素
* @param {string|number} sbzt 设备状态值
*/
function updateRgvAlarmStatus(rgv, sbzt) {
const statusValue = parseInt(sbzt);
if (statusValue > 1000) {
// 添加报警样式
rgv.classList.add('breathing-border');
console.log(`RGV ${rgv.getAttribute('id')} 报警,状态值: ${statusValue}`);
} else {
// 移除报警样式
rgv.classList.remove('breathing-border');
}
}
/**
* 更新RGV位置和货物显示
* @param {HTMLElement} rgv RGV元素
* @param {string} conveyorId 目标输送机ID
* @param {boolean} showCargo 是否显示货物
*/
function updateRgvPosition(rgv, conveyorId, showCargo) {
const centerBlock = rgv.querySelector('.rgv-center');
// 更新货物显示状态
if (centerBlock) {
centerBlock.style.display = showCargo ? 'block' : 'none';
}
// 移动到目标输送机位置
if (conveyorId) {
moveRgvToConveyor(rgv, conveyorId);
}
}
/**
* 移动RGV到指定输送机位置
* @param {HTMLElement} rgv RGV元素
* @param {string} conveyorId 目标输送机ID
*/
function moveRgvToConveyor(rgv, conveyorId) {
const targetConveyor = document.getElementById(conveyorId);
if (!targetConveyor) {
console.warn(`未找到输送机: ${conveyorId}`);
return;
}
// 获取输送机的位置和尺寸
const conveyorX = parseInt(targetConveyor.style.left);
const conveyorY = parseInt(targetConveyor.style.top);
const conveyorWidth = parseInt(targetConveyor.style.width);
const conveyorHeight = parseInt(targetConveyor.style.height);
// 获取RGV的尺寸
const rgvWidth = parseInt(rgv.style.width);
const rgvHeight = parseInt(rgv.style.height);
// 计算RGV的目标位置(只调整y轴与输送机对齐,x轴保持不变)
const currentX = parseInt(rgv.style.left); // 保持当前x坐标
const targetY = conveyorY + (conveyorHeight - rgvHeight) / 2;
// 添加平滑移动动画(只动y轴)
rgv.style.transition = 'top 2s ease-in-out';
rgv.style.top = targetY + 'px';
console.log(`RGV ${rgv.getAttribute('id')} 移动到输送机 ${conveyorId} Y轴位置: ${targetY}px (X轴保持: ${currentX}px)`);
}
// 注意:此处的重复代码已被上面的优化函数替代
// 旧的堆垛机处理逻辑已移至 updateStackerDisplay 等函数中
// 定时器设置已移至 startTimers 函数中
// ==================== 设备信息弹框功能 ====================
/**
* 显示设备信息弹框
* @param {string} deviceType 设备类型
* @param {string} deviceId 设备ID
* @param {Object} deviceConfig 设备配置信息
*/
function showDeviceInfo(deviceType, deviceId, deviceConfig) {
const box = document.getElementById('box');
const detailsDiv = document.getElementById('device-details');
if (!box || !detailsDiv) {
console.error('设备信息弹框元素未找到');
return;
}
// 获取设备实时数据
getDeviceRealTimeData(deviceType, deviceId).then(realTimeData => {
// 构建设备信息HTML
const deviceInfo = buildDeviceInfoHTML(deviceType, deviceId, deviceConfig, realTimeData);
detailsDiv.innerHTML = deviceInfo;
// 显示弹框
box.style.display = 'block';
}).catch(error => {
console.error('获取设备实时数据失败:', error);
// 即使获取实时数据失败,也显示基本配置信息
const deviceInfo = buildDeviceInfoHTML(deviceType, deviceId, deviceConfig, null);
detailsDiv.innerHTML = deviceInfo;
box.style.display = 'block';
});
}
/**
* 获取设备实时数据
* @param {string} deviceType 设备类型
* @param {string} deviceId 设备ID
* @returns {Promise} 设备实时数据
*/
function getDeviceRealTimeData(deviceType, deviceId) {
return new Promise((resolve, reject) => {
if (deviceType === 'stacker') {
// 获取堆垛机实时数据 - 使用配置的URL
request({
url: '/dev-api/dynamicMonitoring/list/ddjList',
method: 'get'
}).then(response => {
if (response && response.data) {
const deviceData = response.data.find(item => item.id === deviceId);
resolve(deviceData || null);
} else {
resolve(null);
}
}).catch(error => {
console.error('获取堆垛机数据失败:', error);
reject('获取堆垛机数据失败');
});
} else if (deviceType === 'conveyor' || deviceType === 'rollerConveyor') {
// 获取输送机实时数据 - 使用配置的URL
request({
url: '/dev-api/dynamicMonitoring/list/ssjList',
method: 'get'
}).then(response => {
if (response && response.data) {
const deviceData = response.data.find(item => item.id === deviceId);
resolve(deviceData || null);
} else {
resolve(null);
}
}).catch(error => {
console.error('获取输送机数据失败:', error);
reject('获取输送机数据失败');
});
} else if (deviceType === 'rgv') {
// 获取RGV实时数据 - 使用配置的URL
$.ajax({
url: API_URLS.rgv,
type: 'GET',
dataType: 'json',
success: function(response) {
if (response && response.data) {
const deviceData = response.data.find(item => item.id === deviceId);
resolve(deviceData || null);
} else {
resolve(null);
}
},
error: function(xhr, status, error) {
console.error('获取RGV数据失败:', error);
reject('获取RGV数据失败');
}
});
} else {
// 其他设备类型暂无实时数据
resolve(null);
}
});
}
function ssjSbzt(a){
if (a==0){
return"未知"
}else if(a==1){
return "手动"
}else if(a==2){
return "自动"
}else if (a==3){
return "运行"
}else if (a==4){
return "输送超时"
}else if(a==5){
return "断路器跳闸"
}else if (a==6){
return "变频器报警"
}else if (a==7){
return "开关任务异常"
}else if (a==8){
return "急停"
}else if (a==9){
return "超界"
}else if(a==10){
return "外型异常"
}else if(a==11){
return "称重异常"
}else if(a==12){
return "扫码异常"
}else {
return a;
}
}
function ddjSbzt(a){
if (a<1000){
return "正常"
}else if(a==1031){
return "急停按下"
}else if (a==1032){
return "货叉不在中位"
}else if (a==1033){
return "运行变频器报警"
}else if (a==1034){
return "升降变频器报警"
}else if (a==1035){
return "货叉变频器报警"
}else if (a==1037){
return "运行限位故障"
}else if(a==1038){
return "升降限位故障"
}else if (a==1039){
return "运行不能纠偏"
}else if (a==1040){
return "升降不能纠偏"
}else if (a==1041){
return "水平测距出错"
}else if (a==1042){
return "垂直BPS出错"
}else if (a==1043){
return "超速保护"
}else if (a==1044){
return "前边超宽"
}else if (a==1045){
return "后边超宽"
}else if (a==1046){
return "左边歪斜"
}else if (a==1047){
return "右边歪斜"
}else if (a==1048){
return "超高1"
}else if (a==1049){
return "超高2"
}else if (a==1050){
return "放货完毕货叉有货"
}else if (a==1051){
return "取货时货叉有货"
}else if (a==1052){
return "放货目标有货"
}else if (a==1053){
return "取货完毕无货"
}else if (a==1054){
return "放货时货叉无货"
}else if (a==1055){
return "货叉动作超时"
}else if (a==1056){
return "货叉不能纠偏"
}else if (a==1057){
return "微升降超时"
}else if (a==1058){
return "松绳过载"
}else if (a==1059){
return "设定超限"
}else if (a==1062){
return "入库输送机任务号不符"
}else if (a==1063){
return "安全门打开"
}else if (a==1064){
return "与安全门通讯中断"
}else {
return a
}
}
/**
* 构建设备信息HTML
* @param {string} deviceType 设备类型
* @param {string} deviceId 设备ID
* @param {Object} deviceConfig 设备配置信息
* @param {Object} realTimeData 实时数据
* @returns {string} HTML字符串
*/
function buildDeviceInfoHTML(deviceType, deviceId, deviceConfig, realTimeData) {
const deviceTypeNames = {
'conveyor': '链条输送机',
'rollerConveyor': '滚筒输送机',
'stacker': '堆垛机',
'rgv': 'RGV',
'track': '堆垛机轨道'
};
const typeName = deviceTypeNames[deviceType] || '未知设备';
// 使用后台数据中的ID,如果没有实时数据则使用配置ID
const displayId = realTimeData ? realTimeData.id : deviceId;
// 设备类型图标
const deviceIcons = {
'conveyor': '
',
'rollerConveyor': '
',
'stacker': '
',
'rgv': '
' };
const deviceIcon = deviceIcons[deviceType] || '⚙️';
let html = `