Commit 3efab788 authored by wanghao's avatar wanghao

1 定时任务上料指令处理

parent c3484161
......@@ -33,7 +33,7 @@ public class RobotArmCommand extends BaseEntity
private String type;
/** 状态:0-待执行;1-执行中;2-执行结束(上料就是绑定托盘,下料就是解绑托盘) */
@Excel(name = "状态:0-待执行;1-执行中;2-未上电,3-执行结束(上料就是绑定托盘,下料就是解绑托盘)")
@Excel(name = "状态:0-待分配位置,1-待执行;2-执行中;3-未上电;4-执行结束(上料就是绑定托盘,下料就是解绑托盘)")
private String status;
/** 指令开始执行时间 */
......
......@@ -58,12 +58,12 @@ public interface RobotArmCommandMapper
int updateExecutingToCompleted();
/**
* 获取待执行的上料指令
* 获取待执行的上料指令 0-待分配位置,1-待执行;2-执行中;3-未上电;4-执行结束
*/
List<RobotArmCommand> selectPendingLoadingCommands();
/**
* 获取待执行的下料指令
* 获取待执行的下料指令 0-待分配位置,1-待执行;2-执行中;3-未上电;4-执行结束
*/
List<RobotArmCommand> selectPendingUnloadingCommands();
......
......@@ -19,6 +19,8 @@ public interface TTrayInfoMapper
*/
public TTrayInfo selectTTrayInfoById(Long fTrayId);
public TTrayInfo selectTTrayInfoByCode(String code);
/**
* 查询托盘信息列表
*
......
......@@ -167,6 +167,42 @@ public class Modbus4jUtils {
return value;
}
/**
* 机械臂入口传送带 0位置是 投入点,1位置是 末尾点
* @param slaveId
* @param registerOffsets
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
*/
public static boolean[] getRoboticArmEntryConveyorData() throws ModbusTransportException, ModbusInitException {
ModbusMaster master = getMaster("192.168.2.11", 502);
boolean[] booleans = readDiscreteInputs(master, 1, 0, 2);
if(master != null) {
master.destroy();
}
return booleans;
}
/**
* 机械臂出库传送带 0位置是 投入点,1位置是 末尾点
* @param slaveId
* @param registerOffsets
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
*/
public static boolean[] getRoboticArmExitConveyorData() throws ModbusTransportException, ModbusInitException {
ModbusMaster master = getMaster("192.168.2.12", 502);
boolean[] booleans = readDiscreteInputs(master, 1, 0, 2);
if(master != null) {
master.destroy();
}
return booleans;
}
/**
* 读取离散输入(功能码02)的兼容方法
* 使用ReadDiscreteInputsRequest/Response,适配更多版本
......
......@@ -224,19 +224,6 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
}
private void handleLongIdleState() {
// 检查是否有超时的指令
long now = System.currentTimeMillis();
Iterator<Map.Entry<SocketAddress, CommandExecution>> it = currentCommands.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<SocketAddress, CommandExecution> entry = it.next();
CommandExecution exec = entry.getValue();
// 5分钟超时
if (now - exec.startTime > 30000) {
robotArmCommandService.markCommandTimeout(exec.commandId);
it.remove();
log.warn("指令超时: {}", exec.commandId);
}
}
// 如果当前没有执行中的指令,尝试处理待执行指令
if (currentCommands.isEmpty()) {
......
......@@ -62,12 +62,8 @@ public interface IRobotArmCommandService
*/
public int deleteRobotArmCommandById(Long robotArmCommandId);
public void processIdleState();
void completeCommand(Long commandId);
void markCommandTimeout(Long commandId);
public void processPendingCommands();
public void completeCommand(String trayCode,String storeyCode);
}
......@@ -7,8 +7,11 @@ import java.net.UnknownHostException;
import java.util.Date;
import java.util.List;
import com.zehong.common.utils.DateUtils;
import com.zehong.common.utils.StringUtils;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.domain.TTrayInfo;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.mapper.TTrayInfoMapper;
import com.zehong.system.netty.handler.NettyUdpServerHandler;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import com.zehong.system.udp.UdpCommandSender;
......@@ -46,6 +49,9 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
@Resource
private TStoreyInfoMapper storeyInfoMapper;
@Resource
private TTrayInfoMapper tTrayInfoMapper;
@Resource
private UdpCommandSender udpCommandSender;
@Value("${robot.arm.udp.ip}")
......@@ -85,7 +91,7 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
private void sendCommand(RobotArmCommand command, String commandType) {
// 更新状态为执行中
command.setStatus("1");
command.setStatus("2");
command.setStartExecutionTime(new Date());
robotArmCommandMapper.updateRobotArmCommand(command);
......@@ -110,8 +116,21 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
@Transactional
public void completeCommand(Long commandId) {
RobotArmCommand command = robotArmCommandMapper.selectRobotArmCommandById(commandId);
if (command != null && "1".equals(command.getStatus())) {
if (command != null && "2".equals(command.getStatus())) {
// 发送上电指令
try {
// 发送上电指令给机械臂
String udpMessage = String.format("POWER_ON,%s,%s", command.getTrayCode(), command.getStoreyCode());
udpCommandSender.sendCommandThrowError(udpMessage);
command.setStatus("4");
log.info("指令执行成功,状态设置为4");
} catch (Exception e) {
command.setStatus("3");
log.info("执行指令时发生异常: " + e.getMessage());
}
command.setEndExecutionTime(new Date());
robotArmCommandMapper.updateRobotArmCommand(command);
......@@ -124,8 +143,8 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
@Transactional
public void markCommandTimeout(Long commandId) {
RobotArmCommand command = robotArmCommandMapper.selectRobotArmCommandById(commandId);
if (command != null && "1".equals(command.getStatus())) {
command.setStatus("3"); // 标记为未上电
if (command != null && "2".equals(command.getStatus())) {
command.setStatus("5"); // 执行异常;先保留此参数
robotArmCommandMapper.updateRobotArmCommand(command);
// 通过WebSocket广播更新
......@@ -135,53 +154,6 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
}
}
@Override
@Transactional
public void completeCommand(String trayCode, String storeyCode) {
// 1. 查找对应的执行中指令
RobotArmCommand command = robotArmCommandMapper.findExecutingCommand(trayCode, storeyCode);
if (command == null) {
log.warn("未找到对应的执行中指令: {} @ {}", trayCode, storeyCode);
return;
}
// 2. 更新状态为已完成
command.setStatus("3");
command.setEndExecutionTime(new Date());
robotArmCommandMapper.updateRobotArmCommand(command);
log.info("指令完成: {} @ {}", trayCode, storeyCode);
// 3. 广播指令更新
robotArmWebSocketHandler.broadcastCommandUpdate();
}
private void sendLoadingCommand(RobotArmCommand command) {
// 更新状态为执行中
command.setStatus("1");
command.setStartExecutionTime(new Date());
robotArmCommandMapper.updateRobotArmCommand(command);
notifyCommandsUpdate();
// 发送UDP指令
String udpMessage = String.format("LOAD,%s,%s", command.getTrayCode(), command.getStoreyCode());
udpCommandSender.sendCommand(udpMessage);
}
private void sendUnloadingCommand(RobotArmCommand command) {
// 更新状态为执行中
command.setStatus("1");
command.setStartExecutionTime(new Date());
robotArmCommandMapper.updateRobotArmCommand(command);
notifyCommandsUpdate();
// 发送UDP指令
String udpMessage = String.format("UNLOAD,%s,%s", command.getTrayCode(), command.getStoreyCode());
udpCommandSender.sendCommand(udpMessage);
}
/**
* 查询机械臂指令
*
......@@ -222,11 +194,22 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
{
robotArmCommand.setCreateTime(DateUtils.getNowDate());
if(StringUtils.isBlank(robotArmCommand.getTrayCode())) {
throw new RuntimeException("托盘编号不能为空");
}
TTrayInfo tTrayInfo = tTrayInfoMapper.selectTTrayInfoByCode(robotArmCommand.getTrayCode());
if(!"0".equals(tTrayInfo.getfStatus())) {
throw new RuntimeException("托盘未解绑,请联系管理员");
}
TStoreyInfo tStoreyInfo = storeyInfoMapper.selectNearestFreeStorey();
if(tStoreyInfo != null) {
robotArmCommand.setStatus("1");
robotArmCommand.setStoreyCode(tStoreyInfo.getfStoreyCode());
} else {
robotArmCommand.setStoreyCode("无空闲老化层");
robotArmCommand.setStoreyCode("待分配位置");
}
int i = robotArmCommandMapper.insertRobotArmCommand(robotArmCommand);
......@@ -242,12 +225,12 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
throw new RuntimeException("指令不存在");
}
if (!"2".equals(command.getStatus())) {
if (!"3".equals(command.getStatus())) {
throw new RuntimeException("只有未上电状态的指令才能执行上电操作");
}
// 更新状态为执行中
command.setStatus("1");
// 上完电 此条指令就结束了
command.setStatus("4");
robotArmCommandMapper.updateRobotArmCommand(command);
// 发送上电指令给机械臂
......@@ -256,6 +239,7 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
// 记录操作日志
log.info("执行上电操作: 指令ID={}, 托盘={}, 位置={}", commandId, command.getTrayCode(), command.getStoreyCode());
notifyCommandsUpdate();
}
/**
......@@ -300,28 +284,6 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
return i;
}
@Override
@Transactional
public void processIdleState() {
// 1. 更新执行中状态为完成状态
robotArmCommandMapper.updateExecutingToCompleted();
// 2. 优先处理上料指令
List<RobotArmCommand> loadingCommands = robotArmCommandMapper.selectPendingLoadingCommands();
if (!loadingCommands.isEmpty()) {
RobotArmCommand command = loadingCommands.get(0);
sendLoadingCommand(command);
return;
}
// 3. 处理下料指令
List<RobotArmCommand> unloadingCommands = robotArmCommandMapper.selectPendingUnloadingCommands();
if (!unloadingCommands.isEmpty()) {
RobotArmCommand command = unloadingCommands.get(0);
sendUnloadingCommand(command);
}
}
private void notifyCommandsUpdate() {
robotArmWebSocketHandler.broadcastCommandUpdate();
}
......
......@@ -77,6 +77,15 @@ public class UdpCommandSender {
sendCommand(address, message);
}
public void sendCommandThrowError(String message) throws IOException {
if(socket == null || address == null){
throw new RuntimeException("UDP命令发送器未初始化,无法发送消息");
}
byte[] buffer = message.getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, robotArmPort);
socket.send(packet);
}
public void sendCommand(String message) {
if (socket == null || address == null) {
log.error("UDP命令发送器未初始化,无法发送消息");
......
......@@ -21,7 +21,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="findByType" parameterType="string" resultMap="RobotArmCommandResult">
<include refid="selectRobotArmCommandVo"/>
where f_type = #{type} and f_status != '3'
where f_type = #{type} and f_status != '4'
</select>
<select id="selectRobotArmCommandList" parameterType="RobotArmCommand" resultMap="RobotArmCommandResult">
......@@ -112,7 +112,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<!-- 获取待执行的上料指令 -->
<select id="selectPendingLoadingCommands" resultMap="RobotArmCommandResult">
<include refid="selectRobotArmCommandVo"/>
WHERE f_type = '0' AND f_status = '0'
WHERE f_type = '0' AND f_status = '1'
ORDER BY f_create_time ASC
LIMIT 1
</select>
......@@ -120,7 +120,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<!-- 获取待执行的下料指令 -->
<select id="selectPendingUnloadingCommands" resultMap="RobotArmCommandResult">
<include refid="selectRobotArmCommandVo"/>
WHERE f_type = '1' AND f_status = '0'
WHERE f_type = '1' AND f_status = '1'
ORDER BY f_create_time ASC
LIMIT 1
</select>
......
......@@ -34,6 +34,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectTTrayInfoVo"/>
where f_tray_id = #{fTrayId}
</select>
<select id="selectTTrayInfoByCode" parameterType="string" resultMap="TTrayInfoResult">
<include refid="selectTTrayInfoVo"/>
where f_tray_code = #{fTrayCode}
</select>
<insert id="insertTTrayInfo" parameterType="TTrayInfo" useGeneratedKeys="true" keyProperty="fTrayId">
insert into t_tray_info
......
......@@ -242,7 +242,6 @@ export default {
this.websocket.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
console.log('收到WebSocket消息:', message);
if (message.type === 'loading') {
this.loadingCommands = message.data.map(cmd => ({
robotArmCommandId: cmd.robotArmCommandId,
......@@ -342,28 +341,31 @@ export default {
})
},
getCommandStatusClass(status) {
return {
'status-pending': status == '0', // 待执行
'status-executing': status == '1', // 执行中
'status-poweroff': status == '2', // 未上电
'status-completed': status == '3' // 执行结束
};
},
// 更新状态文本映射
getStatusText(status) {
const statusMap = {
'0': '待执行',
'1': '执行中',
'2': '未上电',
'3': '执行结束'
'0': '待分配位置',
'1': '待执行',
'2': '执行中',
'3': '未上电',
'4': '执行结束'
};
return statusMap[status] || '未知状态';
},
// 更新状态样式类映射
getCommandStatusClass(status) {
return {
'status-0': status == '0', // 待分配位置
'status-1': status == '1', // 待执行
'status-2': status == '2', // 执行中
'status-3': status == '3', // 未上电
'status-4': status == '4' // 执行结束
};
},
handleCommandClick(cmd) {
// 只有未上电状态的指令才可点击
if (cmd.status === '2') {
if (cmd.status === '3') {
this.selectedCommand = cmd;
this.showPowerOnDialog = true;
}
......@@ -381,9 +383,6 @@ export default {
if(res.code === 200) {
this.msgSuccess("上电操作成功");
// 更新本地指令状态
this.updateCommandStatus(this.selectedCommand.robotArmCommandId, '1');
this.closePowerOnDialog();
} else {
this.msgError("上电操作失败");
......@@ -392,21 +391,6 @@ export default {
this.msgError("上电操作失败");
});
},
updateCommandStatus(commandId, newStatus) {
// 更新上料指令
const loadingIndex = this.loadingCommands.findIndex(c => c.robotArmCommandId === commandId);
if (loadingIndex !== -1) {
this.loadingCommands[loadingIndex].status = newStatus;
return;
}
// 更新下料指令
const unloadingIndex = this.unloadingCommands.findIndex(c => c.robotArmCommandId === commandId);
if (unloadingIndex !== -1) {
this.unloadingCommands[unloadingIndex].status = newStatus;
}
}
}
};
</script>
......@@ -1006,30 +990,61 @@ export default {
min-width: auto;
}
}
/* 指令状态样式 */
.command-item.status-pending {
/* 指令状态样式(核心修改部分) */
.command-item.status-0 {
/* 待分配位置:紫蓝色调 */
background: rgba(50, 50, 100, 0.3);
border: 1px solid rgba(160, 160, 255, 0.5);
}
.command-item.status-1 {
/* 待执行:天蓝色调(保留原待执行样式基础) */
background: rgba(0, 40, 80, 0.3);
border: 1px solid rgba(100, 180, 255, 0.3);
border: 1px solid rgba(100, 200, 255, 0.5);
}
.command-item.status-executing {
.command-item.status-2 {
/* 执行中:青绿色调,带动态效果 */
background: rgba(0, 80, 40, 0.3);
border: 1px solid rgba(100, 255, 180, 0.5);
animation: pulse-green 1.5s infinite;
border: 1px solid rgba(100, 255, 170, 0.5);
animation: pulse-green 1.5s infinite; /* 保持原执行中动画 */
}
.command-item.status-poweroff {
.command-item.status-3 {
/* 未上电:红色调,带提醒效果 */
background: rgba(80, 0, 0, 0.3);
border: 1px solid rgba(255, 100, 100, 0.5);
cursor: pointer;
animation: blink-red 1s infinite;
animation: blink-red 1s infinite; /* 保持原未上电动画 */
}
.command-item.status-completed {
.command-item.status-4 {
/* 执行结束:灰色调,无动态效果 */
background: rgba(60, 60, 60, 0.3);
border: 1px solid rgba(180, 180, 180, 0.5);
}
/* 状态文本颜色(核心修改部分) */
.status-0 .cmd-status {
color: #a0a0ff; /* 待分配位置文本色 */
}
.status-1 .cmd-status {
color: #64c8ff; /* 待执行文本色 */
}
.status-2 .cmd-status {
color: #64ffaa; /* 执行中文本色 */
}
.status-3 .cmd-status {
color: #ff6464; /* 未上电文本色 */
}
.status-4 .cmd-status {
color: #a0a0a0; /* 执行结束文本色 */
}
/* 状态文本样式 */
.cmd-status {
font-size: 12px;
......
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