Commit e9aecf8a authored by wanghao's avatar wanghao

1 扫码绑定托盘,上料,机械臂整体测试

parent 9eb47dd7
package com.zehong.web.task; package com.zehong.web.task;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TEquipmentInfo; import com.zehong.system.domain.TEquipmentInfo;
import com.zehong.system.domain.modbus.ModbusDeviceData; import com.zehong.system.domain.modbus.ModbusDeviceData;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITEquipmentInfoService; import com.zehong.system.service.ITEquipmentInfoService;
import com.zehong.system.service.websocket.AgingCabinetWebSocketHandler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/** /**
* @author lenovo * @author lenovo
...@@ -25,6 +37,15 @@ public class AgingCabinetInspectionAndPowerCheckTask { ...@@ -25,6 +37,15 @@ public class AgingCabinetInspectionAndPowerCheckTask {
@Resource @Resource
private ITEquipmentInfoService tEquipmentInfoService; private ITEquipmentInfoService tEquipmentInfoService;
@Resource
private ApplicationEventPublisher eventPublisher; // 新增事件发布器
@Resource
private ITEquipmentAlarmDataService tEquipmentAlarmDataService;
@Resource
private AgingCabinetWebSocketHandler agingCabinetWebSocketHandler;
/** /**
* 五分钟一次 * 五分钟一次
* 1.老化柜、标定柜巡查 * 1.老化柜、标定柜巡查
...@@ -52,77 +73,135 @@ public class AgingCabinetInspectionAndPowerCheckTask { ...@@ -52,77 +73,135 @@ public class AgingCabinetInspectionAndPowerCheckTask {
type.add("2"); type.add("2");
List<TEquipmentInfo> equipmentInfos = tEquipmentInfoService.selectTEquipmentList(type); List<TEquipmentInfo> equipmentInfos = tEquipmentInfoService.selectTEquipmentList(type);
// try { TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
// if (equipmentInfos.size() == 0) {
// log.error("设备列表查询结果为空"); try {
// throw new Exception("无设备信息!"); if (equipmentInfos.size() == 0) {
// } log.error("设备列表查询结果为空");
// return;
// // 10 层 }
// List<Integer> registerOffset = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); List<CompletableFuture<TEquipmentInfo>> futures = equipmentInfos.stream().map(equipmentInfo -> CompletableFuture.supplyAsync(() -> {
// ModbusMaster master = null;
// List<CompletableFuture<ModbusDeviceData>> futures = equipmentInfos.stream().map(equipmentInfo -> CompletableFuture.supplyAsync(() -> { // 10 层
// ModbusMaster master = null; List<Integer> registerOffsets = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
// try { try {
// master = Modbus4jUtils.getMaster(equipmentInfo.getfIp(), equipmentInfo.getfPort()); master = Modbus4jUtils.getMaster(equipmentInfo.getfIp(), equipmentInfo.getfPort());
// Map<Integer, Object> integerObjectMap = Modbus4jUtils.batchReadAgingCabinetStatus(master, registerOffset); Map<Integer, Object> integerObjectMap = Modbus4jUtils.batchReadAgingCabinetStatus(master, registerOffsets);
// // 构造结果对象
// ModbusDeviceData deviceData = new ModbusDeviceData(); equipmentInfo.setRegisterValues(integerObjectMap);
// deviceData.setDeviceCode(equipmentInfo.getfEquipmentCode());
// deviceData.setRegisterValues(integerObjectMap.entrySet().stream() } catch (ModbusInitException e) {
// .collect(Collectors.toMap( // 记录异常数据
// Map.Entry::getKey, alarmData.setfAlarmType("01"); //01.老化柜 02.机械臂 03.老化层 04.点位
// e -> e.getValue() != null ? e.getValue().toString() : "NULL" alarmData.setfEquipmentCode(equipmentInfo.getfEquipmentCode());
// ))); alarmData.setfAlarmData("定时任务巡检:Modbus初始化失败");
// tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
// return deviceData; // 更新老化柜状态(“0”空闲,“1”运行,“2”故障)
// equipmentInfo.setfStatus("2");
// } catch (ModbusInitException e) { equipmentInfo.setErrorReason("定时任务巡检:Modbus初始化失败");
// equipmentInfo.setRegisterValues(new HashMap<>());
// // 初始化失败 // 返回结果
// Map<Integer, String> errorMap = new HashMap<>(); return equipmentInfo;
// // 层有错误返回 柜可能连不上也在这个报错里面
// ModbusDeviceData deviceData = new ModbusDeviceData(); } catch (ModbusTransportException e) {
// deviceData.setDeviceCode(""); // 网线没插好通讯不上
// deviceData.setRegisterValues(errorMap); if (e.getMessage().equals("java.net.SocketTimeoutException: connect timed out")) {
// // 记录异常数据
// return deviceData; alarmData.setfAlarmType("01"); //01.老化柜 02.机械臂 03.老化层 04.点位
// } catch (ModbusTransportException | ErrorResponseException e) { alarmData.setfEquipmentCode(equipmentInfo.getfEquipmentCode());
// // 返回错误信息 alarmData.setfAlarmData("定时任务巡检:老化柜网线没插好");
// Map<Integer, String> errorMap = new HashMap<>(); equipmentInfo.setErrorReason("定时任务巡检:老化柜网线没插好");
// registerOffset.forEach(offset -> errorMap.put(offset, "ERROR: " + e.getMessage())); // 线接错误
// } else {
// ModbusDeviceData deviceData = new ModbusDeviceData(); // 记录异常数据
// deviceData.setDeviceCode(equipmentInfo.getfEquipmentCode()); alarmData.setfAlarmType("01"); //01.老化柜 02.机械臂 03.老化层 04.点位
// deviceData.setRegisterValues(errorMap); alarmData.setfEquipmentCode(equipmentInfo.getfEquipmentCode());
// alarmData.setfAlarmData("定时任务巡检:老化柜通信线路没接好");
// return deviceData; equipmentInfo.setErrorReason("定时任务巡检:老化柜通信线路没接好");
// } finally { }
// if (master != null) { tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
// master.destroy(); // 更新老化柜状态(“0”空闲,“1”运行,“2”故障)
// } equipmentInfo.setRegisterValues(new HashMap<>());
// } equipmentInfo.setfStatus("2");
// }, executor)).collect(Collectors.toList());
// return equipmentInfo;
// // 等待所有任务完成并收集结果 } catch (ErrorResponseException e) {
// CompletableFuture<List<ModbusDeviceData>> listCompletableFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) equipmentInfo.setfStatus("2");
// .thenApply(v -> futures.stream() equipmentInfo.setRegisterValues(new HashMap<>());
// .map(CompletableFuture::join) equipmentInfo.setErrorReason("定时任务巡检:ErrorResponseException");
// .collect(Collectors.toList())); return equipmentInfo;
// } finally {
// DeferredResult<List<ModbusDeviceData>> deferredResult = new DeferredResult<>(); if (master != null) {
// master.destroy();
// listCompletableFuture.whenComplete((result, ex) -> { }
// if (ex != null) { }
// deferredResult.setErrorResult(Collections.singletonList(createErrorData(ex))); return equipmentInfo;
// } else { }, executor)).collect(Collectors.toList());
// deferredResult.setResult(result);
// } // 等待所有任务完成并收集结果
// }); CompletableFuture<List<TEquipmentInfo>> listCompletableFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
// .thenApply(v -> futures.stream()
// } catch (Exception e) { .map(CompletableFuture::join)
// e.printStackTrace(); .collect(Collectors.toList()));
// } // 处理 老化柜 是 运行 开始 空闲
CompletableFuture<List<TEquipmentInfo>> processedFuture = listCompletableFuture.thenApply(this::processDeviceData);
processedFuture.whenComplete((result, ex) -> {
tEquipmentInfoService.batchUpdate(result);
// 执行WebSocket广播,推送最新老化柜状态
if (result != null && !result.isEmpty()) {
agingCabinetWebSocketHandler.broadcastAgingCabinetData(result);
log.info("已通过WebSocket广播{}条老化柜状态数据", result.size());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
// 处理 老化柜 是 运行 开始 空闲
private List<TEquipmentInfo> processDeviceData(List<TEquipmentInfo> deviceDataList) {
for (TEquipmentInfo modbusDeviceData : deviceDataList) {
Map<Integer, Object> registerValues = modbusDeviceData.getRegisterValues();
// 大于0 说明 柜能通信;小于0 说明柜 不能通信
if (registerValues.size() > 0) {
boolean isRun = false;
for (Map.Entry<Integer, Object> entry : registerValues.entrySet()) {
Integer registerOffset = entry.getKey();
Object registerValue = entry.getValue();
if ("true".equals(registerValue)) {
eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
this,
modbusDeviceData.getfEquipmentCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset
));
isRun = true;
// 要给这个 层 发断电的 指令
} else {
// 发布断电指令事件(不再直接执行)
eventPublisher.publishEvent(new PowerOffCommandEvent(
this,
modbusDeviceData.getfEquipmentCode(),
modbusDeviceData.getfPowerOutageIp(),
modbusDeviceData.getfPowerOutagePort(),
registerOffset
));
}
}
if (isRun) {
modbusDeviceData.setfStatus("1");
} else {
modbusDeviceData.setfStatus("0");
}
} else {
modbusDeviceData.setfStatus("0");
}
}
return deviceDataList;
} }
private ModbusDeviceData createErrorData(Throwable ex) { private ModbusDeviceData createErrorData(Throwable ex) {
......
...@@ -108,6 +108,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter ...@@ -108,6 +108,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
// 对于 websocket 匿名访问 // 对于 websocket 匿名访问
.antMatchers("/ws-robot-arm").permitAll() .antMatchers("/ws-robot-arm").permitAll()
// 对于 websocket 匿名访问
.antMatchers("/ws-aging-cabinet").permitAll()
.antMatchers("/profile/**").anonymous() .antMatchers("/profile/**").anonymous()
.antMatchers("/common/download**").anonymous() .antMatchers("/common/download**").anonymous()
......
package com.zehong.system.domain; package com.zehong.system.domain;
import java.util.Date; import java.util.Date;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
...@@ -78,6 +80,11 @@ public class TEquipmentInfo extends BaseEntity ...@@ -78,6 +80,11 @@ public class TEquipmentInfo extends BaseEntity
@Excel(name = "状态:0空闲,1运行,2故障") @Excel(name = "状态:0空闲,1运行,2故障")
private String fStatus; private String fStatus;
/**
* 寄存器值
*/
private Map<Integer,Object> registerValues;
/** /**
* 故障原因 * 故障原因
*/ */
...@@ -263,6 +270,14 @@ public class TEquipmentInfo extends BaseEntity ...@@ -263,6 +270,14 @@ public class TEquipmentInfo extends BaseEntity
this.errorReason = errorReason; this.errorReason = errorReason;
} }
public Map<Integer, Object> getRegisterValues() {
return registerValues;
}
public void setRegisterValues(Map<Integer, Object> registerValues) {
this.registerValues = registerValues;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......
...@@ -39,6 +39,9 @@ public class TStoreyInfo extends BaseEntity ...@@ -39,6 +39,9 @@ public class TStoreyInfo extends BaseEntity
@Excel(name = "层编号") @Excel(name = "层编号")
private String fStoreyCode; private String fStoreyCode;
/** 托盘编号 */
private String fTrayCode;
/** IP地址 */ /** IP地址 */
@Excel(name = "IP地址") @Excel(name = "IP地址")
private String fIp; private String fIp;
...@@ -79,7 +82,15 @@ public class TStoreyInfo extends BaseEntity ...@@ -79,7 +82,15 @@ public class TStoreyInfo extends BaseEntity
@Excel(name = "报警时间", width = 30, dateFormat = "yyyy-MM-dd") @Excel(name = "报警时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date fAlarmTime; private Date fAlarmTime;
public void setfStoreyId(Long fStoreyId) public String getfTrayCode() {
return fTrayCode;
}
public void setfTrayCode(String fTrayCode) {
this.fTrayCode = fTrayCode;
}
public void setfStoreyId(Long fStoreyId)
{ {
this.fStoreyId = fStoreyId; this.fStoreyId = fStoreyId;
} }
......
...@@ -52,6 +52,8 @@ public interface TEquipmentInfoMapper ...@@ -52,6 +52,8 @@ public interface TEquipmentInfoMapper
*/ */
public int updateTEquipmentInfo(TEquipmentInfo tEquipmentInfo); public int updateTEquipmentInfo(TEquipmentInfo tEquipmentInfo);
public int batchUpdate(@Param("equipmentInfos") List<TEquipmentInfo> equipmentInfos);
/** /**
* 删除生产设备信息 * 删除生产设备信息
* *
......
...@@ -58,6 +58,8 @@ public interface ITEquipmentInfoService ...@@ -58,6 +58,8 @@ public interface ITEquipmentInfoService
*/ */
public int updateTEquipmentInfo(TEquipmentInfo tEquipmentInfo); public int updateTEquipmentInfo(TEquipmentInfo tEquipmentInfo);
public int batchUpdate(List<TEquipmentInfo> equipmentInfos);
/** /**
* 批量删除生产设备信息 * 批量删除生产设备信息
* *
......
...@@ -79,6 +79,8 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService ...@@ -79,6 +79,8 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
if (!loadingCommands.isEmpty()) { if (!loadingCommands.isEmpty()) {
boolean[] roboticArmEntryConveyorData = Modbus4jUtils.getRoboticArmEntryConveyorData(); boolean[] roboticArmEntryConveyorData = Modbus4jUtils.getRoboticArmEntryConveyorData();
log.info("机械臂入口 conveyor 0状态: " + roboticArmEntryConveyorData[0]);
log.info("机械臂入口 conveyor 1状态: " + roboticArmEntryConveyorData[1]);
if(roboticArmEntryConveyorData[1]) { if(roboticArmEntryConveyorData[1]) {
sendCommand(loadingCommands.get(0), "LOAD"); sendCommand(loadingCommands.get(0), "LOAD");
return; return;
...@@ -221,6 +223,10 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService ...@@ -221,6 +223,10 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
if(tStoreyInfo != null) { if(tStoreyInfo != null) {
robotArmCommand.setStatus("1"); robotArmCommand.setStatus("1");
robotArmCommand.setStoreyCode(tStoreyInfo.getfStoreyCode()); robotArmCommand.setStoreyCode(tStoreyInfo.getfStoreyCode());
tTrayInfo.setfStoreyCode(tStoreyInfo.getfStoreyCode());
tTrayInfo.setfBindingTime(new Date());
tTrayInfoMapper.updateTTrayInfo(tTrayInfo);
} else { } else {
robotArmCommand.setStoreyCode("待分配位置"); robotArmCommand.setStoreyCode("待分配位置");
} }
......
...@@ -4,7 +4,6 @@ import java.util.ArrayList; ...@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import com.sun.org.apache.xpath.internal.operations.Mod;
import com.zehong.system.domain.modbus.ModbusDeviceData; import com.zehong.system.domain.modbus.ModbusDeviceData;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.zehong.system.mapper.TEquipmentInfoMapper; import com.zehong.system.mapper.TEquipmentInfoMapper;
...@@ -91,6 +90,17 @@ public class TEquipmentInfoServiceImpl implements ITEquipmentInfoService ...@@ -91,6 +90,17 @@ public class TEquipmentInfoServiceImpl implements ITEquipmentInfoService
return tEquipmentInfoMapper.updateTEquipmentInfo(tEquipmentInfo); return tEquipmentInfoMapper.updateTEquipmentInfo(tEquipmentInfo);
} }
/**
* 批量修改生产设备信息
*
* @param equipmentInfos 生产设备信息集合
* @return 结果
*/
@Override
public int batchUpdate(List<TEquipmentInfo> equipmentInfos) {
return 0;
}
/** /**
* 批量删除生产设备信息 * 批量删除生产设备信息
* *
......
package com.zehong.system.service.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
/**
* @author lenovo
* @date 2025/8/14
* @description 老化柜WebSocket配置类
*/
@Configuration
@EnableWebSocket
public class AgingCabinetWebSocketConfig implements WebSocketConfigurer {
@Resource
private AgingCabinetWebSocketHandler agingCabinetWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册处理器,设置访问路径为/ws-aging-cabinet,允许所有跨域
registry.addHandler(agingCabinetWebSocketHandler, "/ws-aging-cabinet")
.setAllowedOrigins("*");
}
}
\ No newline at end of file
package com.zehong.system.service.websocket;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zehong.system.domain.TEquipmentInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author lenovo
* @date 2025/8/14
* @description 老化柜WebSocket处理器
*/
@Component
public class AgingCabinetWebSocketHandler extends TextWebSocketHandler {
// 线程安全的会话列表,存储所有连接的客户端
private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
private static final Logger log = LoggerFactory.getLogger(AgingCabinetWebSocketHandler.class);
/**
* 连接建立后添加会话
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
log.info("新的老化柜WebSocket连接建立,当前连接数: {}", sessions.size());
}
/**
* 连接关闭后移除会话
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session);
log.info("老化柜WebSocket连接关闭,当前连接数: {}", sessions.size());
}
/**
* 广播老化柜数据给所有客户端
* @param equipmentList 处理后的老化柜设备列表
*/
public void broadcastAgingCabinetData(List<TEquipmentInfo> equipmentList) {
try {
String message = createAgingCabinetMessage(equipmentList);
for (WebSocketSession session : sessions) {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
}
} catch (IOException e) {
log.error("广播老化柜数据失败", e);
}
}
/**
* 构建老化柜数据消息
* 转换为前端需要的格式(与cabinetRows所需字段匹配)
*/
private String createAgingCabinetMessage(List<TEquipmentInfo> equipmentList) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> cabinetData = new ArrayList<>();
for (TEquipmentInfo equipment : equipmentList) {
Map<String, Object> dataMap = new HashMap<>();
// 前端需要的核心字段
dataMap.put("fEquipmentId", equipment.getfEquipmentId());
dataMap.put("fEquipmentCode", equipment.getfEquipmentCode());
dataMap.put("fStatus", equipment.getfStatus());
dataMap.put("errorReason", equipment.getErrorReason());
dataMap.put("registerValues", equipment.getRegisterValues()); // 可选:层状态数据
cabinetData.add(dataMap);
}
Map<String, Object> message = new HashMap<>();
message.put("type", "aging_cabinet_data"); // 消息类型标识
message.put("data", cabinetData);
return mapper.writeValueAsString(message);
}
}
\ No newline at end of file
...@@ -129,6 +129,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -129,6 +129,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim> </trim>
where f_equipment_id = #{fEquipmentId} where f_equipment_id = #{fEquipmentId}
</update> </update>
<update id="batchUpdate" parameterType="list" >
<foreach collection="equipmentInfos" item="item" index="index" separator=";">
update t_equipment_info
<trim prefix="SET" suffixOverrides=",">
<if test="item.fStatus != null">f_status = #{item.fStatus},</if>
<if test="item.errorReason != null">f_error_Reason = #{item.errorReason},</if>
</trim>
where f_equipment_id = #{item.fEquipmentId}
</foreach>
</update>
<delete id="deleteTEquipmentInfoById" parameterType="Long"> <delete id="deleteTEquipmentInfoById" parameterType="Long">
delete from t_equipment_info where f_equipment_id = #{fEquipmentId} delete from t_equipment_info where f_equipment_id = #{fEquipmentId}
......
...@@ -8,6 +8,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -8,6 +8,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="fStoreyId" column="f_storey_id" /> <result property="fStoreyId" column="f_storey_id" />
<result property="fEquipmentId" column="f_equipment_id" /> <result property="fEquipmentId" column="f_equipment_id" />
<result property="fStoreyCode" column="f_storey_code" /> <result property="fStoreyCode" column="f_storey_code" />
<result property="fTrayCode" column="f_tray_code" />
<result property="fIp" column="f_ip" /> <result property="fIp" column="f_ip" />
<result property="fStatus" column="f_status" /> <result property="fStatus" column="f_status" />
<result property="fPort" column="f_port" /> <result property="fPort" column="f_port" />
...@@ -18,7 +19,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -18,7 +19,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectTStoreyInfoVo"> <sql id="selectTStoreyInfoVo">
select f_storey_id, f_equipment_id, f_storey_code, f_ip, f_status, f_port, f_aging_start_time, f_update_time, f_create_time, f_alarm_time from t_storey_info select storeyInfo.f_storey_id,
storeyInfo.f_equipment_id,
storeyInfo.f_storey_code,
storeyInfo.f_ip,
storeyInfo.f_status,
storeyInfo.f_port,
storeyInfo.f_aging_start_time,
storeyInfo.f_update_time,
storeyInfo.f_create_time,
storeyInfo.f_alarm_time,
trayInfo.f_tray_code
from t_storey_info storeyInfo
left join t_tray_info trayInfo on storeyInfo.f_storey_code = trayInfo.f_storey_code
</sql> </sql>
<select id="selectTStoreyInfoList" parameterType="TStoreyInfo" resultMap="TStoreyInfoResult"> <select id="selectTStoreyInfoList" parameterType="TStoreyInfo" resultMap="TStoreyInfoResult">
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
font-weight: bold;" font-weight: bold;"
@click.native="handleCardClick(item)" @click.native="handleCardClick(item)"
> >
{{ item.fEquipmentId + "号柜" }} {{ item.fEquipmentCode + "号柜" }}
</el-card> </el-card>
</el-tooltip> </el-tooltip>
</el-col> </el-col>
...@@ -68,52 +68,13 @@ ...@@ -68,52 +68,13 @@
</template> </template>
<script> <script>
import { getAgingCabinetAndPowerCheck } from "@/api/testScheduledTasks/testTasks";
import { getAllEquipmentList } from "@/api/equipment/equipment" import { getAllEquipmentList } from "@/api/equipment/equipment"
export default { export default {
name: "AgingCabinetBoard", name: "AgingCabinetBoard",
data() { data() {
return { return {
// 示例数据格式,实际从后端获取 // 示例数据格式,实际从后端获取
cabinets: [ cabinets: [],
// { id: 1, deviceStatus: '1' },
// { id: 2, deviceStatus: '2' },
// { id: 3, deviceStatus: '0' },
// { id: 4, deviceStatus: '0' },
// { id: 5, deviceStatus: '0' },
// { id: 6, deviceStatus: '0' },
// { id: 7, deviceStatus: '0' },
// { id: 8, deviceStatus: '0' },
// { id: 9, deviceStatus: '0' },
// { id: 10, deviceStatus: '0' },
// { id: 11, deviceStatus: '0' },
// { id: 12, deviceStatus: '0' },
// { id: 13, deviceStatus: '0' },
// { id: 14, deviceStatus: '0' },
// { id: 15, deviceStatus: '0' },
// { id: 16, deviceStatus: '0' },
// { id: 17, deviceStatus: '0' },
// { id: 18, deviceStatus: '0' },
// { id: 19, deviceStatus: '0' },
// { id: 20, deviceStatus: '0' },
// { id: 21, deviceStatus: '0' },
// { id: 22, deviceStatus: '0' },
// { id: 23, deviceStatus: '0' },
// { id: 24, deviceStatus: '0' },
// { id: 25, deviceStatus: '0' },
// { id: 26, deviceStatus: '0' },
// { id: 27, deviceStatus: '0' },
// { id: 28, deviceStatus: '0' },
// { id: 29, deviceStatus: '0' },
// { id: 30, deviceStatus: '0' },
// { id: 31, deviceStatus: '0' },
// { id: 32, deviceStatus: '0' },
// { id: 33, deviceStatus: '0' },
// { id: 34, deviceStatus: '0' },
// { id: 35, deviceStatus: '0' },
// { id: 36, deviceStatus: '0' },
// 共36个
],
// 状态对应的颜色类名 // 状态对应的颜色类名
statusMap: { statusMap: {
0: 'default', 0: 'default',
...@@ -138,8 +99,50 @@ export default { ...@@ -138,8 +99,50 @@ export default {
}, },
mounted() { mounted() {
this.testAgingCabinetAndPowerCheck(); this.testAgingCabinetAndPowerCheck();
this.initWebSocket(); // 初始化WebSocket连接
}, },
methods: { methods: {
// 初始化WebSocket连接
initWebSocket() {
// 根据当前页面协议选择ws/wss
const backendUrl = process.env.VUE_APP_API_BASE_URL || 'http://localhost:8080';
const wsUrl = backendUrl.replace('http', 'ws') + '/ws-aging-cabinet';
this.ws = new WebSocket(wsUrl);
// 连接建立
this.ws.onopen = () => {
console.log('老化柜WebSocket连接已建立');
this.pageLoading = false;
};
// 接收消息
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
// 只处理老化柜数据类型的消息
if (message.type === 'aging_cabinet_data') {
this.cabinets = message.data; // 更新数据,自动触发cabinetRows重新计算
console.log('收到老化柜更新数据,共', message.data.length, '条');
}
} catch (e) {
console.error('解析WebSocket消息失败', e);
}
};
// 连接关闭(自动重连)
this.ws.onclose = () => {
console.log('老化柜WebSocket连接关闭,3秒后尝试重连');
setTimeout(() => this.initWebSocket(), 3000);
};
// 连接错误
this.ws.onerror = (error) => {
console.error('老化柜WebSocket错误', error);
this.pageLoading = false;
};
},
handleCardClick(item) { handleCardClick(item) {
// 触发事件传递 cabinetId 给父组件 // 触发事件传递 cabinetId 给父组件
// 3 是 AgingLayer 组件的索引 // 3 是 AgingLayer 组件的索引
...@@ -159,15 +162,17 @@ export default { ...@@ -159,15 +162,17 @@ export default {
testAgingCabinetAndPowerCheck() { testAgingCabinetAndPowerCheck() {
// 开始加载:显示全局loading // 开始加载:显示全局loading
this.pageLoading = true; this.pageLoading = true;
// getAgingCabinetAndPowerCheck().then(response => {
// this.cabinets = response;
// this.pageLoading = false;
// });
getAllEquipmentList().then(response => { getAllEquipmentList().then(response => {
this.cabinets = response; this.cabinets = response;
this.pageLoading = false; this.pageLoading = false;
}) })
},
// 组件销毁时关闭WebSocket连接
beforeDestroy() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
} }
} }
}; };
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<!-- 头部标题区域 --> <!-- 头部标题区域 -->
<div class="tray-header"> <div class="tray-header">
<div class="tray-title"> <div class="tray-title">
<div class="title-text">{{ modbusDeviceData.id }}号柜</div> <div class="title-text">{{ modbusDeviceData.fEquipmentCode }}号柜</div>
<div class="title-line"></div> <div class="title-line"></div>
</div> </div>
</div> </div>
...@@ -30,7 +30,12 @@ ...@@ -30,7 +30,12 @@
:class="[ :class="[
'layer-depth', 'layer-depth',
`layer-${index}`, `layer-${index}`,
getStatusClass(layer.fStatus) // 动态状态类 {
'status-running': layer.fStatus === '1',
'status-fault': layer.fStatus === '2',
'status-idle': layer.fStatus === '0',
'status-power_outage': layer.fStatus === '3'
}
]" ]"
@click="selectLayer(index)" @click="selectLayer(index)"
> >
...@@ -51,18 +56,14 @@ ...@@ -51,18 +56,14 @@
<!-- 托盘信息展示区域 --> <!-- 托盘信息展示区域 -->
<div class="tray-header-inner"> <div class="tray-header-inner">
<span class="tray-label">托盘</span> <span class="tray-label">托盘</span>
<span class="tray-id">{{ modbusDeviceData.fTrayCode }}</span> <span class="tray-id">{{ trayInfo.fTrayCode }}</span>
</div> </div>
<div class="tray-info"> <div class="tray-info">
<div class="info-row"> <div class="info-row">
<div class="info-label">所在柜体:</div> <div class="info-label">所在柜体:</div>
<div class="info-value">{{ modbusDeviceData.id }}号柜 - {{ trayInfo.layer.split("-")[1] || '--' }}</div> <div class="info-value">{{ modbusDeviceData.fEquipmentCode }}号柜 - {{ trayInfo.layer ? trayInfo.layer.split("-")[1] : '--' }}</div>
</div> </div>
<!-- <div class="info-row">-->
<!-- <div class="info-label">产品型号:</div>-->
<!-- <div class="info-value">{{ trayInfo.productModel || '&#45;&#45;' }}</div>-->
<!-- </div>-->
<div class="info-row"> <div class="info-row">
<div class="info-label">当前状态:</div> <div class="info-label">当前状态:</div>
<div class="info-value" :class="trayInfo.statusClass || 'status-idle'"> <div class="info-value" :class="trayInfo.statusClass || 'status-idle'">
...@@ -136,8 +137,8 @@ export default { ...@@ -136,8 +137,8 @@ export default {
} }
}, },
mounted() { mounted() {
if (this.modbusDeviceData && this.modbusDeviceData.id) { if (this.modbusDeviceData && this.modbusDeviceData.fEquipmentId) {
this.loadCabinetData(this.modbusDeviceData.id); this.loadCabinetData(this.modbusDeviceData.fEquipmentId);
} }
}, },
beforeDestroy() { beforeDestroy() {
...@@ -331,27 +332,107 @@ export default { ...@@ -331,27 +332,107 @@ export default {
border-radius: 2px; border-radius: 2px;
} }
/* 状态背景色 */ /* 层状态样式 - 运行中 */
.status-idle { .status-running {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15), rgba(0, 0, 0, 0.2)); /* 运行中状态 - 绿色 #67c23a */
border-color: rgba(240, 240, 240, 1) !important; color: #67c23a;
border: 1px solid rgba(103, 194, 58, 0.8);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(103, 194, 58, 0.5);
} }
.status-running.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(103, 194, 58, 0.4)
rgba(103, 194, 58, 0.8)
rgba(103, 194, 58, 0.8)
rgba(103, 194, 58, 0.4);
}
.status-running:hover {
.status-running { background: linear-gradient(to bottom, rgba(103, 194, 58, 0.2), rgba(0,0,0,0.3));
background: linear-gradient(to bottom, rgba(100, 255, 100, 0.15), rgba(0, 80, 0, 0.2)) !important; box-shadow:
border-color: rgba(100, 255, 100, 0.8) !important; 0 4px 15px rgba(103, 194, 58, 0.4),
inset 0 0 10px rgba(103, 194, 58, 0.4);
} }
/* 层状态样式 - 故障 */
.status-fault { .status-fault {
background: linear-gradient(to bottom, rgba(255, 100, 100, 0.15), rgba(80, 0, 0, 0.2)) !important; /* 故障状态 - 红色 #f56c6c */
border-color: rgba(255, 100, 100, 0.8) !important; color: #f56c6c;
border: 1px solid rgba(245, 108, 108, 0.8);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(245, 108, 108, 0.5);
}
.status-fault.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(245, 108, 108, 0.4)
rgba(245, 108, 108, 0.8)
rgba(245, 108, 108, 0.8)
rgba(245, 108, 108, 0.4);
} }
.status-fault:hover {
background: linear-gradient(to bottom, rgba(245, 108, 108, 0.2), rgba(0,0,0,0.3));
box-shadow:
0 4px 15px rgba(245, 108, 108, 0.4),
inset 0 0 10px rgba(245, 108, 108, 0.4);
}
/* 层状态样式 - 空闲 */
.status-idle {
/* 空闲状态 - 浅灰 #f0f0f0 */
color: #f0f0f0;
border: 1px solid rgba(240, 240, 240, 0.8);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(240, 240, 240, 0.5);
}
.status-idle.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(240, 240, 240, 0.4)
rgba(240, 240, 240, 0.8)
rgba(240, 240, 240, 0.8)
rgba(240, 240, 240, 0.4);
}
.status-idle:hover {
background: linear-gradient(to bottom, rgba(240, 240, 240, 0.2), rgba(0,0,0,0.3));
box-shadow:
0 4px 15px rgba(240, 240, 240, 0.4),
inset 0 0 10px rgba(240, 240, 240, 0.4);
}
/* 层状态样式 - 断电 */
.status-power_outage { .status-power_outage {
background: linear-gradient(to bottom, rgba(150, 150, 150, 0.15), rgba(50, 50, 50, 0.2)) !important; color: #9e9e9e;
border-color: rgba(150, 150, 150, 0.8) !important; border: 1px solid rgba(158, 158, 158, 0.8);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(158, 158, 158, 0.5);
}
.status-power_outage.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(158, 158, 158, 0.4)
rgba(158, 158, 158, 0.8)
rgba(158, 158, 158, 0.8)
rgba(158, 158, 158, 0.4);
}
.status-power_outage:hover {
background: linear-gradient(to bottom, rgba(158, 158, 158, 0.2), rgba(0,0,0,0.3));
box-shadow:
0 4px 15px rgba(158, 158, 158, 0.4),
inset 0 0 10px rgba(158, 158, 158, 0.4);
} }
/* 层内容样式 */ /* 层内容样式 */
...@@ -368,11 +449,13 @@ export default { ...@@ -368,11 +449,13 @@ export default {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
margin-bottom: 4px; margin-bottom: 4px;
text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
} }
.layer-status { .layer-status {
font-size: 12px; font-size: 12px;
opacity: 0.9; opacity: 0.9;
text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
} }
/* 老化时间样式 */ /* 老化时间样式 */
...@@ -424,10 +507,6 @@ export default { ...@@ -424,10 +507,6 @@ export default {
.layer { .layer {
position: absolute; position: absolute;
left: 0; left: 0;
border: 1px solid rgba(100, 180, 255, 0.6);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.6),
inset 0 0 8px rgba(100, 200, 255, 0.3);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -438,22 +517,9 @@ export default { ...@@ -438,22 +517,9 @@ export default {
} }
.layer:hover { .layer:hover {
background: linear-gradient(to bottom, rgba(100, 180, 255, 0.2), rgba(0,0,0,0.3));
box-shadow:
0 4px 15px rgba(100, 200, 255, 0.4),
inset 0 0 10px rgba(100, 220, 255, 0.4);
transform: translateY(-2px); transform: translateY(-2px);
} }
.layer-depth {
border-width: 1px 3px 3px 1px;
border-color:
rgba(100, 180, 255, 0.4)
rgba(100, 180, 255, 0.8)
rgba(100, 180, 255, 0.8)
rgba(100, 180, 255, 0.4);
}
.right-content { .right-content {
flex: 1; flex: 1;
margin-left: 80px; margin-left: 80px;
...@@ -553,18 +619,19 @@ export default { ...@@ -553,18 +619,19 @@ export default {
color: #fff; color: #fff;
} }
/* 右侧状态文本样式 */
.status-idle { .status-idle {
color: #ffcc00; color: #f0f0f0;
font-weight: bold; font-weight: bold;
} }
.status-running { .status-running {
color: #64ffaa; color: #67c23a;
font-weight: bold; font-weight: bold;
} }
.status-fault { .status-fault {
color: #ff3d3d; color: #f56c6c;
font-weight: bold; font-weight: bold;
} }
...@@ -656,5 +723,4 @@ export default { ...@@ -656,5 +723,4 @@ export default {
color: #66b1ff; color: #66b1ff;
transform: translateX(-5px); transform: translateX(-5px);
} }
</style> </style>
...@@ -152,7 +152,7 @@ export default { ...@@ -152,7 +152,7 @@ export default {
name: 'RoboticArm', name: 'RoboticArm',
data() { data() {
return { return {
status: 'unknown', // idle, running, error status: 'idle', // idle, running, error
showAddDialog: false, showAddDialog: false,
trayCode: '', trayCode: '',
loadingCommands: [ loadingCommands: [
...@@ -235,7 +235,7 @@ export default { ...@@ -235,7 +235,7 @@ export default {
this.websocket.onopen = () => { this.websocket.onopen = () => {
console.log('机械臂指令WebSocket连接成功'); console.log('机械臂指令WebSocket连接成功');
this.status = 'unknown'; this.status = 'idle';
this.sendWebSocketMessage({ type: 'request', commands: ['loading', 'unloading'] }); this.sendWebSocketMessage({ type: 'request', commands: ['loading', 'unloading'] });
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment