Commit 4198ca5c authored by wanghao's avatar wanghao

1 机械臂 可以 实时设置 有指令时是否执行的操作。

2 下料 时 柜子断电 导致 指令不执行 调整,把 断电的的柜子的 指令 设置成 断电失败的状态,等柜子修好后继续。
parent 81db12e6
......@@ -138,6 +138,27 @@ public class RobotArmCommandController extends BaseController
return toAjax(robotArmCommandService.updateInstructionExecutionPriority(priority));
}
/**
* 控制机械臂
* @param action
* @return
*/
@GetMapping("/updateAnimationState/{action}")
public AjaxResult updateAnimationState(@PathVariable("action") String action) {
return toAjax(robotArmCommandService.updateAnimationState(action));
}
/**
* 短电已修复
* @param robotArmCommandId r
* @return r
*/
@GetMapping("/powerFailureResolved/{robotArmCommandId}")
public AjaxResult powerFailureResolved(@PathVariable("robotArmCommandId") Long robotArmCommandId) {
return robotArmCommandService.powerFailureResolved(robotArmCommandId);
}
/**
* 删除机械臂指令
*/
......
......@@ -8,7 +8,7 @@ spring:
master:
url: jdbc:mysql://localhost:3306/zh-mes-device-db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
username: root
password: zh@123456
password: Zh@123456
# 从库数据源
slave:
# 从数据源开关/默认关闭
......@@ -102,4 +102,4 @@ robot:
arm:
udp:
ip: 127.0.0.1
port: 6000
\ No newline at end of file
port: 6000
......@@ -25,8 +25,8 @@ spring:
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: test
profiles:
active: dev
# 文件上传
servlet:
multipart:
......@@ -48,7 +48,7 @@ token:
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 3000000
# MyBatis配置
mybatis:
# 搜索指定包别名
......@@ -59,11 +59,11 @@ mybatis:
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
params: count=countSql
# Swagger配置
swagger:
......@@ -73,7 +73,7 @@ swagger:
pathMapping: /dev-api
# 防止XSS攻击
xss:
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
......
......@@ -36,7 +36,7 @@ public class RobotArmCommand extends BaseEntity
private String type;
/** 状态:0-待执行;1-执行中;2-执行结束(上料就是绑定托盘,下料就是解绑托盘) */
@Excel(name = "状态:0-待分配位置,1-待执行;2-执行中;3-未上电;4-执行结束(上料就是绑定托盘,下料就是解绑托盘)")
@Excel(name = "状态:0-待分配位置,1-待执行;2-执行中;3-未上电;4-执行结束(上料就是绑定托盘,下料就是解绑托盘): 5-断电失败")
private String status;
/** 指令开始执行时间 */
......
......@@ -43,6 +43,8 @@ public interface RobotArmCommandMapper
public List<RobotArmCommand> selectIsRunningCommandByTrayCode(String trayCode);
public void updateStatusPowerOffFailed(String storeyCode);
/**
* 新增机械臂指令
*
......
......@@ -241,7 +241,9 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
// 完成指令
robotArmCommandService.completeCommand(commandIdToComplete);
log.info("指令完成: {}", commandIdToComplete);
} catch (Exception e) {
log.error("指令完成失败: {}", commandIdToComplete, e);
} finally {
// 清空当前指令
currentCommandId = null;
......@@ -252,9 +254,6 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
synchronized (this) {
handleFullyIdleState();
}
} catch (Exception e) {
log.error("指令完成失败: {}", commandIdToComplete, e);
commandState.set(CommandState.ERROR);
}
}
}
......
......@@ -69,6 +69,10 @@ public interface IRobotArmCommandService
public int updateInstructionExecutionPriority(String priority);
public int updateAnimationState(String action);
public AjaxResult powerFailureResolved(Long robotArmCommandId);
/**
* 批量删除机械臂指令
*
......
......@@ -105,52 +105,62 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
if (cacheObject != null) {
priority = (String) cacheObject;
}
// 合并查询:同时获取上料、下料各1条待执行指令
List<RobotArmCommand> pendingCommands = robotArmCommandMapper.selectPendingLoadUnloadCommands();
String feedConveyorIpAndPort = iConveyorBeltIpMaintainService.selectConfigByKey(Constants.FEED_CONVEYOR_IP_AND_PORT);
String feedIp ;
int feedPort ;
String outLetBeltIpAndPort = iConveyorBeltIpMaintainService.selectConfigByKey(Constants.OUT_LET_BELT_IP_AND_PORT);
String outLetBeltIp;
int outLetBeltPort;
// 区分上料、下料指令(根据f_type)
RobotArmCommand loadingCommand = null;
RobotArmCommand unloadingCommand = null;
for (RobotArmCommand command : pendingCommands) {
if ("0".equals(command.getType())) { // 上料指令(type=0)
loadingCommand = command;
} else if ("1".equals(command.getType())) { // 下料指令(type=1)
unloadingCommand = command;
}
Object cacheObject1 = redisCache.getCacheObject("sys_config:robotAction");
String action = "start";
if (cacheObject1 != null) {
action = (String) cacheObject1;
}
if(StringUtils.isNotBlank(feedConveyorIpAndPort) && StringUtils.isNotBlank(outLetBeltIpAndPort)) {
String[] splitFeed = feedConveyorIpAndPort.split(":");
feedIp = splitFeed[0];
feedPort = Integer.parseInt(splitFeed[1]);
if("start".equals(action)) {
// 合并查询:同时获取上料、下料各1条待执行指令
List<RobotArmCommand> pendingCommands = robotArmCommandMapper.selectPendingLoadUnloadCommands();
String feedConveyorIpAndPort = iConveyorBeltIpMaintainService.selectConfigByKey(Constants.FEED_CONVEYOR_IP_AND_PORT);
String feedIp ;
int feedPort ;
String outLetBeltIpAndPort = iConveyorBeltIpMaintainService.selectConfigByKey(Constants.OUT_LET_BELT_IP_AND_PORT);
String outLetBeltIp;
int outLetBeltPort;
// 区分上料、下料指令(根据f_type)
RobotArmCommand loadingCommand = null;
RobotArmCommand unloadingCommand = null;
for (RobotArmCommand command : pendingCommands) {
if ("0".equals(command.getType())) { // 上料指令(type=0)
loadingCommand = command;
} else if ("1".equals(command.getType())) { // 下料指令(type=1)
unloadingCommand = command;
}
}
if(StringUtils.isNotBlank(feedConveyorIpAndPort) && StringUtils.isNotBlank(outLetBeltIpAndPort)) {
String[] splitOut = outLetBeltIpAndPort.split(":");
outLetBeltIp = splitOut[0];
outLetBeltPort = Integer.parseInt(splitOut[1]);
String[] splitFeed = feedConveyorIpAndPort.split(":");
feedIp = splitFeed[0];
feedPort = Integer.parseInt(splitFeed[1]);
if((loadingCommand != null && "loading".equals(priority)) ||
(unloadingCommand == null && loadingCommand != null && "unloading".equals(priority))) {
// 传送带检测先去掉
boolean[] roboticArmEntryConveyorData = Modbus4jUtils.getRoboticArmExitConveyorData(feedIp,feedPort);
String[] splitOut = outLetBeltIpAndPort.split(":");
outLetBeltIp = splitOut[0];
outLetBeltPort = Integer.parseInt(splitOut[1]);
log.info("机械臂入口 conveyor 0状态: " + roboticArmEntryConveyorData[0]);
log.info("机械臂入口 conveyor 1状态: " + roboticArmEntryConveyorData[1]);
if(roboticArmEntryConveyorData[1]) {
sendCommand(loadingCommand, "LOAD");
}
} else if(unloadingCommand != null) {
boolean[] roboticArmExitConveyorData = Modbus4jUtils.getRoboticArmExitConveyorData(outLetBeltIp,outLetBeltPort);
log.info("机械臂出口 conveyor 0状态: " + roboticArmExitConveyorData[0]);
log.info("机械臂出口 conveyor 1状态: " + roboticArmExitConveyorData[1]);
if(!roboticArmExitConveyorData[0]) {
log.info("开始处理下料指令: {}", unloadingCommand);
sendCommand(unloadingCommand, "UNLOAD");
if((loadingCommand != null && "loading".equals(priority)) ||
(unloadingCommand == null && loadingCommand != null && "unloading".equals(priority))) {
// 传送带检测先去掉
boolean[] roboticArmEntryConveyorData = Modbus4jUtils.getRoboticArmExitConveyorData(feedIp,feedPort);
log.info("机械臂入口 conveyor 0状态: " + roboticArmEntryConveyorData[0]);
log.info("机械臂入口 conveyor 1状态: " + roboticArmEntryConveyorData[1]);
if(roboticArmEntryConveyorData[1]) {
sendCommand(loadingCommand, "LOAD");
}
} else if(unloadingCommand != null) {
boolean[] roboticArmExitConveyorData = Modbus4jUtils.getRoboticArmExitConveyorData(outLetBeltIp,outLetBeltPort);
log.info("机械臂出口 conveyor 0状态: " + roboticArmExitConveyorData[0]);
log.info("机械臂出口 conveyor 1状态: " + roboticArmExitConveyorData[1]);
if(!roboticArmExitConveyorData[0]) {
log.info("开始处理下料指令: {}", unloadingCommand);
sendCommand(unloadingCommand, "UNLOAD");
}
}
}
}
......@@ -659,6 +669,41 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
return 1;
}
/**
* 修改动画状态
*
* @param action 动作
* @return 结果
*/
@Override
public int updateAnimationState(String action) {
redisCache.setCacheObject("sys_config:robotAction", action);
return 1;
}
/**
* 恢复电源故障
*
* @param robotArmCommandId r
* @return r
*/
@Override
public AjaxResult powerFailureResolved(Long robotArmCommandId) {
if(robotArmCommandId == null) {
return AjaxResult.error("参数错误");
}
RobotArmCommand robotArmCommand = new RobotArmCommand();
robotArmCommand.setRobotArmCommandId(robotArmCommandId);
robotArmCommand.setStatus("1");
robotArmCommandMapper.updateRobotArmCommand(robotArmCommand);
notifyCommandsUpdate();
return AjaxResult.success();
}
/**
* 批量删除机械臂指令
*
......
......@@ -9,13 +9,12 @@ import com.zehong.system.domain.SysConfig;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.domain.TTrayInfo;
import com.zehong.system.mapper.RobotArmCommandMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.mapper.TTrayInfoMapper;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ISysConfigService;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService;
import com.zehong.system.service.ITTrayInfoService;
import com.zehong.system.service.*;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -54,6 +53,11 @@ public class AllCommandHandler {
@Autowired
private ISysConfigService configService;
@Resource
private RobotArmCommandMapper robotArmCommandMapper;
@Resource
private RobotArmWebSocketHandler robotArmWebSocketHandler;
// 使用常量定义索引,避免魔法数字
private static final int AGING_STAGE_INDEX = 3;
private static final int DEFAULT_HOURS = 72;
......@@ -140,8 +144,8 @@ public class AllCommandHandler {
@Async // 异步执行
@EventListener(PowerOffCommandEvent.class)
public void handlePowerOffCommand(PowerOffCommandEvent event) {
String storeyCode = event.getDeviceCode() + "-" + event.getLayer();
try {
String storeyCode = event.getDeviceCode() + "-" + event.getLayer();
String ip = event.getIp();
int port = event.getPort();
int registerOffset = event.getRegisterOffset();
......@@ -169,6 +173,11 @@ public class AllCommandHandler {
} catch (ModbusInitException | ModbusTransportException e) {
log.error("断电指令执行失败 - 设备:{} 层:{}", event.getDeviceCode(), event.getLayer(), e);
//把这个指令状态 改成 断电失败,不影响下面的指令执行
robotArmCommandMapper.updateStatusPowerOffFailed(storeyCode);
// 通过WebSocket广播更新
robotArmWebSocketHandler.broadcastCommandUpdate();
// 记录异常
TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
alarmData.setfAlarmType("03"); // 老化层
......
......@@ -37,6 +37,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectRobotArmCommandVo"/>
where f_tray_code = #{trayCode} and f_status != '4'
</select>
<update id="updateStatusPowerOffFailed" parameterType="string">
update t_robot_arm_command
set f_status = '5'
where f_storey_code = #{storeyCode} and f_status = '2'
</update>
<select id="selectRobotArmCommandList" parameterType="RobotArmCommand" resultMap="RobotArmCommandResult">
<include refid="selectRobotArmCommandVo"/>
......
......@@ -112,6 +112,14 @@ export function delCommand(robotArmCommandId) {
})
}
export function powerFailureResolved(robotArmCommandId) {
return request({
url: '/robotArm/command/powerFailureResolved/' + robotArmCommandId,
method: 'get'
})
}
// 导出机械臂指令
export function exportCommand(query) {
return request({
......@@ -120,3 +128,10 @@ export function exportCommand(query) {
params: query
})
}
export function updateAnimationState(action) {
return request({
url: '/robotArm/command/updateAnimationState/' + action,
method: 'get'
})
}
......@@ -2,21 +2,16 @@
<div class="robotic-arm-panel">
<!-- 标题区域 -->
<div class="panel-title">
<!-- 左侧标题+状态指示灯组合 -->
<div class="title-with-status">
<div class="title-left">
<div class="title-text">机械臂</div>
<div class="title-line"></div>
</div>
<!-- 状态指示灯:紧挨着标题文字右侧 -->
<div class="status-indicator">
<div class="status-light" :class="statusClass"></div>
<div class="status-text">{{ statusText }}</div>
</div>
</div>
<!-- 上料按钮 -->
<div class="title-right">
<button class="add-button" @click="openAddModeDialog">
<i class="el-icon-plus"></i> 上料
......@@ -38,16 +33,12 @@
<div class="dialog-content">
<div class="mode-selection">
<div class="mode-option" @click="selectMode('auto')">
<div class="mode-icon">
<i class="el-icon-video-play"></i>
</div>
<div class="mode-icon"><i class="el-icon-video-play"></i></div>
<div class="mode-title">自动模式</div>
<div class="mode-desc">系统自动分配位置</div>
</div>
<div class="mode-option" @click="selectMode('manual')">
<div class="mode-icon">
<i class="el-icon-s-operation"></i>
</div>
<div class="mode-icon"><i class="el-icon-s-operation"></i></div>
<div class="mode-title">手动模式</div>
<div class="mode-desc">手动指定层编号</div>
</div>
......@@ -68,23 +59,13 @@
<div class="dialog-content">
<div class="scan-prompt">请扫描托盘二维码</div>
<div class="mode-indicator">自动模式 - 系统自动分配位置</div>
<div class="input-group">
<label for="trayCode">标检单号:</label>
<input
type="text"
v-model="productStandardInspectionNumber"
placeholder="手动输入或扫码标检单号"
ref="productStandardInspectionNumberInput"
>
<input type="text" v-model="productStandardInspectionNumber" placeholder="手动输入或扫码标检单号" ref="productStandardInspectionNumberInput">
</div>
<div class="input-group">
<label for="trayCode">托盘编号:</label>
<input
type="text"
v-model="trayCode"
placeholder="手动输入或扫码"
>
<input type="text" v-model="trayCode" placeholder="手动输入或扫码">
</div>
</div>
</div>
......@@ -103,36 +84,18 @@
<div class="dialog-content">
<div class="scan-prompt">请输入上料信息</div>
<div class="mode-indicator">手动模式 - 需要指定层编号</div>
<div class="manual-inputs">
<div class="input-group">
<label for="trayCode">标检单号:</label>
<input
type="text"
v-model="productStandardInspectionNumber"
placeholder="手动输入或扫码标检单号"
ref="manualStandardInput"
>
<input type="text" v-model="productStandardInspectionNumber" placeholder="手动输入或扫码标检单号" ref="manualStandardInput">
</div>
<div class="input-group">
<label for="trayCode">托盘编号:</label>
<input
id="trayCode"
type="text"
v-model="manualTrayCode"
placeholder="输入托盘编号"
>
<input id="trayCode" type="text" v-model="manualTrayCode" placeholder="输入托盘编号">
</div>
<div class="input-group">
<label for="storeyCode">层编号:</label>
<input
id="storeyCode"
type="text"
v-model="storeyCode"
placeholder="输入层编号(如: 1-1, 12-3)"
>
<input id="storeyCode" type="text" v-model="storeyCode" placeholder="输入层编号(如: 1-1, 12-3)">
</div>
</div>
</div>
......@@ -150,13 +113,7 @@
<div class="loading-command">
<div class="command-title">待上料指令</div>
<div class="command-list">
<div
v-for="(cmd, index) in loadingCommands"
:key="index"
class="command-item"
:class="getCommandStatusClass(cmd.status)"
@click="handleCommandClick(cmd)"
>
<div v-for="(cmd, index) in loadingCommands" :key="index" class="command-item" :class="getCommandStatusClass(cmd.status)" @click="handleCommandClick(cmd)">
<div class="cmd-info">
<div class="cmd-tray">托盘: {{ cmd.trayCode }}</div>
<div class="cmd-position">位置: {{ cmd.position }}</div>
......@@ -166,35 +123,23 @@
</div>
</div>
<!-- 中间:机械臂主体 -->
<!-- 中间:机械臂主体(添加双击事件和暂停类) -->
<div class="arm-center-wrapper">
<!-- 优先级控制区域 -->
<div class="priority-control">
<div class="priority-display">{{ priorityDisplayText }}</div>
<button class="priority-toggle-btn" @click="togglePriority">
点击切换
</button>
<button class="priority-toggle-btn" @click="togglePriority">点击切换</button>
</div>
<div class="robotic-arm-container">
<div class="robotic-arm-container" @dblclick="toggleAnimation" :class="{ 'paused-animation': !isAnimating }">
<div class="robotic-arm">
<!-- 机械臂各部件 -->
<div class="arm-base">
<div class="base-top"></div>
<div class="base-bottom"></div>
</div>
<div class="arm-joint joint-1">
<div class="joint-body"></div>
</div>
<div class="arm-segment segment-1">
<div class="segment-body"></div>
</div>
<div class="arm-joint joint-2">
<div class="joint-body"></div>
</div>
<div class="arm-segment segment-2">
<div class="segment-body"></div>
</div>
<div class="arm-joint joint-1"><div class="joint-body"></div></div>
<div class="arm-segment segment-1"><div class="segment-body"></div></div>
<div class="arm-joint joint-2"><div class="joint-body"></div></div>
<div class="arm-segment segment-2"><div class="segment-body"></div></div>
<div class="arm-gripper">
<div class="gripper-left"></div>
<div class="gripper-right"></div>
......@@ -207,13 +152,7 @@
<div class="unloading-command">
<div class="command-title">待下料指令</div>
<div class="command-list">
<div
v-for="(cmd, index) in unloadingCommands"
:key="index"
class="command-item"
:class="getCommandStatusClass(cmd.status)"
@click="handleCommandClick(cmd)"
>
<div v-for="(cmd, index) in unloadingCommands" :key="index" class="command-item" :class="getCommandStatusClass(cmd.status)" @click="handleCommandClick(cmd)">
<div class="cmd-info">
<div class="cmd-tray">托盘: {{ cmd.trayCode }}</div>
<div class="cmd-position">位置: {{ cmd.position }}</div>
......@@ -245,7 +184,7 @@
</div>
<!-- 确认执行完成对话框 -->
<div class="dialog-mask" v-if="showSureCompleteDialog" @click.self="closePowerOnDialog">
<div class="dialog-mask" v-if="showSureCompleteDialog" @click.self="closeSureCompleteDialog">
<div class="dialog-container">
<div class="dialog-header">确认执行完成</div>
<div class="dialog-body">
......@@ -263,11 +202,32 @@
</div>
</div>
</div>
<!-- 新增:断电失败修复对话框(状态5) -->
<div class="dialog-mask" v-if="showFixDialog" @click.self="closeFixDialog">
<div class="dialog-container">
<div class="dialog-header">断电失败</div>
<div class="dialog-body">
<div class="dialog-content">
<div class="scan-prompt">该指令断电失败,是否已修复?</div>
<div class="power-on-info">
<div><span class="label">托盘编号:</span> {{ selectedFailedCommand ? selectedFailedCommand.trayCode : '' }}</div>
<div><span class="label">位置:</span> {{ selectedFailedCommand ? selectedFailedCommand.position : '' }}</div>
</div>
</div>
</div>
<div class="dialog-footer">
<button class="cancel-button" @click="closeFixDialog">取消</button>
<button class="confirm-button" @click="handleFixConfirm">是,已修复</button>
</div>
</div>
</div>
</div>
</template>
<script>
import {addCommand, powerOnCommand, sendHomeCommand, sendStopCommand, sureCompletedCommand, updateInstructionExecutionPriority, addManualCommand} from "@/api/robotArm/robotArmCommand"
import {addCommand, powerOnCommand, sendHomeCommand, sendStopCommand, sureCompletedCommand,
updateInstructionExecutionPriority, addManualCommand,updateAnimationState,powerFailureResolved} from "@/api/robotArm/robotArmCommand"
export default {
name: 'RoboticArm',
......@@ -281,7 +241,6 @@ export default {
// 自动模式数据
trayCode: '',
// 手动模式数据
manualTrayCode: '',
storeyCode: '',
......@@ -297,6 +256,12 @@ export default {
selectedCommand: null,
priority: 'loading', // loading: 上料优先, unloading: 下料优先
initWebSocketIp: '',
// 新增:动画控制
isAnimating: true, // 默认动画运行
// 新增:断电失败修复对话框
showFixDialog: false,
selectedFailedCommand: null,
};
},
computed: {
......@@ -362,34 +327,19 @@ export default {
},
methods: {
// 打开上料模式选择对话框
openAddModeDialog() {
this.showModeDialog = true;
},
// 关闭模式选择对话框
closeModeDialog() {
this.showModeDialog = false;
},
// 选择模式
// ==================== 原有方法保持不变 ====================
openAddModeDialog() { this.showModeDialog = true; },
closeModeDialog() { this.showModeDialog = false; },
selectMode(mode) {
this.showModeDialog = false;
if (mode === 'auto') {
this.showAutoAddDialog = true;
} else {
this.showManualAddDialog = true;
}
if (mode === 'auto') this.showAutoAddDialog = true;
else this.showManualAddDialog = true;
},
// 关闭自动模式对话框
closeAutoDialog() {
this.showAutoAddDialog = false;
this.productStandardInspectionNumber = '';
this.trayCode = '';
},
// 关闭手动模式对话框
closeManualDialog() {
this.showManualAddDialog = false;
this.manualTrayCode = '';
......@@ -397,85 +347,44 @@ export default {
this.trayType = '0';
this.productStandardInspectionNumber = '';
},
// 确认自动模式上料
confirmAutoAdd() {
if (!this.trayCode.trim()) {
this.$message.warning('请输入托盘编号');
return;
}
if (!this.productStandardInspectionNumber.trim()) {
this.$message.warning('请输入标检单编号');
return;
}
if (!this.trayCode.trim()) { this.$message.warning('请输入托盘编号'); return; }
if (!this.productStandardInspectionNumber.trim()) { this.$message.warning('请输入标检单编号'); return; }
const robotArmCommand = {
trayCode: this.trayCode,
storeyCode: '待分配位置',
type: '0',
productStandardInspectionNumber: this.productStandardInspectionNumber
};
addCommand(robotArmCommand).then(res => {
if(res.code === 200) {
this.$message.success("添加成功");
this.closeAutoDialog();
} else {
this.$message.error("添加失败");
}
if(res.code === 200) { this.$message.success("添加成功"); this.closeAutoDialog(); }
else { this.$message.error("添加失败"); }
})
},
// 确认手动模式上料
confirmManualAdd() {
if (!this.productStandardInspectionNumber.trim()) {
this.$message.warning('请输入标检单号');
return;
}
if (!this.manualTrayCode.trim()) {
this.$message.warning('请输入托盘编号');
return;
}
if (!this.storeyCode.trim()) {
this.$message.warning('请输入层编号');
return;
}
if (!this.productStandardInspectionNumber.trim()) { this.$message.warning('请输入标检单号'); return; }
if (!this.manualTrayCode.trim()) { this.$message.warning('请输入托盘编号'); return; }
if (!this.storeyCode.trim()) { this.$message.warning('请输入层编号'); return; }
const robotArmCommand = {
trayCode: this.manualTrayCode,
storeyCode: this.storeyCode,
productStandardInspectionNumber: this.productStandardInspectionNumber,
};
addManualCommand(robotArmCommand).then(res => {
if(res.code === 200) {
this.$message.success("手动添加成功");
this.closeManualDialog();
}
if(res.code === 200) { this.$message.success("手动添加成功"); this.closeManualDialog(); }
})
},
initWebSocket() {
// 从环境变量获取基础URL,默认使用Nginx代理地址
const backendUrl = process.env.VUE_APP_API_BASE_URL || this.initWebSocketIp;
// 根据需要切换不同的WebSocket端点
const wsPath = '/agecal/ws-robot-arm'; // 或 '/agecal/ws-aging-cabinet'
// 替换协议并添加完整路径
const wsPath = '/agecal/ws-robot-arm';
const wsUrl = backendUrl.replace('http', 'ws') + wsPath;
try {
this.websocket = new WebSocket(wsUrl);
this.websocket.onopen = () => {
console.log('机械臂指令WebSocket连接成功');
this.status = 'idle';
this.sendWebSocketMessage({ type: 'request', commands: ['loading', 'unloading'] });
};
this.websocket.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
......@@ -484,7 +393,7 @@ export default {
robotArmCommandId: cmd.robotArmCommandId,
trayCode: cmd.trayCode,
position: cmd.storeyCode,
status: cmd.status || '0' // 默认待执行状态
status: cmd.status || '0'
}));
}
else if (message.type === 'unloading') {
......@@ -492,41 +401,23 @@ export default {
robotArmCommandId: cmd.robotArmCommandId,
trayCode: cmd.trayCode,
position: cmd.storeyCode,
status: cmd.status || '0' // 默认待执行状态
status: cmd.status || '0'
}));
}
// 新增:处理状态更新消息
else if (message.type === 'status') {
this.status = message.data; // 更新机械臂状态
this.status = message.data;
}
} catch (e) {
console.error('解析WebSocket消息失败:', e);
}
};
this.websocket.onerror = (error) => {
console.error('WebSocket错误:', error);
this.status = 'error';
this.scheduleReconnect();
} catch (e) { console.error('解析WebSocket消息失败:', e); }
};
this.websocket.onclose = () => {
console.log('WebSocket连接关闭');
this.scheduleReconnect();
};
} catch (e) {
console.error('创建WebSocket失败:', e);
this.scheduleReconnect();
}
this.websocket.onerror = (error) => { console.error('WebSocket错误:', error); this.status = 'error'; this.scheduleReconnect(); };
this.websocket.onclose = () => { console.log('WebSocket连接关闭'); this.scheduleReconnect(); };
} catch (e) { console.error('创建WebSocket失败:', e); this.scheduleReconnect(); }
},
sendWebSocketMessage(message) {
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
this.websocket.send(JSON.stringify(message));
}
},
scheduleReconnect() {
if (this.reconnectInterval) clearInterval(this.reconnectInterval);
this.reconnectInterval = setInterval(() => {
......@@ -537,132 +428,116 @@ export default {
}
}, 5000);
},
disconnectWebSocket() {
if (this.websocket) {
try {
this.websocket.close();
} catch (e) {
console.error('关闭WebSocket时出错:', e);
}
}
if (this.reconnectInterval) {
clearInterval(this.reconnectInterval);
this.reconnectInterval = null;
}
if (this.websocket) { try { this.websocket.close(); } catch (e) { console.error('关闭WebSocket时出错:', e); } }
if (this.reconnectInterval) { clearInterval(this.reconnectInterval); this.reconnectInterval = null; }
this.websocket = null;
},
// 回零
handleHome() {
sendHomeCommand().then( response => {
this.$message.success("已发送");
})
},
// 停止
handleStop() {
sendStopCommand().then( response => {
this.$message.success("已发送");
})
},
// 更新状态文本映射
handleHome() { sendHomeCommand().then(response => { this.$message.success("已发送"); }) },
handleStop() { sendStopCommand().then(response => { this.$message.success("已发送"); }) },
getStatusText(status) {
const statusMap = {
'0': '待分配位置',
'1': '待执行',
'2': '执行中',
'3': '未上电',
'4': '执行结束'
};
const statusMap = { '0': '待分配位置', '1': '待执行', '2': '执行中', '3': '未上电', '4': '执行结束', '5': '断电失败' };
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' // 执行结束
'status-0': status == '0',
'status-1': status == '1',
'status-2': status == '2',
'status-3': status == '3',
'status-4': status == '4',
'status-5': status === '5'
};
},
// ==================== 修改:handleCommandClick 增加对 status === '5' 的处理 ====================
handleCommandClick(cmd) {
// 只有未上电状态的指令才可点击
if (cmd.status === '3') {
this.selectedCommand = cmd;
this.showPowerOnDialog = true;
} else if(cmd.status === '2') {
} else if (cmd.status === '2') {
this.selectedCommand = cmd;
this.showSureCompleteDialog = true;
} else if (cmd.status === '5') { // 新增:断电失败
this.selectedFailedCommand = cmd;
this.showFixDialog = true;
}
},
closeSureCompleteDialog() {
this.showSureCompleteDialog = false;
this.selectedCommand = null;
},
closePowerOnDialog() {
this.showPowerOnDialog = false;
this.selectedCommand = null;
},
closeSureCompleteDialog() { this.showSureCompleteDialog = false; this.selectedCommand = null; },
closePowerOnDialog() { this.showPowerOnDialog = false; this.selectedCommand = null; },
confirmSureComplete() {
if (!this.selectedCommand) return;
sureCompletedCommand(this.selectedCommand.robotArmCommandId).then(res => {
if(res.code === 200) {
this.$message.success("确认完成成功");
this.closeSureCompleteDialog();
} else {
this.$message.error("确认完成失败");
}
}).catch(err => {
this.$message.error("确认完成失败");
})
if(res.code === 200) { this.$message.success("确认完成成功"); this.closeSureCompleteDialog(); }
else { this.$message.error("确认完成失败"); }
}).catch(err => { this.$message.error("确认完成失败"); })
},
confirmPowerOn() {
if (!this.selectedCommand) return;
powerOnCommand(this.selectedCommand.robotArmCommandId).then(res => {
if(res.code === 200) {
this.$message.success("上电操作成功");
this.closePowerOnDialog();
} else {
this.$message.error("上电操作失败");
}
}).catch(err => {
this.$message.error("上电操作失败");
});
if(res.code === 200) { this.$message.success("上电操作成功"); this.closePowerOnDialog(); }
else { this.$message.error("上电操作失败"); }
}).catch(err => { this.$message.error("上电操作失败"); });
},
// 切换优先级
togglePriority() {
this.priority = this.priority === 'loading' ? 'unloading' : 'loading';
updateInstructionExecutionPriority(this.priority).then(res => {
if(res.code === 200) {
this.$message.success("优先级设置成功");
} else {
this.$message.error("优先级设置失败");
}
if(res.code === 200) { this.$message.success("优先级设置成功"); }
else { this.$message.error("优先级设置失败"); }
})
// 这里可以添加发送优先级设置到后端的逻辑
console.log(`优先级已切换为: ${this.priorityDisplayText} : this.priority:${this.priority}` );
},
// 消息提示方法
msgSuccess(msg) {
this.$message.success(msg);
msgSuccess(msg) { this.$message.success(msg); },
msgError(msg) { this.$message.error(msg); },
// ==================== 新增:动画暂停/继续 ====================
toggleAnimation() {
const action = this.isAnimating ? '暂停' : '继续';
this.$confirm(`确定要${action}机械臂动作吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
// 用户点击确定:切换动画状态
this.isAnimating = !this.isAnimating;
const actionStr = this.isAnimating ? 'start' : 'stop';
updateAnimationState(actionStr).then(() => {
this.$message.success(`动画已${action}`);
}).catch(() => {
this.$message.error(`操作失败`);
// 如果失败,可恢复状态
this.isAnimating = !this.isAnimating;
});
}).catch(() => {
// 用户点击取消
this.$message.info(`已取消${action}操作`);
});
},
msgError(msg) {
this.$message.error(msg);
// ==================== 新增:断电失败修复对话框相关 ====================
closeFixDialog() {
this.showFixDialog = false;
this.selectedFailedCommand = null;
},
// 修复确认后的处理(您可在此完善具体逻辑)
handleFixConfirm() {
if (!this.selectedFailedCommand) return;
powerFailureResolved(this.selectedFailedCommand.robotArmCommandId).then(res => {
if(res.code === 200) { this.$message.success("断电修复成功"); }
else { this.$message.error("断电修复失败"); }
})
this.$message.success('修复操作已执行(待完善)');
this.closeFixDialog();
}
}
};
</script>
<style scoped>
/* 新增:暂停动画类,作用于所有动画元素 */
.paused-animation .robotic-arm * {
animation-play-state: paused !important;
}
/* 标题区域样式 */
.panel-title {
position: absolute;
......@@ -1505,6 +1380,13 @@ export default {
border: 1px solid rgba(180, 180, 180, 0.5);
}
.command-item.status-5 {
/* 断电失败 */
background: rgba(80, 0, 0, 0.3);
border: 1px solid rgb(208, 24, 24);
cursor: pointer;
animation: blink-red 1s infinite; /* 保持原未上电动画 */
}
/* 状态文本颜色 */
.status-0 .cmd-status {
color: #a0a0ff; /* 待分配位置文本色 */
......@@ -1526,6 +1408,9 @@ export default {
color: #a0a0a0; /* 执行结束文本色 */
}
.status-5 .cmd-status {
color: #d01818; /* 未上电文本色 */
}
/* 状态文本样式 */
.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