feat(inspection): 新增扬声器播放音频功能并优化机器人状态同步机制
- 在 ActionEnum 中添加 SPEAKER_PLAYAUDIO 操作类型 - 配置机器人状态检查任务的启用和时间间隔参数 - 移除应用配置中的废弃TTS外部接口配置 - 实现 EdgeSpeakerOperateService 中的音频播放功能和参数验证 - 重构 FlowActionExecutorService 以支持多种操作类型的服务分发 - 更新数据库表结构中机器人状态字段的含义和注释说明 - 修改 InspectionRobot 实体类中的状态定义和业务编号描述 - 扩展 InspectionRobotMapper 接口以支持运行时状态更新和候选查询 - 实现 InspectionRobotService 中的 AGV 请求构建和状态同步逻辑 - 添加 RobotRuntimeStateRefreshTask 定时任务来同步机器人运行状态 - 修复 InspectionTaskInstanceService 中的重复机器人ID设置问题 - 优化 RobotQuicStateService 中的设备状态判断逻辑
This commit is contained in:
parent
86a8e02d85
commit
54df691ea6
@ -132,8 +132,3 @@ flowise:
|
||||
abort: http://192.168.0.108:3000/api/v1/chatmessage/abort/f99329e9-b33d-437d-90ed-69eaa8a05418/
|
||||
query: http://192.168.0.108:3000/api/v1/executions/
|
||||
api-key: pg61JW6W_GXyqmqSoURJ4mlSRrCMRzGLBJli_w3BFkg
|
||||
|
||||
# TTS外部接口配置
|
||||
tts:
|
||||
external-api:
|
||||
url: http://192.168.0.102:9003/generate_advanced_audio
|
||||
|
||||
@ -124,6 +124,11 @@ xss:
|
||||
|
||||
# Integrated QUIC gateway and in-process node synchronization.
|
||||
cmvr:
|
||||
inspection:
|
||||
robot-state:
|
||||
enabled: true
|
||||
initial-delay-ms: 3000
|
||||
refresh-ms: 1000
|
||||
quic:
|
||||
enabled: true
|
||||
bind-host: 0.0.0.0
|
||||
|
||||
@ -38,9 +38,9 @@ public class InspectionRobot extends BaseEntity
|
||||
@ApiModelProperty("机器人永久唯一ID")
|
||||
private String robotId;
|
||||
|
||||
/** 机器人编码 */
|
||||
@Excel(name = "机器人编码")
|
||||
@ApiModelProperty("机器人编码")
|
||||
/** Business display code. It is not an edge device ID. */
|
||||
@Excel(name = "机器人业务编号")
|
||||
@ApiModelProperty("机器人业务编号,仅用于业务展示,不作为设备ID")
|
||||
private String robotCode;
|
||||
|
||||
/** 机器人名称 */
|
||||
@ -75,7 +75,7 @@ public class InspectionRobot extends BaseEntity
|
||||
|
||||
/** 当前状态 */
|
||||
@Excel(name = "当前状态")
|
||||
@ApiModelProperty("当前状态(0在线 1离线 2充电中 3巡检中)")
|
||||
@ApiModelProperty("AGV运行模式(0未知 1断开 2空闲 3手动 4自动 5充电 6暂停 7停止 8故障 9急停)")
|
||||
private String status;
|
||||
|
||||
/** 电量百分比 */
|
||||
|
||||
@ -34,9 +34,9 @@ public class InspectionRobotVo extends BaseEntity
|
||||
@ApiModelProperty("机器人永久唯一ID")
|
||||
private String robotId;
|
||||
|
||||
/** 机器人编码 */
|
||||
@Excel(name = "机器人编码")
|
||||
@ApiModelProperty("机器人编码")
|
||||
/** Business display code. It is not an edge device ID. */
|
||||
@Excel(name = "机器人业务编号")
|
||||
@ApiModelProperty("机器人业务编号,仅用于业务展示,不作为设备ID")
|
||||
private String robotCode;
|
||||
|
||||
/** 机器人名称 */
|
||||
@ -81,7 +81,7 @@ public class InspectionRobotVo extends BaseEntity
|
||||
|
||||
/** 当前状态 */
|
||||
@Excel(name = "当前状态")
|
||||
@ApiModelProperty("当前状态(0在线 1离线 2充电中 3巡检中)")
|
||||
@ApiModelProperty("AGV运行模式(0未知 1断开 2空闲 3手动 4自动 5充电 6暂停 7停止 8故障 9急停)")
|
||||
private String status;
|
||||
|
||||
/** 电量百分比 */
|
||||
|
||||
@ -27,6 +27,16 @@ public interface InspectionRobotMapper extends MPJBaseMapper<InspectionRobot>
|
||||
|
||||
int updateQuicRuntimeState(InspectionRobot robot);
|
||||
|
||||
/**
|
||||
* Update the AGV runtime snapshot without changing business audit fields.
|
||||
*/
|
||||
int updateRuntimeStatus(InspectionRobot robot);
|
||||
|
||||
/**
|
||||
* Select connected robots that currently report one online AGV device.
|
||||
*/
|
||||
List<InspectionRobot> selectRuntimeSyncCandidates();
|
||||
|
||||
int archiveInspectionRobotByIds(@Param("ids") String[] ids, @Param("updateBy") String updateBy);
|
||||
|
||||
/**
|
||||
|
||||
@ -28,6 +28,7 @@ import java.util.Objects;
|
||||
public class RobotQuicStateService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RobotQuicStateService.class);
|
||||
private static final int AGV_DEVICE_KIND = 1;
|
||||
|
||||
private final InspectionRobotMapper robotMapper;
|
||||
private final InspectionRobotDeviceMapper deviceMapper;
|
||||
@ -90,6 +91,9 @@ public class RobotQuicStateService {
|
||||
robot.setSoftwareVersion(node.getSoftwareVersion());
|
||||
robot.setLastHeartbeatTime(eventTime);
|
||||
robot.setUpdateTime(DateUtils.getNowDate());
|
||||
if (offline || !hasEnabledAgv(node)) {
|
||||
robot.setStatus("1");
|
||||
}
|
||||
robotMapper.updateQuicRuntimeState(robot);
|
||||
|
||||
if (offline) {
|
||||
@ -162,6 +166,11 @@ public class RobotQuicStateService {
|
||||
return device;
|
||||
}
|
||||
|
||||
private boolean hasEnabledAgv(NodeSnapshot node) {
|
||||
return node.getDeviceManager().getDevicesList().stream()
|
||||
.anyMatch(device -> device.getKind() == AGV_DEVICE_KIND && device.getEnabled());
|
||||
}
|
||||
|
||||
private String resolveGrpcHost(NodeSnapshot node) {
|
||||
String advertised = StringUtils.trimToNull(node.getGrpcEndpoint().getHost());
|
||||
String observed = StringUtils.trimToNull(node.getObservedSourceIp());
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
package com.cmvr.inspection.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
import cmvr.msgs.AgvUtils;
|
||||
import cmvr.quic_edge.v1.QuicEdge;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import com.cmvr.common.utils.SecurityUtils;
|
||||
import com.cmvr.edge.client.model.EdgeCommonVO;
|
||||
@ -167,6 +169,50 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
return robotId == null ? null : robotId.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an AGV request from the device snapshot reported by this robot.
|
||||
* robotCode is a business label and must never be used as an edge device ID.
|
||||
*/
|
||||
private EdgeCommonVO buildAgvRequest(InspectionRobot robot)
|
||||
{
|
||||
if (robot == null || StrUtil.isBlank(robot.getRobotId())) {
|
||||
throw new GlobalException("机器人永久ID不能为空");
|
||||
}
|
||||
List<InspectionRobotDevice> agvDevices = inspectionRobotDeviceMapper.selectByRobotIdAndKind(
|
||||
robot.getRobotId(), QuicEdge.DeviceKind.DEVICE_KIND_AGV_VALUE, true);
|
||||
if (agvDevices.isEmpty()) {
|
||||
List<InspectionRobotDevice> onlineDevices = inspectionRobotDeviceMapper.selectByRobotIdAndKind(
|
||||
robot.getRobotId(), null, true);
|
||||
if (onlineDevices.isEmpty()) {
|
||||
throw new GlobalException("机器人当前没有在线设备:" + robot.getRobotId());
|
||||
}
|
||||
String deviceSummary = onlineDevices.stream()
|
||||
.map(device -> device.getDeviceId() + "(" + deviceKindName(device.getDeviceKind()) + ")")
|
||||
.collect(java.util.stream.Collectors.joining("、"));
|
||||
throw new GlobalException("机器人在线设备中没有AGV,当前设备:" + deviceSummary);
|
||||
}
|
||||
if (agvDevices.size() > 1) {
|
||||
String deviceIds = agvDevices.stream()
|
||||
.map(InspectionRobotDevice::getDeviceId)
|
||||
.collect(java.util.stream.Collectors.joining("、"));
|
||||
throw new GlobalException("机器人存在多个在线AGV设备,无法确定默认设备:" + deviceIds);
|
||||
}
|
||||
|
||||
EdgeCommonVO request = new EdgeCommonVO();
|
||||
request.setRobotId(robot.getRobotId());
|
||||
request.setDeviceId(agvDevices.get(0).getDeviceId());
|
||||
return request;
|
||||
}
|
||||
|
||||
private String deviceKindName(Integer deviceKind)
|
||||
{
|
||||
if (deviceKind == null) {
|
||||
return "未知类型";
|
||||
}
|
||||
QuicEdge.DeviceKind kind = QuicEdge.DeviceKind.forNumber(deviceKind);
|
||||
return kind == null ? "未知类型" + deviceKind : kind.name().replace("DEVICE_KIND_", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人地图列表(从AGV获取)
|
||||
*
|
||||
@ -180,15 +226,7 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
if (robot == null) {
|
||||
throw new GlobalException("机器人不存在");
|
||||
}
|
||||
if (StrUtil.isBlank(robot.getIpAddress())) {
|
||||
throw new GlobalException("机器人IP地址未配置");
|
||||
}
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
return edgeAgvService.listMaps(edgeCommonVO);
|
||||
return edgeAgvService.listMaps(buildAgvRequest(robot));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,11 +271,7 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
throw new GlobalException("机器人不存在");
|
||||
}
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
return edgeAgvService.downloadMap(edgeCommonVO, mapName);
|
||||
return edgeAgvService.downloadMap(buildAgvRequest(robot), mapName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,22 +310,16 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
}
|
||||
|
||||
// 5. 从来源机器人下载地图JSON
|
||||
EdgeCommonVO sourceEdgeVO = new EdgeCommonVO();
|
||||
sourceEdgeVO.setRobotId(sourceRobot.getRobotId());
|
||||
sourceEdgeVO.setDeviceId(sourceRobot.getRobotCode());
|
||||
|
||||
String mapContent = edgeAgvService.downloadMap(sourceEdgeVO, map.getMapSourceName());
|
||||
String mapContent = edgeAgvService.downloadMap(
|
||||
buildAgvRequest(sourceRobot), map.getMapSourceName());
|
||||
|
||||
if (StrUtil.isBlank(mapContent)) {
|
||||
throw new GlobalException("下载的地图内容为空");
|
||||
}
|
||||
|
||||
// 6. 上传地图到目标机器人
|
||||
EdgeCommonVO targetEdgeVO = new EdgeCommonVO();
|
||||
targetEdgeVO.setRobotId(targetRobot.getRobotId());
|
||||
targetEdgeVO.setDeviceId(targetRobot.getRobotCode());
|
||||
|
||||
edgeAgvService.uploadMap(targetEdgeVO, map.getMapSourceName(), mapContent);
|
||||
edgeAgvService.uploadMap(
|
||||
buildAgvRequest(targetRobot), map.getMapSourceName(), mapContent);
|
||||
|
||||
return "上传成功";
|
||||
}
|
||||
@ -310,31 +338,37 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
throw new GlobalException("机器人不存在");
|
||||
}
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
try {
|
||||
// 获取运行时状态(包含电池、位置等信息)
|
||||
AgvUtils.AgvRuntimeState runtimeState = edgeAgvService.getRuntimeState(edgeCommonVO);
|
||||
AgvUtils.AgvRuntimeState runtimeState = edgeAgvService.getRuntimeState(buildAgvRequest(robot));
|
||||
if (runtimeState == null) {
|
||||
throw new GlobalException("AGV未返回运行状态");
|
||||
}
|
||||
|
||||
// 设置电池电量
|
||||
if (runtimeState.hasBattery()) {
|
||||
robot.setBatteryLevel((int)(runtimeState.getBattery().getPercentage() * 100));
|
||||
int percentage = (int) Math.round(runtimeState.getBattery().getPercentage() * 100D);
|
||||
robot.setBatteryLevel(Math.max(0, Math.min(100, percentage)));
|
||||
}
|
||||
|
||||
// 设置位置信息
|
||||
if (runtimeState.hasPose()) {
|
||||
AgvUtils.AgvPose2d pose = runtimeState.getPose();
|
||||
robot.setCurrentPosition(pose.getX() + "," + pose.getY() + "," + pose.getTheta());
|
||||
robot.setCurrentPosition(String.format(Locale.ROOT, "%.3f,%.3f,%.3f",
|
||||
pose.getX(), pose.getY(), pose.getTheta()));
|
||||
}
|
||||
|
||||
robot.setUpdateTime(DateUtils.getNowDate());
|
||||
inspectionRobotMapper.updateInspectionRobot(robot);
|
||||
// Keep the platform status identical to the edge AgvMode value.
|
||||
robot.setStatus(String.valueOf(runtimeState.getMode()));
|
||||
|
||||
inspectionRobotMapper.updateRuntimeStatus(robot);
|
||||
} catch (Exception e) {
|
||||
// 记录日志但不抛出异常,允许部分失败
|
||||
e.printStackTrace();
|
||||
if (e instanceof GlobalException) {
|
||||
throw (GlobalException) e;
|
||||
}
|
||||
robot.setStatus("0");
|
||||
inspectionRobotMapper.updateRuntimeStatus(robot);
|
||||
throw new GlobalException("获取机器人运行状态失败:" +
|
||||
(StrUtil.isBlank(e.getMessage()) ? e.getClass().getSimpleName() : e.getMessage()), e);
|
||||
}
|
||||
|
||||
return robot;
|
||||
@ -354,11 +388,7 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
throw new GlobalException("机器人不存在");
|
||||
}
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
AgvUtils.AgvRuntimeState runtimeState = edgeAgvService.getRuntimeState(edgeCommonVO);
|
||||
AgvUtils.AgvRuntimeState runtimeState = edgeAgvService.getRuntimeState(buildAgvRequest(robot));
|
||||
return runtimeState.hasPose() ? runtimeState.getPose() : null;
|
||||
}
|
||||
|
||||
@ -389,12 +419,8 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
.setTheta(theta != null ? theta : 0.0)
|
||||
.build();
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
// 调用边缘端接口进行导航
|
||||
edgeAgvService.navigateToPose(edgeCommonVO, pose);
|
||||
edgeAgvService.navigateToPose(buildAgvRequest(robot), pose);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -414,12 +440,9 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
throw new GlobalException("机器人不存在");
|
||||
}
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
AgvUtils.AgvMapDimension mapDimension = AgvUtils.AgvMapDimension.forNumber(dimension);
|
||||
return edgeAgvService.startMapping(edgeCommonVO, mapDimension, mapName, realTime);
|
||||
return edgeAgvService.startMapping(
|
||||
buildAgvRequest(robot), mapDimension, mapName, realTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -435,11 +458,7 @@ public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMappe
|
||||
throw new GlobalException("机器人不存在");
|
||||
}
|
||||
|
||||
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
|
||||
edgeCommonVO.setRobotId(robot.getRobotId());
|
||||
edgeCommonVO.setDeviceId(robot.getRobotCode());
|
||||
|
||||
edgeAgvService.stopMapping(edgeCommonVO);
|
||||
edgeAgvService.stopMapping(buildAgvRequest(robot));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -170,7 +170,6 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
|
||||
String insId = flowTaskRuntimeService.executeTask(TeTaskExecuteNormalVO.builder()
|
||||
.taskId(inspectionTask.getTaskConfigId())
|
||||
.robotId(robot.getRobotId())
|
||||
.robotId(robot.getRobotId())
|
||||
.moduleCode(FlowExecutionModuleCodes.INSPECTION)
|
||||
.runParams(runParams)
|
||||
.build());
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
package com.cmvr.inspection.task;
|
||||
|
||||
import com.cmvr.inspection.domain.InspectionRobot;
|
||||
import com.cmvr.inspection.mapper.InspectionRobotMapper;
|
||||
import com.cmvr.inspection.service.IInspectionRobotService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Periodically persists AGV runtime snapshots for connected robots.
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "cmvr.inspection.robot-state", name = "enabled",
|
||||
havingValue = "true", matchIfMissing = true)
|
||||
public class RobotRuntimeStateRefreshTask
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(RobotRuntimeStateRefreshTask.class);
|
||||
|
||||
private final InspectionRobotMapper inspectionRobotMapper;
|
||||
private final IInspectionRobotService inspectionRobotService;
|
||||
private final TaskExecutor taskExecutor;
|
||||
private final Set<String> syncingRobotIds = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public RobotRuntimeStateRefreshTask(InspectionRobotMapper inspectionRobotMapper,
|
||||
IInspectionRobotService inspectionRobotService,
|
||||
@Qualifier("threadPoolTaskExecutor") TaskExecutor taskExecutor)
|
||||
{
|
||||
this.inspectionRobotMapper = inspectionRobotMapper;
|
||||
this.inspectionRobotService = inspectionRobotService;
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
@Scheduled(initialDelayString = "${cmvr.inspection.robot-state.initial-delay-ms:3000}",
|
||||
fixedDelayString = "${cmvr.inspection.robot-state.refresh-ms:5000}")
|
||||
public void refreshConnectedRobots()
|
||||
{
|
||||
for (InspectionRobot robot : inspectionRobotMapper.selectRuntimeSyncCandidates()) {
|
||||
if (robot.getId() == null || !syncingRobotIds.add(robot.getId())) {
|
||||
continue;
|
||||
}
|
||||
taskExecutor.execute(() -> refreshRobot(robot));
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshRobot(InspectionRobot robot)
|
||||
{
|
||||
try {
|
||||
inspectionRobotService.syncRobotStatus(robot.getId());
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to refresh AGV runtime state, robotId={}, reason={}",
|
||||
robot.getRobotId(), e.getMessage());
|
||||
} finally {
|
||||
syncingRobotIds.remove(robot.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,6 +169,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<update id="updateQuicRuntimeState" parameterType="InspectionRobot">
|
||||
update inspection_robot
|
||||
set ip_address = #{ipAddress}, port = #{port}, connect_status = #{connectStatus},
|
||||
status = coalesce(#{status}, status),
|
||||
quic_node_id = #{quicNodeId}, quic_session_id = #{quicSessionId},
|
||||
quic_boot_id = #{quicBootId}, observed_ip = #{observedIp},
|
||||
software_version = #{softwareVersion}, last_heartbeat_time = #{lastHeartbeatTime},
|
||||
@ -176,6 +177,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updateRuntimeStatus" parameterType="InspectionRobot">
|
||||
update inspection_robot
|
||||
set status = coalesce(#{status}, status),
|
||||
battery_level = coalesce(#{batteryLevel}, battery_level),
|
||||
current_position = coalesce(#{currentPosition}, current_position)
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<select id="selectRuntimeSyncCandidates" resultMap="InspectionRobotResult">
|
||||
select r.id, r.robot_id, r.robot_code, r.robot_name, r.robot_model, r.robot_type,
|
||||
r.ip_address, r.port, r.current_map_id, r.status, r.battery_level,
|
||||
r.current_position, r.connect_status, r.archived_status
|
||||
from inspection_robot r
|
||||
where r.connect_status = '1'
|
||||
and coalesce(r.archived_status, '0') = '0'
|
||||
and (select count(*)
|
||||
from inspection_robot_device d
|
||||
where d.robot_id = r.robot_id
|
||||
and d.device_kind = 1
|
||||
and d.enabled = '1'
|
||||
and d.online_status = '1') = 1
|
||||
</select>
|
||||
|
||||
<update id="archiveInspectionRobotByIds" parameterType="String">
|
||||
update inspection_robot set archived_status = '1', connect_status = '0',
|
||||
update_time = sysdate(), update_by = #{updateBy}
|
||||
|
||||
@ -11,6 +11,8 @@ import java.util.Map;
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ActionEnum {
|
||||
SPEAKER_PLAYAUDIO("EDGE", "SPEAKER_PLAYAUDIO", "Play audio"),
|
||||
|
||||
// 平台控制类
|
||||
STOPPED("NONE", "STOP", "强制终止"),
|
||||
PAUSED("NONE", "PAUSED", "暂停"),
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.cmvr.test.flow.runtime.operator.edge;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.cmvr.common.exception.GlobalException;
|
||||
import com.cmvr.edge.client.service.EdgeSpeakerService;
|
||||
@ -24,6 +25,17 @@ public class EdgeSpeakerOperateService implements EdgeOperateService {
|
||||
|
||||
@Override
|
||||
public TaskNodeExecuteResult execute(TaskNodeExecuteMessage message) {
|
||||
JSONObject inputParams = message.getInputParams();
|
||||
String deviceId = inputParams.getString("deviceId");
|
||||
String audioPath = inputParams.getString("audioPath");
|
||||
String robotId = message.getRobotId();
|
||||
if (StrUtil.hasBlank(robotId, deviceId, audioPath)) {
|
||||
throw new GlobalException("robotId, deviceId and audioPath are required");
|
||||
}
|
||||
String result = edgeSpeakerService.playAudio(robotId, deviceId, audioPath);
|
||||
if (result != null) {
|
||||
return TaskNodeExecuteResult.success(JSONObject.of("result", result));
|
||||
}
|
||||
// ActionEnum action = message.getAction();
|
||||
// JSONObject inputParams = message.getInputParams();
|
||||
// String deviceId = inputParams.getString("deviceId");
|
||||
@ -64,6 +76,6 @@ public class EdgeSpeakerOperateService implements EdgeOperateService {
|
||||
// throw new GlobalException("不支持的扬声器操作类型: " + action);
|
||||
// }
|
||||
// return TaskNodeExecuteResult.success();
|
||||
return null;
|
||||
return TaskNodeExecuteResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,16 +17,25 @@ import com.cmvr.edge.client.service.EdgeAgvService;
|
||||
import com.cmvr.device.service.InspectionAlertListenService;
|
||||
import com.cmvr.llm.service.LLMAiAgentPlatformService;
|
||||
import com.cmvr.test.enums.ActionEnum;
|
||||
import com.cmvr.test.flow.runtime.dispatcher.FlowCodeNodeHandler;
|
||||
import com.cmvr.test.flow.runtime.dispatcher.FlowHttpNodeHandler;
|
||||
import com.cmvr.test.flow.runtime.dispatcher.FlowSleepNodeHandler;
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
|
||||
import com.cmvr.test.flow.runtime.operator.ae.AEOperateService;
|
||||
import com.cmvr.test.flow.runtime.operator.edge.EdgeManualInspectionOperateService;
|
||||
import com.cmvr.test.flow.runtime.operator.edge.EdgeDeviceCommandOperateService;
|
||||
import com.cmvr.test.flow.runtime.operator.edge.EdgeArmOperateService;
|
||||
import com.cmvr.test.flow.runtime.operator.edge.EdgeOperateService;
|
||||
import com.cmvr.test.flow.runtime.operator.llm.InspectionMeterRecognizeOperateService;
|
||||
import com.cmvr.test.flow.runtime.operator.llm.LLMOperateService;
|
||||
import com.cmvr.test.model.vo.FlowActionRequestVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ -43,6 +52,12 @@ public class FlowActionExecutorService {
|
||||
private final EdgeManualInspectionOperateService edgeManualInspectionOperateService;
|
||||
private final EdgeDeviceCommandOperateService edgeDeviceCommandOperateService;
|
||||
private final InspectionMeterRecognizeOperateService inspectionMeterRecognizeOperateService;
|
||||
private final List<EdgeOperateService> edgeOperateServices;
|
||||
private final List<LLMOperateService> llmOperateServices;
|
||||
private final List<AEOperateService> aeOperateServices;
|
||||
private final FlowSleepNodeHandler flowSleepNodeHandler;
|
||||
private final FlowHttpNodeHandler flowHttpNodeHandler;
|
||||
private final FlowCodeNodeHandler flowCodeNodeHandler;
|
||||
|
||||
public String actionExecute(FlowActionRequestVO req) {
|
||||
|
||||
@ -54,16 +69,77 @@ public class FlowActionExecutorService {
|
||||
|
||||
switch (action.getOperate()) {
|
||||
case "EDGE":
|
||||
return executeEdgeAction(action, req);
|
||||
return executeEdgeOperateAction(action, req);
|
||||
|
||||
case "LLM":
|
||||
return executeLLMAction(action, req);
|
||||
return executeLLMOperateAction(action, req);
|
||||
|
||||
case "AE":
|
||||
return executeAEOperateAction(action, req);
|
||||
|
||||
case "NONE":
|
||||
return executeStandaloneAction(action, req);
|
||||
|
||||
default:
|
||||
throw new GlobalException("仅设备和大模型 Action 可调用!");
|
||||
}
|
||||
}
|
||||
|
||||
private String executeEdgeOperateAction(ActionEnum action, FlowActionRequestVO req) {
|
||||
TaskNodeExecuteMessage message = buildSingleNodeMessage(action, req.getPayload());
|
||||
for (EdgeOperateService service : edgeOperateServices) {
|
||||
if (service.supports(action)) {
|
||||
return resultToString(service.execute(message));
|
||||
}
|
||||
}
|
||||
throw new GlobalException("Unsupported EDGE action: " + action.getAction());
|
||||
}
|
||||
|
||||
private String executeLLMOperateAction(ActionEnum action, FlowActionRequestVO req) {
|
||||
TaskNodeExecuteMessage message = buildSingleNodeMessage(action, req.getPayload());
|
||||
for (LLMOperateService service : llmOperateServices) {
|
||||
if (service.supports(action)) {
|
||||
return resultToString(service.execute(message));
|
||||
}
|
||||
}
|
||||
throw new GlobalException("Unsupported LLM action: " + action.getAction());
|
||||
}
|
||||
|
||||
private String executeAEOperateAction(ActionEnum action, FlowActionRequestVO req) {
|
||||
TaskNodeExecuteMessage message = buildSingleNodeMessage(action, req.getPayload());
|
||||
for (AEOperateService service : aeOperateServices) {
|
||||
if (service.supports(action)) {
|
||||
return resultToString(service.execute(message));
|
||||
}
|
||||
}
|
||||
throw new GlobalException("Unsupported AE action: " + action.getAction());
|
||||
}
|
||||
|
||||
private String executeStandaloneAction(ActionEnum action, FlowActionRequestVO req) {
|
||||
TaskNodeExecuteMessage message = buildSingleNodeMessage(action, req.getPayload());
|
||||
switch (action) {
|
||||
case SLEEP:
|
||||
return resultToString(flowSleepNodeHandler.handle(message));
|
||||
case HTTP:
|
||||
return resultToString(flowHttpNodeHandler.handle(message));
|
||||
case CODE:
|
||||
return resultToString(flowCodeNodeHandler.handle(message));
|
||||
default:
|
||||
throw new GlobalException("This action requires flow context: " + action.getAction());
|
||||
}
|
||||
}
|
||||
|
||||
private String resultToString(TaskNodeExecuteResult result) {
|
||||
if (result == null) {
|
||||
throw new GlobalException("Single-node execution returned no result");
|
||||
}
|
||||
if (!result.isSuccess()) {
|
||||
throw new GlobalException(StrUtil.isBlank(result.getErrorMsg())
|
||||
? "Single-node execution failed" : result.getErrorMsg());
|
||||
}
|
||||
return result.getOutputParams() == null ? "{}" : result.getOutputParams().toJSONString();
|
||||
}
|
||||
|
||||
private String executeEdgeAction(ActionEnum action, FlowActionRequestVO req) {
|
||||
JSONObject payload = req.getPayload() == null ? new JSONObject() : req.getPayload();
|
||||
String robotId = payload.getString("robotId");
|
||||
@ -253,9 +329,15 @@ public class FlowActionExecutorService {
|
||||
}
|
||||
|
||||
private TaskNodeExecuteMessage buildSingleNodeMessage(ActionEnum action, JSONObject payload) {
|
||||
JSONObject inputParams = payload == null ? new JSONObject() : payload;
|
||||
TaskNodeExecuteMessage message = new TaskNodeExecuteMessage();
|
||||
message.setAction(action);
|
||||
message.setInputParams(payload == null ? new JSONObject() : payload);
|
||||
message.setNodeId("single-node");
|
||||
message.setRobotId(inputParams.getString("robotId"));
|
||||
message.setIterations(java.util.Arrays.asList(1, 1));
|
||||
message.setLoopNum(1);
|
||||
message.setLoopArray(inputParams.get("array"));
|
||||
message.setInputParams(inputParams);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ CREATE TABLE `inspection_robot` (
|
||||
`ip_address` varchar(50) DEFAULT NULL COMMENT 'IP地址',
|
||||
`port` int(11) DEFAULT NULL COMMENT '端口号',
|
||||
`current_map_id` varchar(64) DEFAULT NULL COMMENT '当前地图ID',
|
||||
`status` char(1) DEFAULT '0' COMMENT '状态(0在线 1离线 2充电中 3巡检中)',
|
||||
`status` char(1) DEFAULT '0' COMMENT 'AGV运行模式(0未知 1断开 2空闲 3手动 4自动 5充电 6暂停 7停止 8故障 9急停)',
|
||||
`battery_level` int(3) DEFAULT NULL COMMENT '电量百分比',
|
||||
`current_position` varchar(200) DEFAULT NULL COMMENT '当前位置',
|
||||
`connect_status` char(1) NOT NULL DEFAULT '0' COMMENT '连接状态(0离线 1在线)',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user