refactor(config): 调整资源配置并移除设备配置相关代码

- 将application.yml中的inspection配置项重命名为resource
- 更新开发环境配置文件端口号从13080改为14080
- 在数据库连接URL中引入CMVR_DB_NAME环境变量
- 配置Redis数据库索引使用CMVR_REDIS_DATABASE环境变量
- 在测试环境中添加资源中心隔离配置
- 移除设备配置信息相关的域模型、控制器、数据访问层及映射文件
- 移除设备注册相关的域模型、控制器、数据访问层及映射文件
This commit is contained in:
lixiaolong 2026-08-25 10:58:17 +08:00
parent 61bb2b4a59
commit ca62f939a9
111 changed files with 2306 additions and 5374 deletions

View File

@ -121,9 +121,9 @@ pnpm dev
## gRPC 与 QUIC ## gRPC 与 QUIC
原有机器人 gRPC 客户端与 proto 均保留,但业务调用只传永久 `robotId`。平台根据 `inspection_robot.robot_id` 查找 QUIC 最近同步的 `ip_address` 和 `port`,不再使用终端 ID 或终端配置解析 gRPC 地址。 原有机器人 gRPC 客户端与 proto 均保留,但业务调用只传永久 `robotId`。平台根据 `iot_robot.robot_id` 查找 QUIC 最近同步的 `ip_address` 和 `port`,不再使用终端 ID 或终端配置解析 gRPC 地址。
边缘端通过 QUIC 上报设备快照后,平台按 `robotId + deviceId` 写入 `inspection_robot_device`。前端选择机器人后只展示该机器人当前在线且启用的设备;单一匹配设备会自动选中并锁定。 边缘端通过 QUIC 上报设备快照后,平台按 `robotId + deviceId` 写入 `iot_device`。前端选择机器人后只展示该机器人当前在线且启用的设备;单一匹配设备会自动选中并锁定。
QUIC 不是独立服务,`cmvr-iot-quic` 已作为 `cmvr-iot-admin` 的运行时依赖。启用时主应用同时启动: QUIC 不是独立服务,`cmvr-iot-quic` 已作为 `cmvr-iot-admin` 的运行时依赖。启用时主应用同时启动:
@ -151,6 +151,6 @@ Windows 下也可以使用 `bin/package.bat`、`bin/run.bat` 和 `bin/clean.bat`
## 排查提示 ## 排查提示
- `ApplicationContext ... has been closed already`:彻底停止旧 Java 进程后重新启动,不要使用 DevTools 热重启。 - `ApplicationContext ... has been closed already`:彻底停止旧 Java 进程后重新启动,不要使用 DevTools 热重启。
- 找不到机器人或机器人离线:确认机器人表已登记相同的永久 `robotId`,并检查 QUIC 注册日志、`inspection_robot` 的地址和连接状态。 - 找不到机器人或机器人离线:确认资源中心已登记相同的永久 `robotId`,并检查 QUIC 注册日志、`iot_robot` 的地址和连接状态。
- QUIC 启动失败:检查证书、私钥、ALPN、UDP 端口占用和 native library 对应的操作系统架构。 - QUIC 启动失败:检查证书、私钥、ALPN、UDP 端口占用和 native library 对应的操作系统架构。
- 数据库提示缺少字段:确认基础脚本版本及对应模块迁移脚本已经执行。 - 数据库提示缺少字段:确认基础脚本版本及对应模块迁移脚本已经执行。

View File

@ -86,6 +86,10 @@
<groupId>com.cmvr</groupId> <groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-aima</artifactId> <artifactId>cmvr-iot-aima</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-resource</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.cmvr</groupId> <groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-tts</artifactId> <artifactId>cmvr-iot-tts</artifactId>

View File

@ -1,40 +0,0 @@
package com.cmvr.web.controller.api;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.device.service.IDeDeviceRegistrationService;
import com.cmvr.edge.client.service.EdgeSystemService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "边缘--设备信息获取")
@RestController
@RequestMapping("/api/deviceList")
@RequiredArgsConstructor
public class DeviceListController {
private final EdgeSystemService systemService;
private final IDeDeviceRegistrationService registrationService;
@PreAuthorize("@ss.hasPermi('device:profile:list')")
@GetMapping("/addList")
@ApiOperation("同步设备信息列表")
public int addDeviceList(@RequestParam String robotId){
List<DeDeviceRegistration> list = systemService.deviceList(robotId);
return registrationService.addDeviceList(list);
}
}

View File

@ -1,115 +0,0 @@
package com.cmvr.web.controller.device;
import java.util.List;
import com.cmvr.device.domain.vo.ProfileVo;
import com.cmvr.device.domain.vo.TeMechanicalArmVo;
import com.cmvr.edge.client.service.EdgeSystemService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.cmvr.common.annotation.Log;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.enums.BusinessType;
import com.cmvr.device.domain.DeDeviceProfile;
import com.cmvr.device.service.IDeDeviceProfileService;
import com.cmvr.common.core.page.TableDataInfo;
/**
* 设备配置信息Controller
*
* @author cmvr-iot
* @date 2025-05-09
*/
@RestController
@RequestMapping("/device/profile")
@RequiredArgsConstructor
@Api(tags = "设备--配置信息")
public class DeDeviceProfileController extends BaseController
{
private final IDeDeviceProfileService deDeviceProfileService;
private final EdgeSystemService systemService;
/**
* 查询设备配置信息列表
*/
@PreAuthorize("@ss.hasPermi('device:profile:list')")
@GetMapping("/list")
@ApiOperation("查询设备信息列表")
public TableDataInfo list(DeDeviceProfile deDeviceProfile)
{
startPage();
List<ProfileVo> list = deDeviceProfileService.selectDeDeviceProfileList(deDeviceProfile);
return getDataTable(list);
}
/**
* 根据设备id获取设备详细配置信息
*/
@PreAuthorize("@ss.hasPermi('device:profile:query')")
@GetMapping("/{id}")
@ApiOperation("根据设备id获取当前设备配置信息")
public AjaxResult getInfo(@PathVariable String id)
{
return success(deDeviceProfileService.selectDeDeviceProfileById(id));
}
/**
* 根据设备id和备注获取设备详细配置信息
*/
@PreAuthorize("@ss.hasPermi('device:profile:query')")
@GetMapping("/parameterInfo")
@ApiOperation("根据设备id获取当前设备配置信息")
public AjaxResult getParameterInfo(@RequestParam String id, @RequestParam String remark)
{
return success(deDeviceProfileService.selectProfileByIdAndRemark(id,remark));
}
/**
* 新增设备配置信息
*/
@PreAuthorize("@ss.hasPermi('device:profile:add')")
@Log(title = "设备配置信息", businessType = BusinessType.INSERT)
@ApiOperation("新增设备信息")
@PostMapping
public AjaxResult add(@RequestBody List<DeDeviceProfile> deDeviceProfiles)
{
return toAjax(deDeviceProfileService.insertDeDeviceProfile(deDeviceProfiles));
}
/**
* 修改设备配置信息
*/
@PreAuthorize("@ss.hasPermi('device:profile:edit')")
@Log(title = "设备配置信息", businessType = BusinessType.UPDATE)
@ApiOperation("修改设备信息")
@PutMapping
public AjaxResult edit(@RequestBody List<DeDeviceProfile> deDeviceProfiles)
{
return toAjax(deDeviceProfileService.updateDeDeviceProfile(deDeviceProfiles));
}
/**
* 删除设备配置信息
*/
@PreAuthorize("@ss.hasPermi('device:profile:remove')")
@Log(title = "设备配置信息", businessType = BusinessType.DELETE)
@ApiOperation("删除设备配置信息")
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(deDeviceProfileService.deleteDeDeviceProfileByIds(ids));
}
}

View File

@ -1,122 +0,0 @@
package com.cmvr.web.controller.device;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import com.cmvr.edge.client.service.EdgeSystemService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cmvr.common.annotation.Log;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.enums.BusinessType;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.device.service.IDeDeviceRegistrationService;
import com.cmvr.common.utils.poi.ExcelUtil;
import com.cmvr.common.core.page.TableDataInfo;
/**
* 设备注册Controller
*
* @author cmvr-iot
* @date 2025-05-08
*/
@RestController
@RequestMapping("/device/register")
@RequiredArgsConstructor
@Api(tags = "设备--注册")
public class DeDeviceRegistrationController extends BaseController
{
@Autowired
private IDeDeviceRegistrationService deDeviceRegistrationService;
private final EdgeSystemService systemService;
/**
* 查询设备注册列表
*/
@ApiOperation("查询设备注册列表")
@PreAuthorize("@ss.hasPermi('device:register:list')")
@GetMapping("/list")
public TableDataInfo list(DeDeviceRegistration deDeviceRegistration)
{
startPage();
List<DeDeviceRegistration> list = deDeviceRegistrationService.selectDeDeviceRegistrationList(deDeviceRegistration);
return getDataTable(list);
}
/**
* 导出设备注册列表
*/
@ApiOperation("导出设备注册列表")
@PreAuthorize("@ss.hasPermi('device:register:export')")
@Log(title = "设备注册", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, DeDeviceRegistration deDeviceRegistration)
{
List<DeDeviceRegistration> list = deDeviceRegistrationService.selectDeDeviceRegistrationList(deDeviceRegistration);
ExcelUtil<DeDeviceRegistration> util = new ExcelUtil<DeDeviceRegistration>(DeDeviceRegistration.class);
util.exportExcel(response, list, "设备注册数据");
}
/**
* 获取设备注册详细信息
*/
@ApiOperation("获取设备注册详细信息")
@PreAuthorize("@ss.hasPermi('device:register:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(deDeviceRegistrationService.selectDeDeviceRegistrationById(id));
}
/**
* 新增设备注册
*/
@ApiOperation("新增设备注册")
@PreAuthorize("@ss.hasPermi('device:register:add')")
@Log(title = "设备注册", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody DeDeviceRegistration deDeviceRegistration)
{
return toAjax(deDeviceRegistrationService.insertDeDeviceRegistration(deDeviceRegistration));
}
/**
* 修改设备注册
*/
@ApiOperation("修改设备注册")
@PreAuthorize("@ss.hasPermi('device:register:edit')")
@Log(title = "设备注册", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody DeDeviceRegistration deDeviceRegistration)
{
return toAjax(deDeviceRegistrationService.updateDeDeviceRegistration(deDeviceRegistration));
}
/**
* 删除设备注册
*/
@ApiOperation("删除设备注册")
@PreAuthorize("@ss.hasPermi('device:register:remove')")
@Log(title = "设备注册", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(deDeviceRegistrationService.deleteDeDeviceRegistrationByIds(ids));
}
}

View File

@ -1,102 +0,0 @@
package com.cmvr.web.controller.device;
import com.cmvr.common.annotation.Log;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.core.page.TableDataInfo;
import com.cmvr.common.enums.BusinessType;
import com.cmvr.common.utils.poi.ExcelUtil;
import com.cmvr.device.domain.DeDeviceTerminalConfig;
import com.cmvr.device.service.IDeDeviceTerminalConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 设备终端配置Controller
*
* @author cmvr-iot
* @date 2025-05-08
*/
@RestController
@RequestMapping("/device/terminal")
@RequiredArgsConstructor
@Api(tags = "设备--终端配置")
public class DeDeviceTerminalConfigController extends BaseController {
@Autowired
private IDeDeviceTerminalConfigService deDeviceTerminalConfigService;
/**
* 查询设备终端配置列表
*/
@ApiOperation("查询设备终端配置列表")
@PreAuthorize("@ss.hasPermi('device:terminal:list')")
@GetMapping("/list")
public TableDataInfo list(DeDeviceTerminalConfig deDeviceTerminalConfig) {
startPage();
List<DeDeviceTerminalConfig> list = deDeviceTerminalConfigService.selectDeDeviceTerminalConfigList(deDeviceTerminalConfig);
return getDataTable(list);
}
/**
* 导出设备终端配置列表
*/
@ApiOperation("导出设备终端配置列表")
@PreAuthorize("@ss.hasPermi('device:terminal:export')")
@Log(title = "设备终端配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, DeDeviceTerminalConfig deDeviceTerminalConfig) {
List<DeDeviceTerminalConfig> list = deDeviceTerminalConfigService.selectDeDeviceTerminalConfigList(deDeviceTerminalConfig);
ExcelUtil<DeDeviceTerminalConfig> util = new ExcelUtil<DeDeviceTerminalConfig>(DeDeviceTerminalConfig.class);
util.exportExcel(response, list, "设备终端配置数据");
}
/**
* 获取设备终端配置详细信息
*/
@ApiOperation("获取设备终端配置详细信息")
@PreAuthorize("@ss.hasPermi('device:terminal:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id) {
return success(deDeviceTerminalConfigService.selectDeDeviceTerminalConfigById(id));
}
/**
* 新增设备终端配置
*/
@ApiOperation("新增设备终端配置")
@PreAuthorize("@ss.hasPermi('device:terminal:add')")
@Log(title = "设备终端配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody DeDeviceTerminalConfig deDeviceTerminalConfig) {
return toAjax(deDeviceTerminalConfigService.insertDeDeviceTerminalConfig(deDeviceTerminalConfig));
}
/**
* 修改设备终端配置
*/
@ApiOperation("修改设备终端数据")
@PreAuthorize("@ss.hasPermi('device:terminal:edit')")
@Log(title = "设备终端配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody DeDeviceTerminalConfig deDeviceTerminalConfig) {
return toAjax(deDeviceTerminalConfigService.updateDeDeviceTerminalConfig(deDeviceTerminalConfig));
}
/**
* 删除设备终端配置
*/
@ApiOperation("删除设备终端配置")
@PreAuthorize("@ss.hasPermi('device:terminal:remove')")
@Log(title = "设备终端配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids) {
return toAjax(deDeviceTerminalConfigService.deleteDeDeviceTerminalConfigByIds(ids));
}
}

View File

@ -1,105 +0,0 @@
package com.cmvr.web.controller.device;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import com.cmvr.common.annotation.Log;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.enums.BusinessType;
import com.cmvr.common.utils.poi.ExcelUtil;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cmvr.device.domain.DeRobotConfig;
import com.cmvr.device.service.IDeRobotConfigService;
import static com.cmvr.common.core.domain.AjaxResult.success;
/**
* 机器人配置Controller
*
* @author cmvr-iot
* @date 2025-09-09
*/
@RestController
@RequestMapping("/device/robot")
public class DeRobotConfigController extends BaseController
{
@Autowired
private IDeRobotConfigService deRobotConfigService;
/**
* 查询机器人配置列表
*/
@PreAuthorize("@ss.hasPermi('device:robot:list')")
@GetMapping("/list")
public AjaxResult list(DeRobotConfig deRobotConfig)
{
List<DeRobotConfig> list = deRobotConfigService.selectDeRobotConfigList(deRobotConfig);
return success(list);
}
/**
* 导出机器人配置列表
*/
@PreAuthorize("@ss.hasPermi('device:robot:export')")
@Log(title = "机器人配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, DeRobotConfig deRobotConfig)
{
List<DeRobotConfig> list = deRobotConfigService.selectDeRobotConfigList(deRobotConfig);
ExcelUtil<DeRobotConfig> util = new ExcelUtil<DeRobotConfig>(DeRobotConfig.class);
util.exportExcel(response, list, "机器人配置数据");
}
/**
* 获取机器人配置详细信息
*/
@PreAuthorize("@ss.hasPermi('device:robot:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(deRobotConfigService.selectDeRobotConfigById(id));
}
/**
* 新增机器人配置
*/
@PreAuthorize("@ss.hasPermi('device:robot:add')")
@Log(title = "机器人配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody DeRobotConfig deRobotConfig)
{
return toAjax(deRobotConfigService.insertDeRobotConfig(deRobotConfig));
}
/**
* 修改机器人配置
*/
@PreAuthorize("@ss.hasPermi('device:robot:edit')")
@Log(title = "机器人配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody DeRobotConfig deRobotConfig)
{
return toAjax(deRobotConfigService.updateDeRobotConfig(deRobotConfig));
}
/**
* 删除机器人配置
*/
@PreAuthorize("@ss.hasPermi('device:robot:remove')")
@Log(title = "机器人配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(deRobotConfigService.deleteDeRobotConfigByIds(ids));
}
}

View File

@ -1,114 +0,0 @@
package com.cmvr.web.controller.inspection;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cmvr.common.annotation.Log;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.enums.BusinessType;
import com.cmvr.inspection.domain.InspectionMap;
import com.cmvr.inspection.domain.vo.InspectionMapVo;
import com.cmvr.inspection.service.IInspectionMapService;
import com.cmvr.common.utils.poi.ExcelUtil;
import com.cmvr.common.core.page.TableDataInfo;
/**
* 巡检地图Controller
*
* @author cmvr-iot
* @since 2026-05-29
*/
@RestController
@RequestMapping("/inspection/map")
@Api(tags = "智能巡检--地图管理")
public class InspectionMapController extends BaseController
{
@Autowired
private IInspectionMapService inspectionMapService;
/**
* 查询巡检地图列表
*/
@ApiOperation("查询巡检地图列表")
@PreAuthorize("@ss.hasPermi('inspection:map:list')")
@GetMapping("/list")
public TableDataInfo list(InspectionMap inspectionMap)
{
startPage();
List<InspectionMapVo> list = inspectionMapService.selectInspectionMapList(inspectionMap);
return getDataTable(list);
}
/**
* 导出巡检地图列表
*/
@ApiOperation("导出巡检地图列表")
@PreAuthorize("@ss.hasPermi('inspection:map:export')")
@Log(title = "巡检地图", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, InspectionMap inspectionMap)
{
List<InspectionMapVo> list = inspectionMapService.selectInspectionMapList(inspectionMap);
ExcelUtil<InspectionMapVo> util = new ExcelUtil<>(InspectionMapVo.class);
util.exportExcel(response, list, "巡检地图数据");
}
/**
* 获取巡检地图详细信息
*/
@ApiOperation("获取巡检地图详细信息")
@PreAuthorize("@ss.hasPermi('inspection:map:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(inspectionMapService.selectInspectionMapById(id));
}
/**
* 新增巡检地图
*/
@ApiOperation("新增巡检地图")
@PreAuthorize("@ss.hasPermi('inspection:map:add')")
@Log(title = "巡检地图", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody InspectionMap inspectionMap)
{
return toAjax(inspectionMapService.insertInspectionMap(inspectionMap));
}
/**
* 修改巡检地图
*/
@ApiOperation("修改巡检地图")
@PreAuthorize("@ss.hasPermi('inspection:map:edit')")
@Log(title = "巡检地图", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody InspectionMap inspectionMap)
{
return toAjax(inspectionMapService.updateInspectionMap(inspectionMap));
}
/**
* 删除巡检地图
*/
@ApiOperation("删除巡检地图")
@PreAuthorize("@ss.hasPermi('inspection:map:remove')")
@Log(title = "巡检地图", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(inspectionMapService.deleteInspectionMapByIds(ids));
}
}

View File

@ -1,260 +0,0 @@
package com.cmvr.web.controller.inspection;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import cmvr.msgs.AgvUtils;
import com.cmvr.inspection.domain.vo.InspectionRobotVo;
import io.swagger.annotations.*;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.cmvr.common.annotation.Log;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.enums.BusinessType;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.service.IInspectionRobotService;
import com.cmvr.common.utils.poi.ExcelUtil;
import com.cmvr.common.core.page.TableDataInfo;
/**
* 巡检机器人Controller
*
* @author cmvr-iot
* @since 2026-05-29
*/
@RestController
@RequestMapping("/inspection/robot")
@Api(tags = "智能巡检--机器人管理")
public class InspectionRobotController extends BaseController
{
@Autowired
private IInspectionRobotService inspectionRobotService;
/**
* 查询巡检机器人列表
*/
@ApiOperation("查询巡检机器人列表")
@PreAuthorize("@ss.hasPermi('inspection:robot:list')")
@GetMapping("/list")
public TableDataInfo list(InspectionRobot inspectionRobot)
{
startPage();
List<InspectionRobotVo> list = inspectionRobotService.selectInspectionRobotList(inspectionRobot);
return getDataTable(list);
}
/**
* 导出巡检机器人列表
*/
@ApiOperation("导出巡检机器人列表")
@PreAuthorize("@ss.hasPermi('inspection:robot:export')")
@Log(title = "巡检机器人", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, InspectionRobot inspectionRobot)
{
List<InspectionRobotVo> list = inspectionRobotService.selectInspectionRobotList(inspectionRobot);
ExcelUtil<InspectionRobotVo> util = new ExcelUtil<>(InspectionRobotVo.class);
util.exportExcel(response, list, "巡检机器人数据");
}
/**
* 获取巡检机器人详细信息
*/
@ApiOperation("获取巡检机器人详细信息")
@PreAuthorize("@ss.hasPermi('inspection:robot:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(inspectionRobotService.selectInspectionRobotById(id));
}
@ApiOperation("查询机器人自动发现的设备")
@PreAuthorize("@ss.hasPermi('inspection:robot:query')")
@GetMapping("/{id}/devices")
public AjaxResult devices(@PathVariable("id") String id)
{
return success(inspectionRobotService.selectRobotDevices(id));
}
@ApiOperation("按机器人ID查询自动发现的设备")
@PreAuthorize("@ss.hasPermi('inspection:robot:query')")
@GetMapping("/devices")
public AjaxResult devicesByRobotId(
@RequestParam @ApiParam(value = "机器人永久唯一ID", required = true) String robotId,
@RequestParam(required = false) @ApiParam("设备类型") Integer deviceKind,
@RequestParam(defaultValue = "true") @ApiParam("是否只查询在线且启用的设备") boolean onlineOnly)
{
return success(inspectionRobotService.selectRobotDevicesByRobotId(robotId, deviceKind, onlineOnly));
}
/**
* 新增巡检机器人
*/
@ApiOperation("新增巡检机器人")
@PreAuthorize("@ss.hasPermi('inspection:robot:add')")
@Log(title = "巡检机器人", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody InspectionRobot inspectionRobot)
{
return toAjax(inspectionRobotService.insertInspectionRobot(inspectionRobot));
}
/**
* 修改巡检机器人
*/
@ApiOperation("修改巡检机器人")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "巡检机器人", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody InspectionRobot inspectionRobot)
{
return toAjax(inspectionRobotService.updateInspectionRobot(inspectionRobot));
}
/**
* 删除巡检机器人
*/
@ApiOperation("删除巡检机器人")
@PreAuthorize("@ss.hasPermi('inspection:robot:remove')")
@Log(title = "巡检机器人", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(inspectionRobotService.deleteInspectionRobotByIds(ids));
}
/**
* 获取机器人地图列表(从AGV获取)
*/
@ApiOperation("获取机器人地图列表")
@PreAuthorize("@ss.hasPermi('inspection:robot:query')")
@GetMapping("/{robotId}/maps")
public AjaxResult getRobotMapList(@PathVariable String robotId)
{
List<String> robotMapList = inspectionRobotService.getRobotMapList(robotId);
return success(robotMapList);
}
/**
* 绑定机器人地图
*/
@ApiOperation("绑定机器人地图")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "绑定机器人地图", businessType = BusinessType.UPDATE)
@PostMapping("/{robotId}/bind-map/{mapId}")
public AjaxResult bindRobotMap(@PathVariable String robotId,
@PathVariable String mapId)
{
return toAjax(inspectionRobotService.bindRobotMap(robotId, mapId));
}
/**
* 从机器人下载地图
*/
@ApiOperation("从机器人下载地图")
@PreAuthorize("@ss.hasPermi('inspection:robot:add')")
@Log(title = "从机器人下载地图", businessType = BusinessType.INSERT)
@GetMapping("/{robotId}/download-map")
public AjaxResult downloadMapFromRobot(
@PathVariable @ApiParam("机器人ID") String robotId,
@RequestParam(value = "mapName") @ApiParam("地图名称") String mapName
)
{
Object mapId = inspectionRobotService.downloadMapFromRobot(robotId, mapName);
return success(mapId);
}
/**
* 上传地图(从源机器人下载到目标机器人)
*/
@ApiOperation("上传地图到机器人")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "上传地图到机器人", businessType = BusinessType.OTHER)
@PostMapping("/{robotId}/upload-map-from-source")
public AjaxResult uploadMapFromSource(@PathVariable String robotId)
{
String result = inspectionRobotService.uploadMapFromSource(robotId);
return success(result);
}
/**
* 同步机器人状态
*/
@ApiOperation("同步机器人状态")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "同步机器人状态", businessType = BusinessType.UPDATE)
@PostMapping("/{robotId}/sync-status")
public AjaxResult syncRobotStatus(@PathVariable String robotId)
{
return success(inspectionRobotService.syncRobotStatus(robotId));
}
/**
* 获取机器人实时位置
*/
@ApiOperation("获取机器人实时位置")
@PreAuthorize("@ss.hasPermi('inspection:robot:query')")
@GetMapping("/{robotId}/location")
public AjaxResult getRobotRealtimeLocation(@PathVariable String robotId)
{
AgvUtils.AgvPose2d location = inspectionRobotService.getRobotRealtimeLocation(robotId);
return success(location);
}
/**
* 导航到指定位置(坐标)
*/
@ApiOperation("导航到指定位置")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "导航到指定位置", businessType = BusinessType.OTHER)
@PostMapping("/{robotId}/navigate-to-position")
public AjaxResult navigateToPosition(
@PathVariable @ApiParam("机器人ID") String robotId,
@RequestParam @ApiParam("X坐标(米)") Double x,
@RequestParam @ApiParam("Y坐标(米)") Double y,
@RequestParam(required = false) @ApiParam("朝向角(弧度)") Double theta
)
{
inspectionRobotService.navigateToPosition(
robotId, x, y, theta
);
return success();
}
/**
* 开始建图
*/
@ApiOperation("开始建图")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "开始建图", businessType = BusinessType.OTHER)
@PostMapping("/{robotId}/start-mapping")
public AjaxResult startMapping(
@PathVariable @ApiParam("机器人ID") String robotId,
@RequestParam @ApiParam("建图维度(0:未指定 1:2D 2:3D 3:2D+3D)") int dimension,
@RequestParam(required = false) @ApiParam("地图名称(可选)") String mapName,
@RequestParam(defaultValue = "true") @ApiParam("是否实时建图") boolean realTime
)
{
String sessionId = inspectionRobotService.startMapping(robotId, dimension, mapName, realTime);
java.util.Map<String, Object> result = new java.util.HashMap<>();
result.put("sessionId", sessionId);
return success(result);
}
/**
* 停止建图
*/
@ApiOperation("停止建图")
@PreAuthorize("@ss.hasPermi('inspection:robot:edit')")
@Log(title = "停止建图", businessType = BusinessType.OTHER)
@PostMapping("/{robotId}/stop-mapping")
public AjaxResult stopMapping(@PathVariable @ApiParam("机器人ID") String robotId)
{
inspectionRobotService.stopMapping(robotId);
return success();
}
}

View File

@ -0,0 +1,52 @@
package com.cmvr.web.controller.resource;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.core.page.TableDataInfo;
import com.cmvr.resource.domain.ResourceDevice;
import com.cmvr.resource.service.ResourceDeviceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/resource/devices")
@Api(tags = "资源中心-设备管理")
public class ResourceDeviceController extends BaseController {
private final ResourceDeviceService deviceService;
public ResourceDeviceController(ResourceDeviceService deviceService) {
this.deviceService = deviceService;
}
@GetMapping
@ApiOperation("查询设备列表")
@PreAuthorize("@ss.hasPermi('resource:device:list')")
public TableDataInfo list(ResourceDevice query,
@RequestParam(defaultValue = "true") boolean onlineOnly) {
startPage();
return getDataTable(deviceService.list(query, onlineOnly));
}
@GetMapping("/{id}")
@ApiOperation("查询设备详情")
@PreAuthorize("@ss.hasPermi('resource:device:list')")
public AjaxResult get(@PathVariable String id) {
return success(deviceService.get(id));
}
@PutMapping("/{id}")
@ApiOperation("修改设备名称、用途和启用状态")
@PreAuthorize("@ss.hasPermi('resource:device:edit')")
public AjaxResult update(@PathVariable String id, @RequestBody ResourceDevice device) {
device.setId(id);
return toAjax(deviceService.updateBusinessConfig(device));
}
}

View File

@ -0,0 +1,73 @@
package com.cmvr.web.controller.resource;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.core.page.TableDataInfo;
import com.cmvr.resource.domain.ResourceMap;
import com.cmvr.resource.service.ResourceMapService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Arrays;
@RestController
@RequestMapping("/resource/maps")
@Api(tags = "资源中心-地图管理")
public class ResourceMapController extends BaseController {
private final ResourceMapService mapService;
public ResourceMapController(ResourceMapService mapService) {
this.mapService = mapService;
}
@GetMapping
@ApiOperation("查询地图列表")
@PreAuthorize("@ss.hasPermi('resource:map:list')")
public TableDataInfo list(ResourceMap query) {
startPage();
return getDataTable(mapService.list(query));
}
@GetMapping("/robot/{robotId}")
@ApiOperation("查询机器人可用地图")
@PreAuthorize("@ss.hasPermi('resource:map:list')")
public AjaxResult listByRobot(@PathVariable String robotId) {
return success(mapService.listByRobotId(robotId));
}
@GetMapping("/{id}")
@ApiOperation("查询地图详情")
@PreAuthorize("@ss.hasPermi('resource:map:query')")
public AjaxResult get(@PathVariable String id) {
return success(mapService.get(id));
}
@PostMapping
@ApiOperation("新增地图")
@PreAuthorize("@ss.hasPermi('resource:map:add')")
public AjaxResult create(@RequestBody ResourceMap map) {
return toAjax(mapService.create(map));
}
@PutMapping
@ApiOperation("修改地图")
@PreAuthorize("@ss.hasPermi('resource:map:edit')")
public AjaxResult update(@RequestBody ResourceMap map) {
return toAjax(mapService.update(map));
}
@DeleteMapping("/{ids}")
@ApiOperation("删除地图")
@PreAuthorize("@ss.hasPermi('resource:map:remove')")
public AjaxResult remove(@PathVariable String[] ids) {
return toAjax(mapService.remove(Arrays.asList(ids)));
}
}

View File

@ -0,0 +1,188 @@
package com.cmvr.web.controller.resource;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.core.page.TableDataInfo;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.service.ResourceRobotService;
import com.cmvr.resource.service.ResourceRobotMapService;
import com.cmvr.resource.service.ResourceRobotControlService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/resource/robots")
@Api(tags = "资源中心-机器人管理")
public class ResourceRobotController extends BaseController {
private final ResourceRobotService resourceRobotService;
private final ResourceRobotMapService robotMapService;
private final ResourceRobotControlService controlService;
public ResourceRobotController(ResourceRobotService resourceRobotService,
ResourceRobotMapService robotMapService,
ResourceRobotControlService controlService) {
this.resourceRobotService = resourceRobotService;
this.robotMapService = robotMapService;
this.controlService = controlService;
}
@GetMapping
@ApiOperation("查询机器人列表")
@PreAuthorize("@ss.hasPermi('resource:robot:list')")
public TableDataInfo list(ResourceRobot query) {
startPage();
return getDataTable(resourceRobotService.list(query));
}
@GetMapping("/{robotId}")
@ApiOperation("根据永久机器人ID查询机器人")
@PreAuthorize("@ss.hasPermi('resource:robot:query')")
public AjaxResult get(@PathVariable String robotId) {
ResourceRobot robot = resourceRobotService.getByRobotId(robotId);
return robot == null ? error("机器人不存在:" + robotId) : success(robot);
}
@GetMapping("/{robotId}/devices")
@ApiOperation("查询机器人设备")
@PreAuthorize("@ss.hasPermi('resource:device:list')")
public AjaxResult devices(@PathVariable String robotId,
@RequestParam(required = false) Integer deviceKind,
@RequestParam(defaultValue = "true") @ApiParam("是否只返回在线且启用的设备") boolean onlineOnly) {
return success(resourceRobotService.listDevices(robotId, deviceKind, onlineOnly));
}
@GetMapping("/id/{id}")
@ApiOperation("根据数据主键查询机器人")
@PreAuthorize("@ss.hasPermi('resource:robot:query')")
public AjaxResult getById(@PathVariable String id) {
return success(resourceRobotService.getById(id));
}
@GetMapping("/id/{id}/devices")
@ApiOperation("根据机器人数据主键查询设备")
@PreAuthorize("@ss.hasPermi('resource:device:list')")
public AjaxResult devicesById(@PathVariable String id,
@RequestParam(required = false) Integer deviceKind,
@RequestParam(defaultValue = "false") boolean onlineOnly) {
return success(resourceRobotService.listDevicesByRobotRecord(id, deviceKind, onlineOnly));
}
@PostMapping
@ApiOperation("登记机器人")
@PreAuthorize("@ss.hasPermi('resource:robot:add')")
public AjaxResult create(@RequestBody ResourceRobot robot) {
return toAjax(resourceRobotService.create(robot));
}
@PutMapping
@ApiOperation("修改机器人")
@PreAuthorize("@ss.hasPermi('resource:robot:edit')")
public AjaxResult update(@RequestBody ResourceRobot robot) {
return toAjax(resourceRobotService.update(robot));
}
@DeleteMapping("/{ids}")
@ApiOperation("归档机器人")
@PreAuthorize("@ss.hasPermi('resource:robot:remove')")
public AjaxResult archive(@PathVariable String[] ids) {
return toAjax(resourceRobotService.archive(ids));
}
@GetMapping("/{robotId}/maps")
@ApiOperation("查询机器人绑定的地图")
@PreAuthorize("@ss.hasPermi('resource:map:list')")
public AjaxResult maps(@PathVariable String robotId) {
return success(robotMapService.listMaps(robotId));
}
@PostMapping("/{robotId}/maps/{mapId}")
@ApiOperation("绑定机器人地图")
@PreAuthorize("@ss.hasPermi('resource:robot:edit')")
public AjaxResult bindMap(@PathVariable String robotId, @PathVariable String mapId,
@RequestParam(defaultValue = "false") boolean makeDefault) {
return toAjax(robotMapService.bind(robotId, mapId, makeDefault));
}
@DeleteMapping("/{robotId}/maps/{mapId}")
@ApiOperation("解除机器人地图绑定")
@PreAuthorize("@ss.hasPermi('resource:robot:edit')")
public AjaxResult unbindMap(@PathVariable String robotId, @PathVariable String mapId) {
return toAjax(robotMapService.unbind(robotId, mapId));
}
@GetMapping("/{robotRef}/remote-maps")
@ApiOperation("查询边缘机器人地图列表")
@PreAuthorize("@ss.hasPermi('resource:map:list')")
public AjaxResult remoteMaps(@PathVariable String robotRef) {
return success(controlService.listRemoteMaps(robotRef));
}
@GetMapping("/{robotRef}/download-map")
@ApiOperation("下载边缘机器人地图")
@PreAuthorize("@ss.hasPermi('resource:map:add')")
public AjaxResult downloadMap(@PathVariable String robotRef, @RequestParam String mapName) {
return success(controlService.downloadMap(robotRef, mapName));
}
@PostMapping("/{robotRef}/upload-active-map")
@ApiOperation("上传当前地图到边缘机器人")
@PreAuthorize("@ss.hasPermi('resource:map:edit')")
public AjaxResult uploadActiveMap(@PathVariable String robotRef) {
return success(controlService.uploadActiveMap(robotRef));
}
@PostMapping("/{robotRef}/sync-runtime")
@ApiOperation("同步机器人实时状态")
@PreAuthorize("@ss.hasPermi('resource:robot:edit')")
public AjaxResult syncRuntime(@PathVariable String robotRef) {
return success(controlService.syncRuntimeState(robotRef));
}
@GetMapping("/{robotRef}/location")
@ApiOperation("查询机器人实时位置")
@PreAuthorize("@ss.hasPermi('resource:robot:query')")
public AjaxResult location(@PathVariable String robotRef) {
return success(controlService.getRealtimeLocation(robotRef));
}
@PostMapping("/{robotRef}/navigate-to-position")
@ApiOperation("控制机器人导航到坐标")
@PreAuthorize("@ss.hasPermi('resource:robot:edit')")
public AjaxResult navigateToPosition(@PathVariable String robotRef,
@RequestParam Double x,
@RequestParam Double y,
@RequestParam(required = false) Double theta) {
controlService.navigateToPosition(robotRef, x, y, theta);
return success();
}
@PostMapping("/{robotRef}/start-mapping")
@ApiOperation("控制机器人开始建图")
@PreAuthorize("@ss.hasPermi('resource:map:add')")
public AjaxResult startMapping(@PathVariable String robotRef,
@RequestParam int dimension,
@RequestParam(required = false) String mapName,
@RequestParam(defaultValue = "true") boolean realTime) {
return success(java.util.Map.of("sessionId",
controlService.startMapping(robotRef, dimension, mapName, realTime)));
}
@PostMapping("/{robotRef}/stop-mapping")
@ApiOperation("控制机器人结束建图")
@PreAuthorize("@ss.hasPermi('resource:map:edit')")
public AjaxResult stopMapping(@PathVariable String robotRef) {
controlService.stopMapping(robotRef);
return success();
}
}

View File

@ -0,0 +1,75 @@
package com.cmvr.web.controller.resource;
import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.common.core.page.TableDataInfo;
import com.cmvr.resource.domain.ResourceTerminal;
import com.cmvr.resource.service.ResourceTerminalService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.DeleteMapping;
import java.util.List;
@RestController
@RequestMapping("/resource/terminals")
@Api(tags = "资源中心-边缘终端")
public class ResourceTerminalController extends BaseController {
private final ResourceTerminalService terminalService;
public ResourceTerminalController(ResourceTerminalService terminalService) {
this.terminalService = terminalService;
}
@GetMapping
@ApiOperation("查询边缘终端列表")
@PreAuthorize("@ss.hasPermi('resource:terminal:list')")
public TableDataInfo list(ResourceTerminal query) {
startPage();
return getDataTable(terminalService.list(query));
}
@GetMapping("/{id}")
@ApiOperation("查询边缘终端详情")
@PreAuthorize("@ss.hasPermi('resource:terminal:list')")
public AjaxResult get(@PathVariable String id) { return success(terminalService.get(id)); }
@PostMapping
@ApiOperation("新增边缘终端")
@PreAuthorize("@ss.hasPermi('resource:terminal:add')")
public AjaxResult create(@RequestBody ResourceTerminal terminal) {
return toAjax(terminalService.create(terminal));
}
@PutMapping
@ApiOperation("修改边缘终端")
@PreAuthorize("@ss.hasPermi('resource:terminal:edit')")
public AjaxResult update(@RequestBody ResourceTerminal terminal) {
return toAjax(terminalService.update(terminal));
}
@GetMapping("/{id}/robots")
@ApiOperation("查询边缘终端关联的机器人")
@PreAuthorize("@ss.hasPermi('resource:terminal:list')")
public AjaxResult robots(@PathVariable String id) { return success(terminalService.listRobotIds(id)); }
@PutMapping("/{id}/robots")
@ApiOperation("更新边缘终端关联的机器人")
@PreAuthorize("@ss.hasPermi('resource:terminal:edit')")
public AjaxResult replaceRobots(@PathVariable String id, @RequestBody List<String> robotIds) {
terminalService.replaceRobots(id, robotIds);
return success();
}
@DeleteMapping("/{ids}")
@ApiOperation("删除边缘终端")
@PreAuthorize("@ss.hasPermi('resource:terminal:remove')")
public AjaxResult remove(@PathVariable String[] ids) { return toAjax(terminalService.remove(ids)); }
}

View File

@ -57,6 +57,18 @@ public class OpenApiConfig
return group("00-all", "全部接口", "/**"); return group("00-all", "全部接口", "/**");
} }
@Bean
public GroupedOpenApi resourceCenterApi()
{
return GroupedOpenApi.builder()
.group("10-resource-center")
.displayName("资源中心")
.pathsToMatch("/resource/**")
.addOperationCustomizer(legacySwaggerOperationCustomizer())
.addOpenApiCustomizer(legacyTagOrderCustomizer())
.build();
}
private GroupedOpenApi group(String id, String displayName, String... paths) private GroupedOpenApi group(String id, String displayName, String... paths)
{ {
return GroupedOpenApi.builder() return GroupedOpenApi.builder()

View File

@ -1,6 +1,6 @@
server: server:
# 服务器的HTTP端口,默认为8080 # 服务器的HTTP端口,默认为8080
port: 13080 port: 14080
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
@ -9,7 +9,7 @@ spring:
druid: druid:
# 主库数据源 # 主库数据源
master: master:
url: jdbc:mysql://192.168.28.10:3306/cmvr-iot-dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 url: jdbc:mysql://192.168.28.10:3306/${CMVR_DB_NAME:cmvr-iot-resource-dev}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root username: root
password: cmvr2468 password: cmvr2468
# 从库数据源 # 从库数据源
@ -74,7 +74,7 @@ spring:
# 端口,默认为6379 # 端口,默认为6379
port: 6379 port: 6379
# 数据库索引 # 数据库索引
database: 0 database: ${CMVR_REDIS_DATABASE:1}
# 密码 # 密码
password: cmvr2468 password: cmvr2468
# 连接超时时间 # 连接超时时间
@ -138,3 +138,11 @@ media-analysis:
api-key: ${MEDIA_ANALYSIS_API_KEY:} api-key: ${MEDIA_ANALYSIS_API_KEY:}
connect-timeout-ms: 5000 connect-timeout-ms: 5000
read-timeout-ms: 600000 read-timeout-ms: 600000
# Isolate the resource-center development instance from the currently running robot gateway.
cmvr:
resource:
robot-state:
enabled: false
quic:
enabled: false

View File

@ -1,6 +1,6 @@
server: server:
# 服务器的HTTP端口,默认为8080 # 服务器的HTTP端口,默认为8080
port: 13080 port: 14080
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
@ -9,7 +9,7 @@ spring:
druid: druid:
# 主库数据源 # 主库数据源
master: master:
url: jdbc:mysql://192.168.28.10:3306/cmvr-iot-dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 url: jdbc:mysql://192.168.28.10:3306/${CMVR_DB_NAME:cmvr-iot-resource-dev}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root username: root
password: cmvr2468 password: cmvr2468
# 从库数据源 # 从库数据源
@ -74,7 +74,7 @@ spring:
# 端口,默认为6379 # 端口,默认为6379
port: 6379 port: 6379
# 数据库索引 # 数据库索引
database: 0 database: ${CMVR_REDIS_DATABASE:1}
# 密码 # 密码
password: cmvr2468 password: cmvr2468
# 连接超时时间 # 连接超时时间
@ -138,3 +138,11 @@ media-analysis:
api-key: ${MEDIA_ANALYSIS_API_KEY:cmvr-analysis-2026-c65ec181d5ab4a49aef34e6fd1be6704} api-key: ${MEDIA_ANALYSIS_API_KEY:cmvr-analysis-2026-c65ec181d5ab4a49aef34e6fd1be6704}
connect-timeout-ms: 5000 connect-timeout-ms: 5000
read-timeout-ms: 600000 read-timeout-ms: 600000
# Isolate the resource-center development instance from the currently running robot gateway.
#cmvr:
# resource:
# robot-state:
# enabled: false
# quic:
# enabled: false

View File

@ -127,7 +127,7 @@ xss:
# Integrated QUIC gateway and in-process node synchronization. # Integrated QUIC gateway and in-process node synchronization.
cmvr: cmvr:
inspection: resource:
robot-state: robot-state:
enabled: true enabled: true
initial-delay-ms: 3000 initial-delay-ms: 3000

View File

@ -3,12 +3,10 @@ package com.cmvr.edge.client.service;
import cmvr.api.Common; import cmvr.api.Common;
import cmvr.api.SafetyCommand; import cmvr.api.SafetyCommand;
import cmvr.api.SystemCommand; import cmvr.api.SystemCommand;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.edge.client.model.system.EdgeSystemJsonCommandVO; import com.cmvr.edge.client.model.system.EdgeSystemJsonCommandVO;
import com.cmvr.edge.client.model.system.EdgeSystemUpdateParamsVO; import com.cmvr.edge.client.model.system.EdgeSystemUpdateParamsVO;
import com.cmvr.edge.client.model.system.EdgeActionQueueVO; import com.cmvr.edge.client.model.system.EdgeActionQueueVO;
import java.util.List;
/** /**
* 边缘系统服务接口 * 边缘系统服务接口
@ -41,7 +39,5 @@ public interface EdgeSystemService {
String robotId, String reason, Integer timeoutMs); String robotId, String reason, Integer timeoutMs);
public List<DeDeviceRegistration> deviceList(String robotId);
public String stopAll(String robotId); public String stopAll(String robotId);
} }

View File

@ -9,7 +9,6 @@ import cmvr.api.SystemCommand;
import cmvr.api.SystemServiceGrpc; import cmvr.api.SystemServiceGrpc;
import cmvr.api.SafetyCommand; import cmvr.api.SafetyCommand;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.edge.client.manage.GrpcServiceManager; import com.cmvr.edge.client.manage.GrpcServiceManager;
import com.cmvr.edge.client.model.system.EdgeSystemJsonCommandVO; import com.cmvr.edge.client.model.system.EdgeSystemJsonCommandVO;
import com.cmvr.edge.client.model.system.EdgeSystemUpdateParamsVO; import com.cmvr.edge.client.model.system.EdgeSystemUpdateParamsVO;
@ -24,7 +23,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.HashSet; import java.util.HashSet;
@ -373,72 +371,6 @@ public class EdgeSystemServiceImpl implements EdgeSystemService {
} }
@Override
public List<DeDeviceRegistration> deviceList(String robotId) {
try {
SystemServiceGrpc.SystemServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
robotId, SystemServiceGrpc.SystemServiceBlockingStub.class);
SystemCommand.GetDeviceListCommand.Feedback response = stub.getDeviceList(
SystemCommand.GetDeviceListCommand.Request.newBuilder().build());
List<SystemCommand.SystemDeviceInfo> deviceList = response.getDeviceListList();
List<DeDeviceRegistration> devices = new ArrayList<>(deviceList.size());
for (SystemCommand.SystemDeviceInfo device : deviceList) {
DeDeviceRegistration registration = new DeDeviceRegistration();
registration.setDeviceCode(device.getDeviceId());
registration.setDeviceModel(toDeviceModel(device));
registration.setIdDeDeviceTerminalConfig(robotId);
devices.add(registration);
}
logger.info("获取到设备列表,数量:{},设备管理器:{} {}",
devices.size(), response.getManagerName(), response.getManagerVersion());
return devices;
} catch (Exception e) {
logger.error("调用设备列表接口异常", e);
throw new GlobalException("获取设备列表失败: " + e.getMessage());
}
}
private String toDeviceModel(SystemCommand.SystemDeviceInfo device) {
switch (device.getDeviceType()) {
case SYSTEM_DEVICE_TYPE_AGV:
return "AGV";
case SYSTEM_DEVICE_TYPE_ARM:
return "ARM";
case SYSTEM_DEVICE_TYPE_BATTERY:
return "Battery";
case SYSTEM_DEVICE_TYPE_BIO_HEAD:
return "BioHead";
case SYSTEM_DEVICE_TYPE_CAMERA:
return "Camera";
case SYSTEM_DEVICE_TYPE_CAN_BUS:
return "CanBus";
case SYSTEM_DEVICE_TYPE_DEX_HAND:
return "DexHand";
case SYSTEM_DEVICE_TYPE_GRIPPER:
return "Gripper";
case SYSTEM_DEVICE_TYPE_MICROPHONE:
return "Microphone";
case SYSTEM_DEVICE_TYPE_MOTOR:
return "Motor";
case SYSTEM_DEVICE_TYPE_MOTOR_SYSTEM:
return "MotorSystem";
case SYSTEM_DEVICE_TYPE_MUJOCO_VIEWER:
return "MujocoViewer";
case SYSTEM_DEVICE_TYPE_MUJOCO_WORLD:
return "MujocoWorld";
case SYSTEM_DEVICE_TYPE_ROBOT:
return "Robot";
case SYSTEM_DEVICE_TYPE_SPEAKER:
return "Speaker";
case SYSTEM_DEVICE_TYPE_UNSPECIFIED:
case UNRECOGNIZED:
default:
return device.getTypeName().isEmpty() ? "Unknown" : device.getTypeName();
}
}
@Override @Override
public String stopAll(String robotId) { public String stopAll(String robotId) {
SystemServiceGrpc.SystemServiceBlockingStub stub = grpcServiceManager.getGrpcClient( SystemServiceGrpc.SystemServiceBlockingStub stub = grpcServiceManager.getGrpcClient(

View File

@ -1,65 +0,0 @@
package com.cmvr.device.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
/**
* 设备配置信息对象 de_device_profile
*
* @author cmvr-iot
* @since 2025-05-09
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("设备配置类")
@EqualsAndHashCode(callSuper = false)
public class DeDeviceProfile extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty("id")
@TableId(value = "id",type = IdType.ASSIGN_UUID)
private String id;
/** 设备id */
@Excel(name = "设备id")
@ApiModelProperty("设备id")
private String idDeDeviceRegistration;
/** 设备终端id */
@Excel(name = "设备终端id")
@ApiModelProperty("设备终端id")
private String id_de_device_terminal_config;
/** 设备类型 */
@Excel(name = "设备类型")
@ApiModelProperty("设备类型")
private String deviceModel;
/** 参数名称 */
@Excel(name = "参数名称")
@ApiModelProperty("参数名称")
private String paramName;
/** 参数默认值 */
@Excel(name = "参数默认值")
@ApiModelProperty("参数默认值")
private String defaultValue;
/** 参数类型 */
@Excel(name = "参数类型")
@ApiModelProperty("参数类型")
private String paramType;
}

View File

@ -1,71 +0,0 @@
package com.cmvr.device.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
/**
* 设备注册对象 de_device_registration
*
* @author cmvr-iot
* @date 2025-05-08
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("设备注册")
public class DeDeviceRegistration extends BaseEntity
{
private static final long serialVersionUID = 1L;
public DeDeviceRegistration(String deviceCode,String deviceModel){
this.deviceCode=deviceCode;
this.deviceModel=deviceModel;
}
/** id */
@ApiModelProperty("id")
@TableId(value = "id",type = IdType.ASSIGN_UUID)
private String id;
/** 备注 */
@Excel(name = "备注")
@ApiModelProperty("备注")
private String remake;
/** 设备终端配置id */
@Excel(name = "设备终端配置id")
@ApiModelProperty("设备终端id")
private String idDeDeviceTerminalConfig;
/** 设备编码 */
@Excel(name = "设备编码")
@ApiModelProperty("设备编码")
private String deviceCode;
/** 设备名称 */
@Excel(name = "设备名称")
@ApiModelProperty("设备名称")
private String deviceName;
/** 设备型号 */
@Excel(name = "设备型号")
@ApiModelProperty("设备型号")
private String deviceModel;
/** 设备状态 */
@Excel(name = "设备状态")
@ApiModelProperty(value = "设备状态 (0正常 1停用)")
private String status;
}

View File

@ -1,95 +0,0 @@
package com.cmvr.device.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import java.util.Date;
/**
* 设备终端配置对象 de_device_terminal_config
*
* @author cmvr-iot
* @date 2025-05-08
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("设备终端配置")
public class DeDeviceTerminalConfig extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty("id")
@TableId(value = "id" , type = IdType.ASSIGN_UUID)
private String id;
/** 备注 */
@Excel(name = "备注")
@ApiModelProperty("备注")
private String remake;
/** 终端名称 */
@Excel(name = "终端名称")
@ApiModelProperty("终端名称")
private String name;
/** 主机 */
@Excel(name = "主机")
@ApiModelProperty("主机")
private String host;
/** 端口号 */
@Excel(name = "端口号")
@ApiModelProperty("端口号")
private Long port;
/** 状态 */
@Excel(name = "状态")
@ApiModelProperty("状态")
private String status;
/** 说明 */
@Excel(name = "说明")
@ApiModelProperty("说明")
private String description;
/** QUIC终端主动注册时使用的稳定节点ID,人工绑定后不可与其他终端重复。 */
@Excel(name = "QUIC节点ID")
@ApiModelProperty("QUIC节点ID")
private String quicNodeId;
/** Gateway从已认证QUIC连接中观察到的源IP。 */
@ApiModelProperty("QUIC连接源IP")
private String quicObservedIp;
/** 当前QUIC注册会话ID。 */
@ApiModelProperty("QUIC会话ID")
private String quicSessionId;
/** 终端本次操作系统启动ID。 */
@ApiModelProperty("QUIC终端启动ID")
private String quicBootId;
/** cmvr-es上报的软件版本。 */
@ApiModelProperty("终端软件版本")
private String quicSoftwareVersion;
/** QUIC在线状态:0离线,1在线;与原有status启停字段相互独立。 */
@ApiModelProperty("QUIC在线状态:0离线,1在线")
private String quicOnlineStatus;
/** 最近一次收到有效QUIC心跳或注册事件的时间。 */
@ApiModelProperty("最近QUIC心跳时间")
private Date quicLastHeartbeatTime;
}

View File

@ -1,52 +0,0 @@
package com.cmvr.device.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 机器人配置对象 de_robot_config
*
* @author cmvr-iot
* @date 2025-09-09
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("机器人配置对象")
public class DeRobotConfig extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty("id")
@TableId(value = "id" , type = IdType.ASSIGN_UUID)
private String id;
/** 配置ID,32位的UUID,可以是设备ID或机器人ID。 */
@Excel(name = "配置ID,32位的UUID,可以是设备ID或机器人ID。")
@ApiModelProperty("配置ID,32位的UUID,可以是设备ID或机器人ID。")
private String configId;
/** 配置类型,区分是设备还是终端 */
@Excel(name = "配置类型,区分是设备还是终端")
@ApiModelProperty(name = "配置类型,区分是设备还是终端")
private String configType;
/** 父配置ID,用于构成树形结构。终端节点为root,设备节点指向其父终端的id */
@Excel(name = "父配置ID,用于构成树形结构。终端节点为root,设备节点指向其父终端的id")
@ApiModelProperty(name = "父配置ID,用于构成树形结构。终端节点为root,设备节点指向其父终端的id")
private String pid;
/** 节点名称,名称可以随意取,不强制唯一 */
@Excel(name = "节点名称,名称可以随意取,不强制唯一")
@ApiModelProperty(name = "节点名称,名称可以随意取,不强制唯一")
private String nodeName;
}

View File

@ -1,52 +0,0 @@
package com.cmvr.device.domain.vo;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("参数视图类")
@EqualsAndHashCode(callSuper = false)
public class ProfileVo extends BaseEntity {
/** 设备类型 */
@Excel(name = "设备类型")
@ApiModelProperty("设备类型")
private String deviceModel;
/** 设备id */
@Excel(name = "设备id")
@ApiModelProperty("设备id")
private String idDeDeviceRegistration;
/** 设备终端id */
@Excel(name = "设备终端id")
@ApiModelProperty("设备终端id")
private String id_de_device_terminal_config;
/** 设备名称 */
@Excel(name = "设备名称")
@ApiModelProperty("设备名称")
private String deviceName;
/** 终端名称 */
@Excel(name = "终端名称")
@ApiModelProperty("终端名称")
private String terminalName;
@Excel(name = "参数列表")
@ApiModelProperty("参数列表")
private List<ParameterVo> parameters;
}

View File

@ -1,89 +0,0 @@
package com.cmvr.device.domain.vo;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel("机械臂相关参数")
public class TeMechanicalArmVo extends BaseEntity {
@Excel(name = "设备id")
@ApiModelProperty("设备id")
private String idDeDeviceRegistration;
@Excel(name = "工作模式")
@ApiModelProperty("工作模式")
private String workModule;
@Excel(name = "位置步进")
@ApiModelProperty("位置步进")
private String posStep;
@Excel(name = "姿态步进")
@ApiModelProperty("姿态步进")
private String postureStep;
@Excel(name = "关节步进")
@ApiModelProperty("关节步进")
private String jointStep;
@Excel(name = "关节1")
@ApiModelProperty("关节1")
private String joint1;
@Excel(name = "关节2")
@ApiModelProperty("关节2")
private String joint2;
@Excel(name = "关节3")
@ApiModelProperty("关节3")
private String joint3;
@Excel(name = "关节4")
@ApiModelProperty("关节4")
private String joint4;
@Excel(name = "关节5")
@ApiModelProperty("关节5")
private String joint5;
@Excel(name = "关节6")
@ApiModelProperty("关节6")
private String joint6;
@Excel(name = "x轴")
@ApiModelProperty("x轴")
private String x;
@Excel(name = "y轴")
@ApiModelProperty("y轴")
private String y;
@Excel(name = "z轴")
@ApiModelProperty("z轴")
private String z;
@Excel(name = "rx轴")
@ApiModelProperty("rx轴")
private String rx;
@Excel(name = "ry轴")
@ApiModelProperty("ry轴")
private String ry;
@Excel(name = "rz轴")
@ApiModelProperty("rz轴")
private String rz;
}

View File

@ -1,108 +0,0 @@
package com.cmvr.device.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.device.domain.DeDeviceProfile;
import com.cmvr.device.domain.vo.ParameterVo;
import com.cmvr.device.domain.vo.ProfileVo;
import com.cmvr.device.domain.vo.TeMechanicalArmVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.context.annotation.Primary;
/**
* 设备配置信息Mapper接口
*
* @author cmvr-iot
* @date 2025-05-09
*/
@Mapper
public interface DeDeviceProfileMapper extends BaseMapper<DeDeviceProfile>
{
/**
* 根据设备id查询当前设备的所有配置信息
*
* @param id 设备配置信息主键
* @return 设备配置信息
*/
public List<DeDeviceProfile> selectDeDeviceProfileById(String id);
/**
* 根据id查询参数信息
* @param id
* @return
*/
public DeDeviceProfile selectProfileById(String id);
// 查询所有设备配置列表信息
public List<ProfileVo> selectAllProfilesVo();
/**
* 根据设备id和备注查询相关参数信息
* @param id 设备id
* @param remark 备注
* @return 返回设备参数对象
*/
public List<DeDeviceProfile> selectProfileByIdAndRemark(@Param("id") String id,@Param("remark") String remark);
/**
* 根据设备id和备注查询相关参数信息
* @param id 设备id
* @param remark 备注
* @return 返回视图对象
*/
public List<ParameterVo> selectParameterByIdAndRemark(@Param("id") String id, @Param("remark") String remark);
/**
* 根据设备参数id去修改参数默认值
* @param id 设备id
* @return 结果
*/
public int updateProfileByIdAndByName(@Param("id") String id, @Param("name") String name, @Param("value") String value);
/**
* 查询设备配置信息列表
*
* @param deDeviceProfile 设备配置信息
* @return 设备配置信息集合
*/
public List<DeDeviceProfile> selectDeDeviceProfileList(DeDeviceProfile deDeviceProfile);
/**
* 新增设备配置信息
*
* @param deDeviceProfile 设备配置信息
* @return 结果
*/
public int insertDeDeviceProfile(DeDeviceProfile deDeviceProfile);
/**
* 修改设备配置信息
*
* @param deDeviceProfile 设备配置信息
* @return 结果
*/
public int updateDeDeviceProfile(DeDeviceProfile deDeviceProfile);
/**
* 删除设备配置信息
*
* @param id 设备配置信息主键
* @return 结果
*/
public int deleteDeDeviceProfileById(String id);
/**
* 批量删除设备配置信息
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteDeDeviceProfileByIds(String[] ids);
}

View File

@ -1,76 +0,0 @@
package com.cmvr.device.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.device.domain.DeDeviceRegistration;
import org.apache.ibatis.annotations.Mapper;
/**
* 设备注册Mapper接口
*
* @author cmvr-iot
* @date 2025-05-08
*/
@Mapper
public interface DeDeviceRegistrationMapper extends BaseMapper<DeDeviceRegistration>
{
/**
* 查询设备注册
*
* @param id 设备注册主键
* @return 设备注册
*/
public DeDeviceRegistration selectDeDeviceRegistrationById(String id);
/**
* 查询设备表中所有的设备编号
* @return 设备编号集合
*/
public List<String> selectAllDeviceCode();
public String selectDeviceModelById(String id);
/**
* 查询设备注册列表
*
* @param deDeviceRegistration 设备注册
* @return 设备注册集合
*/
public List<DeDeviceRegistration> selectDeDeviceRegistrationList(DeDeviceRegistration deDeviceRegistration);
/**
* 新增设备注册
*
* @param deDeviceRegistration 设备注册
* @return 结果
*/
public int insertDeDeviceRegistration(DeDeviceRegistration deDeviceRegistration);
/**
* 修改设备注册
*
* @param deDeviceRegistration 设备注册
* @return 结果
*/
public int updateDeDeviceRegistration(DeDeviceRegistration deDeviceRegistration);
/**
* 删除设备注册
*
* @param id 设备注册主键
* @return 结果
*/
public int deleteDeDeviceRegistrationById(String id);
/**
* 批量删除设备注册
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteDeDeviceRegistrationByIds(String[] ids);
}

View File

@ -1,84 +0,0 @@
package com.cmvr.device.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.device.domain.DeDeviceTerminalConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
/**
* 设备终端配置Mapper接口
*
* @author cmvr-iot
* @date 2025-05-08
*/
@Mapper
public interface DeDeviceTerminalConfigMapper extends BaseMapper<DeDeviceTerminalConfig>
{
/**
* 查询设备终端配置
*
* @param id 设备终端配置主键
* @return 设备终端配置
*/
public DeDeviceTerminalConfig selectDeDeviceTerminalConfigById(String id);
/** 根据QUIC节点ID查找已人工绑定的终端。 */
DeDeviceTerminalConfig selectByQuicNodeId(@Param("quicNodeId") String quicNodeId);
/**
* 更新QUIC运行态和终端gRPC地址,不覆盖终端名称、启停状态等人工配置。
*/
int updateQuicRuntimeState(@Param("id") String id,
@Param("host") String host,
@Param("port") Long port,
@Param("observedIp") String observedIp,
@Param("sessionId") String sessionId,
@Param("bootId") String bootId,
@Param("softwareVersion") String softwareVersion,
@Param("onlineStatus") String onlineStatus,
@Param("lastHeartbeatTime") Date lastHeartbeatTime);
/**
* 查询设备终端配置列表
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 设备终端配置集合
*/
public List<DeDeviceTerminalConfig> selectDeDeviceTerminalConfigList(DeDeviceTerminalConfig deDeviceTerminalConfig);
/**
* 新增设备终端配置
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 结果
*/
public int insertDeDeviceTerminalConfig(DeDeviceTerminalConfig deDeviceTerminalConfig);
/**
* 修改设备终端配置
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 结果
*/
public int updateDeDeviceTerminalConfig(DeDeviceTerminalConfig deDeviceTerminalConfig);
/**
* 删除设备终端配置
*
* @param id 设备终端配置主键
* @return 结果
*/
public int deleteDeDeviceTerminalConfigById(String id);
/**
* 批量删除设备终端配置
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteDeDeviceTerminalConfigByIds(String[] ids);
}

View File

@ -1,61 +0,0 @@
package com.cmvr.device.mapper;
import java.util.List;
import com.cmvr.device.domain.DeRobotConfig;
/**
* 机器人配置Mapper接口
*
* @author cmvr-iot
* @date 2025-09-09
*/
public interface DeRobotConfigMapper
{
/**
* 查询机器人配置
*
* @param id 机器人配置主键
* @return 机器人配置
*/
public DeRobotConfig selectDeRobotConfigById(String id);
/**
* 查询机器人配置列表
*
* @param deRobotConfig 机器人配置
* @return 机器人配置集合
*/
public List<DeRobotConfig> selectDeRobotConfigList(DeRobotConfig deRobotConfig);
/**
* 新增机器人配置
*
* @param deRobotConfig 机器人配置
* @return 结果
*/
public int insertDeRobotConfig(DeRobotConfig deRobotConfig);
/**
* 修改机器人配置
*
* @param deRobotConfig 机器人配置
* @return 结果
*/
public int updateDeRobotConfig(DeRobotConfig deRobotConfig);
/**
* 删除机器人配置
*
* @param id 机器人配置主键
* @return 结果
*/
public int deleteDeRobotConfigById(String id);
/**
* 批量删除机器人配置
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteDeRobotConfigByIds(String[] ids);
}

View File

@ -1,83 +0,0 @@
package com.cmvr.device.service;
import java.util.List;
import com.cmvr.device.domain.DeDeviceProfile;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.device.domain.vo.ProfileVo;
import com.cmvr.device.domain.vo.TeMechanicalArmVo;
import org.apache.ibatis.annotations.Param;
/**
* 设备配置信息Service接口
*
* @author cmvr-iot
* @date 2025-05-09
*/
public interface IDeDeviceProfileService
{
/**
* 根据设备id查询设备配置信息
*
* @param id 设备id
* @return 设备配置信息
*/
public List<DeDeviceProfile> selectDeDeviceProfileById(String id);
/**
* 根据设备id和备注查询相关参数信息
* @param id 设备id
* @param remark 备注
* @return
*/
public List<DeDeviceProfile> selectProfileByIdAndRemark(String id,String remark);
/**
* 根据设备id和参数名称,修改参数值
* @param mechanicalArmVo 配置参数信息
* @return 结果
*/
public int updateProfileByIdAndByName(TeMechanicalArmVo mechanicalArmVo);
/**
* 查询设备配置信息列表
*
* @param deDeviceProfile 设备配置信息
* @return 设备配置信息集合
*/
public List<ProfileVo> selectDeDeviceProfileList(DeDeviceProfile deDeviceProfile);
/**
* 新增设备配置信息
*
* @param deDeviceProfile 设备配置信息
* @return 结果
*/
public int insertDeDeviceProfile(List<DeDeviceProfile> deDeviceProfile);
/**
* 修改设备配置信息
*
* @param deDeviceProfiles 设备配置信息
* @return 结果
*/
public int updateDeDeviceProfile(List<DeDeviceProfile> deDeviceProfiles);
/**
* 批量删除设备配置信息
*
* @param ids 需要删除的设备配置信息主键集合
* @return 结果
*/
public int deleteDeDeviceProfileByIds(String[] ids);
/**
* 删除设备配置信息信息
*
* @param id 设备配置信息主键
* @return 结果
*/
public int deleteDeDeviceProfileById(String id);
}

View File

@ -1,69 +0,0 @@
package com.cmvr.device.service;
import java.util.List;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.device.domain.vo.Device;
/**
* 设备注册Service接口
*
* @author cmvr-iot
* @date 2025-05-08
*/
public interface IDeDeviceRegistrationService
{
/**
* 查询设备注册
*
* @param id 设备注册主键
* @return 设备注册
*/
public DeDeviceRegistration selectDeDeviceRegistrationById(String id);
/**
* 新增获取到的
* @param devices 设备列表
* @return
*/
public int addDeviceList(List<DeDeviceRegistration> devices);
/**
* 查询设备注册列表
*
* @param deDeviceRegistration 设备注册
* @return 设备注册集合
*/
public List<DeDeviceRegistration> selectDeDeviceRegistrationList(DeDeviceRegistration deDeviceRegistration);
/**
* 新增设备注册
*
* @param deDeviceRegistration 设备注册
* @return 结果
*/
public int insertDeDeviceRegistration(DeDeviceRegistration deDeviceRegistration);
/**
* 修改设备注册
*
* @param deDeviceRegistration 设备注册
* @return 结果
*/
public int updateDeDeviceRegistration(DeDeviceRegistration deDeviceRegistration);
/**
* 批量删除设备注册
*
* @param ids 需要删除的设备注册主键集合
* @return 结果
*/
public int deleteDeDeviceRegistrationByIds(String[] ids);
/**
* 删除设备注册信息
*
* @param id 设备注册主键
* @return 结果
*/
public int deleteDeDeviceRegistrationById(String id);
}

View File

@ -1,70 +0,0 @@
package com.cmvr.device.service;
import java.util.List;
import java.util.Date;
import com.cmvr.device.domain.DeDeviceTerminalConfig;
/**
* 设备终端配置Service接口
*
* @author cmvr-iot
* @date 2025-05-08
*/
public interface IDeDeviceTerminalConfigService
{
/**
* 查询设备终端配置
*
* @param id 设备终端配置主键
* @return 设备终端配置
*/
public DeDeviceTerminalConfig selectDeDeviceTerminalConfigById(String id);
/** 根据QUIC节点ID查询已绑定终端。 */
DeDeviceTerminalConfig selectByQuicNodeId(String quicNodeId);
/** 更新QUIC运行态以及由终端主动上报的gRPC地址。 */
int updateQuicRuntimeState(String id, String host, Long port, String observedIp,
String sessionId, String bootId, String softwareVersion,
String onlineStatus, Date lastHeartbeatTime);
/**
* 查询设备终端配置列表
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 设备终端配置集合
*/
public List<DeDeviceTerminalConfig> selectDeDeviceTerminalConfigList(DeDeviceTerminalConfig deDeviceTerminalConfig);
/**
* 新增设备终端配置
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 结果
*/
public int insertDeDeviceTerminalConfig(DeDeviceTerminalConfig deDeviceTerminalConfig);
/**
* 修改设备终端配置
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 结果
*/
public int updateDeDeviceTerminalConfig(DeDeviceTerminalConfig deDeviceTerminalConfig);
/**
* 批量删除设备终端配置
*
* @param ids 需要删除的设备终端配置主键集合
* @return 结果
*/
public int deleteDeDeviceTerminalConfigByIds(String[] ids);
/**
* 删除设备终端配置信息
*
* @param id 设备终端配置主键
* @return 结果
*/
public int deleteDeDeviceTerminalConfigById(String id);
}

View File

@ -1,61 +0,0 @@
package com.cmvr.device.service;
import java.util.List;
import com.cmvr.device.domain.DeRobotConfig;
/**
* 机器人配置Service接口
*
* @author cmvr-iot
* @date 2025-09-09
*/
public interface IDeRobotConfigService
{
/**
* 查询机器人配置
*
* @param id 机器人配置主键
* @return 机器人配置
*/
public DeRobotConfig selectDeRobotConfigById(String id);
/**
* 查询机器人配置列表
*
* @param deRobotConfig 机器人配置
* @return 机器人配置集合
*/
public List<DeRobotConfig> selectDeRobotConfigList(DeRobotConfig deRobotConfig);
/**
* 新增机器人配置
*
* @param deRobotConfig 机器人配置
* @return 结果
*/
public int insertDeRobotConfig(DeRobotConfig deRobotConfig);
/**
* 修改机器人配置
*
* @param deRobotConfig 机器人配置
* @return 结果
*/
public int updateDeRobotConfig(DeRobotConfig deRobotConfig);
/**
* 批量删除机器人配置
*
* @param ids 需要删除的机器人配置主键集合
* @return 结果
*/
public int deleteDeRobotConfigByIds(String[] ids);
/**
* 删除机器人配置信息
*
* @param id 机器人配置主键
* @return 结果
*/
public int deleteDeRobotConfigById(String id);
}

View File

@ -1,176 +0,0 @@
package com.cmvr.device.service.impl;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.device.domain.vo.ParameterVo;
import com.cmvr.device.domain.vo.ProfileVo;
import com.cmvr.device.domain.vo.TeMechanicalArmVo;
import com.cmvr.device.mapper.DeDeviceRegistrationMapper;
import org.apache.commons.collections4.list.PredicatedList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cmvr.device.mapper.DeDeviceProfileMapper;
import com.cmvr.device.domain.DeDeviceProfile;
import com.cmvr.device.service.IDeDeviceProfileService;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* 设备配置信息Service业务层处理
*
* @author cmvr-iot
* @date 2025-05-09
*/
@Service
public class DeDeviceProfileServiceImpl implements IDeDeviceProfileService
{
@Autowired
private DeDeviceProfileMapper deDeviceProfileMapper;
@Autowired
private DeDeviceRegistrationMapper deviceRegistrationMapper;
/**
* 根据设备id查询当前设备设备配置信息
*
* @param id 设备id
* @return 设备配置信息
*/
@Override
public List<DeDeviceProfile> selectDeDeviceProfileById(String id)
{
//根据设备id查询设备配置信息
DeDeviceProfile deviceProfile=deDeviceProfileMapper.selectProfileById(id);
//根据设备id和备注查询设备配置信息
List<DeDeviceProfile> deviceProfiles=deDeviceProfileMapper
.selectProfileByIdAndRemark(
deviceProfile.getIdDeDeviceRegistration(),
deviceProfile.getRemark()
);
//打印设备配置信息
System.out.println(deviceProfiles);
//返回设备配置信息
return deviceProfiles;
}
@Override
public List<DeDeviceProfile> selectProfileByIdAndRemark(String id, String remark) {
return deDeviceProfileMapper.selectProfileByIdAndRemark(id,remark);
}
/**
* 根据设备参数id去修改参数默认值
* @param mechanicalArmVo 配置参数信息
* @return 结果
*/
@Override
public int updateProfileByIdAndByName(TeMechanicalArmVo mechanicalArmVo) {
if (mechanicalArmVo == null || StringUtils.isEmpty(mechanicalArmVo.getIdDeDeviceRegistration())) {
return 0;
}
int totalUpdated = 0;
String deviceId = mechanicalArmVo.getIdDeDeviceRegistration();
// 使用 Map 收集非空属性
Map<String, Object> fieldMap = new HashMap<>();
if (mechanicalArmVo.getWorkModule() != null) fieldMap.put("workModule", mechanicalArmVo.getWorkModule());
if (mechanicalArmVo.getPosStep() != null) fieldMap.put("posStep", mechanicalArmVo.getPosStep());
if (mechanicalArmVo.getJointStep() != null) fieldMap.put("jointStep", mechanicalArmVo.getJointStep());
if (mechanicalArmVo.getJoint1() != null) fieldMap.put("joint1", mechanicalArmVo.getJoint1());
if (mechanicalArmVo.getJoint2() != null) fieldMap.put("joint2", mechanicalArmVo.getJoint2());
if (mechanicalArmVo.getJoint3() != null) fieldMap.put("joint3", mechanicalArmVo.getJoint3());
if (mechanicalArmVo.getJoint4() != null) fieldMap.put("joint4", mechanicalArmVo.getJoint4());
if (mechanicalArmVo.getJoint5() != null) fieldMap.put("joint5", mechanicalArmVo.getJoint5());
if (mechanicalArmVo.getJoint6() != null) fieldMap.put("joint6", mechanicalArmVo.getJoint6());
if (mechanicalArmVo.getX() != null) fieldMap.put("x", mechanicalArmVo.getX());
if (mechanicalArmVo.getY() != null) fieldMap.put("y", mechanicalArmVo.getY());
if (mechanicalArmVo.getZ() != null) fieldMap.put("z", mechanicalArmVo.getZ());
if (mechanicalArmVo.getRx() != null) fieldMap.put("rx", mechanicalArmVo.getRx());
if (mechanicalArmVo.getRy() != null) fieldMap.put("ry", mechanicalArmVo.getRy());
if (mechanicalArmVo.getRz() != null) fieldMap.put("rz", mechanicalArmVo.getRz());
// 遍历 Map 执行更新
for (Map.Entry<String, Object> entry : fieldMap.entrySet()) {
totalUpdated += deDeviceProfileMapper.updateProfileByIdAndByName(
deviceId, entry.getKey(), (String)entry.getValue());
}
return totalUpdated;
}
/**
* 查询设备配置信息列表
*
* @param deDeviceProfile 设备配置信息
* @return 设备配置信息
*/
@Override
public List<ProfileVo> selectDeDeviceProfileList(DeDeviceProfile deDeviceProfile)
{
List<ProfileVo> profileVoList=deDeviceProfileMapper.selectAllProfilesVo();
return profileVoList;
}
/**
* 新增设备配置信息
*
* @param deDeviceProfile 设备配置信息
* @return 结果
*/
@Override
public int insertDeDeviceProfile(List<DeDeviceProfile> deDeviceProfile)
{
int i=0;
for (DeDeviceProfile profile : deDeviceProfile) {
i=deDeviceProfileMapper.insert(profile);
}
return i;
}
/**
* 修改设备配置信息
*
* @param deDeviceProfiles 设备配置信息
* @return 结果
*/
@Override
public int updateDeDeviceProfile(List<DeDeviceProfile> deDeviceProfiles)
{
int i=0;
for (DeDeviceProfile profile : deDeviceProfiles) {
i=deDeviceProfileMapper.updateDeDeviceProfile(profile);
}
return i;
}
/**
* 批量删除设备配置信息
*
* @param ids 需要删除的设备配置信息主键
* @return 结果
*/
@Override
public int deleteDeDeviceProfileByIds(String[] ids)
{
return deDeviceProfileMapper.deleteDeDeviceProfileByIds(ids);
}
/**
* 删除设备配置信息信息
*
* @param id 设备配置信息主键
* @return 结果
*/
@Override
public int deleteDeDeviceProfileById(String id)
{
return deDeviceProfileMapper.deleteDeDeviceProfileById(id);
}
}

View File

@ -1,125 +0,0 @@
package com.cmvr.device.service.impl;
import java.util.List;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.device.domain.DeDeviceProfile;
import com.cmvr.device.domain.vo.Device;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cmvr.device.mapper.DeDeviceRegistrationMapper;
import com.cmvr.device.domain.DeDeviceRegistration;
import com.cmvr.device.service.IDeDeviceRegistrationService;
/**
* 设备注册Service业务层处理
*
* @author cmvr-iot
* @date 2025-05-08
*/
@Service
public class DeDeviceRegistrationServiceImpl implements IDeDeviceRegistrationService
{
@Autowired
private DeDeviceRegistrationMapper deDeviceRegistrationMapper;
/**
* 查询设备注册
*
* @param id 设备注册主键
* @return 设备注册
*/
@Override
public DeDeviceRegistration selectDeDeviceRegistrationById(String id)
{
return deDeviceRegistrationMapper.selectDeDeviceRegistrationById(id);
}
@Override
public int addDeviceList(List<DeDeviceRegistration> devices) {
// 2. 获取数据库中已有设备
List<String> deviceCodes= deDeviceRegistrationMapper.selectAllDeviceCode();
int addedCount = 0;
for (DeDeviceRegistration edgeDevice : devices) {
if (!deviceCodes.contains(edgeDevice.getDeviceCode())) {
deDeviceRegistrationMapper.insert(edgeDevice);
addedCount++;
}
}
return addedCount;
}
/**
* 查询设备注册列表
*
* @param deDeviceRegistration 设备注册
* @return 设备注册
*/
@Override
public List<DeDeviceRegistration> selectDeDeviceRegistrationList(DeDeviceRegistration deDeviceRegistration)
{
return deDeviceRegistrationMapper.selectDeDeviceRegistrationList(deDeviceRegistration);
}
/**
* 新增设备注册
*
* @param deDeviceRegistration 设备注册
* @return 结果
*/
@Override
public int insertDeDeviceRegistration(DeDeviceRegistration deDeviceRegistration)
{
deDeviceRegistration.setCreateTime(DateUtils.getNowDate());
deDeviceRegistration.setStatus("1");
return deDeviceRegistrationMapper.insert(deDeviceRegistration);
}
/**
* 修改设备注册
*
* @param deDeviceRegistration 设备注册
* @return 结果
*/
@Override
public int updateDeDeviceRegistration(DeDeviceRegistration deDeviceRegistration)
{
deDeviceRegistration.setUpdateTime(DateUtils.getNowDate());
return deDeviceRegistrationMapper.updateDeDeviceRegistration(deDeviceRegistration);
}
/**
* 批量删除设备注册
*
* @param ids 需要删除的设备注册主键
* @return 结果
*/
@Override
public int deleteDeDeviceRegistrationByIds(String[] ids)
{
return deDeviceRegistrationMapper.deleteDeDeviceRegistrationByIds(ids);
}
/**
* 删除设备注册信息
*
* @param id 设备注册主键
* @return 结果
*/
@Override
public int deleteDeDeviceRegistrationById(String id)
{
return deDeviceRegistrationMapper.deleteDeDeviceRegistrationById(id);
}
}

View File

@ -1,114 +0,0 @@
package com.cmvr.device.service.impl;
import java.util.List;
import java.util.Date;
import com.cmvr.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import com.cmvr.device.mapper.DeDeviceTerminalConfigMapper;
import com.cmvr.device.domain.DeDeviceTerminalConfig;
import com.cmvr.device.service.IDeDeviceTerminalConfigService;
/**
* 设备终端配置Service业务层处理
*
* @author cmvr-iot
* @date 2025-05-08
*/
@Service
public class DeDeviceTerminalConfigServiceImpl implements IDeDeviceTerminalConfigService
{
@Autowired
private DeDeviceTerminalConfigMapper deDeviceTerminalConfigMapper;
/**
* 查询设备终端配置
*
* @param id 设备终端配置主键
* @return 设备终端配置
*/
@Override
public DeDeviceTerminalConfig selectDeDeviceTerminalConfigById(String id)
{
return deDeviceTerminalConfigMapper.selectDeDeviceTerminalConfigById(id);
}
@Override
public DeDeviceTerminalConfig selectByQuicNodeId(String quicNodeId)
{
return deDeviceTerminalConfigMapper.selectByQuicNodeId(quicNodeId);
}
@Override
public int updateQuicRuntimeState(String id, String host, Long port, String observedIp,
String sessionId, String bootId, String softwareVersion,
String onlineStatus, Date lastHeartbeatTime)
{
return deDeviceTerminalConfigMapper.updateQuicRuntimeState(id, host, port, observedIp,
sessionId, bootId, softwareVersion, onlineStatus, lastHeartbeatTime);
}
/**
* 查询设备终端配置列表
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 设备终端配置
*/
@Override
public List<DeDeviceTerminalConfig> selectDeDeviceTerminalConfigList(DeDeviceTerminalConfig deDeviceTerminalConfig)
{
return deDeviceTerminalConfigMapper.selectDeDeviceTerminalConfigList(deDeviceTerminalConfig);
}
/**
* 新增设备终端配置
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 结果
*/
@Override
public int insertDeDeviceTerminalConfig(DeDeviceTerminalConfig deDeviceTerminalConfig)
{
deDeviceTerminalConfig.setCreateTime(DateUtils.getNowDate());
deDeviceTerminalConfig.setStatus("1");
return this.deDeviceTerminalConfigMapper.insert(deDeviceTerminalConfig);
}
/**
* 修改设备终端配置
*
* @param deDeviceTerminalConfig 设备终端配置
* @return 结果
*/
@Override
public int updateDeDeviceTerminalConfig(DeDeviceTerminalConfig deDeviceTerminalConfig)
{
deDeviceTerminalConfig.setUpdateTime(DateUtils.getNowDate());
return deDeviceTerminalConfigMapper.updateDeDeviceTerminalConfig(deDeviceTerminalConfig);
}
/**
* 批量删除设备终端配置
*
* @param ids 需要删除的设备终端配置主键
* @return 结果
*/
@Override
public int deleteDeDeviceTerminalConfigByIds(String[] ids)
{
return deDeviceTerminalConfigMapper.deleteDeDeviceTerminalConfigByIds(ids);
}
/**
* 删除设备终端配置信息
*
* @param id 设备终端配置主键
* @return 结果
*/
@Override
public int deleteDeDeviceTerminalConfigById(String id)
{
return deDeviceTerminalConfigMapper.deleteDeDeviceTerminalConfigById(id);
}
}

View File

@ -1,97 +0,0 @@
package com.cmvr.device.service.impl;
import java.util.List;
import com.cmvr.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cmvr.device.mapper.DeRobotConfigMapper;
import com.cmvr.device.domain.DeRobotConfig;
import com.cmvr.device.service.IDeRobotConfigService;
/**
* 机器人配置Service业务层处理
*
* @author cmvr-iot
* @date 2025-09-09
*/
@Service
public class DeRobotConfigServiceImpl implements IDeRobotConfigService
{
@Autowired
private DeRobotConfigMapper deRobotConfigMapper;
/**
* 查询机器人配置
*
* @param id 机器人配置主键
* @return 机器人配置
*/
@Override
public DeRobotConfig selectDeRobotConfigById(String id)
{
return deRobotConfigMapper.selectDeRobotConfigById(id);
}
/**
* 查询机器人配置列表
*
* @param deRobotConfig 机器人配置
* @return 机器人配置
*/
@Override
public List<DeRobotConfig> selectDeRobotConfigList(DeRobotConfig deRobotConfig)
{
return deRobotConfigMapper.selectDeRobotConfigList(deRobotConfig);
}
/**
* 新增机器人配置
*
* @param deRobotConfig 机器人配置
* @return 结果
*/
@Override
public int insertDeRobotConfig(DeRobotConfig deRobotConfig)
{
deRobotConfig.setCreateTime(DateUtils.getNowDate());
return deRobotConfigMapper.insertDeRobotConfig(deRobotConfig);
}
/**
* 修改机器人配置
*
* @param deRobotConfig 机器人配置
* @return 结果
*/
@Override
public int updateDeRobotConfig(DeRobotConfig deRobotConfig)
{
deRobotConfig.setUpdateTime(DateUtils.getNowDate());
return deRobotConfigMapper.updateDeRobotConfig(deRobotConfig);
}
/**
* 批量删除机器人配置
*
* @param ids 需要删除的机器人配置主键
* @return 结果
*/
@Override
public int deleteDeRobotConfigByIds(String[] ids)
{
return deRobotConfigMapper.deleteDeRobotConfigByIds(ids);
}
/**
* 删除机器人配置信息
*
* @param id 机器人配置主键
* @return 结果
*/
@Override
public int deleteDeRobotConfigById(String id)
{
return deRobotConfigMapper.deleteDeRobotConfigById(id);
}
}

View File

@ -1,38 +0,0 @@
package com.cmvr.device.util;
import com.cmvr.device.domain.vo.TeMechanicalArmVo;
import java.lang.reflect.Field;
public class VoUtil {
/**
* 获取TeMechanicalArmVo对象中除设备ID外的非空属性名
* @param vo TeMechanicalArmVo对象
* @return 非空属性名,如果没有则返回null
*/
public static String getNonNullPropertyName(TeMechanicalArmVo vo) {
if (vo == null) return null;
try {
for (Field field : TeMechanicalArmVo.class.getDeclaredFields()) {
if ("idDeDeviceRegistration".equals(field.getName())) continue;
field.setAccessible(true);
Object value = field.get(vo);
// 排除null和空字符串
if (value instanceof String) {
if (!((String) value).trim().isEmpty()) {
return field.getName();
}
} else if (value != null) {
return field.getName();
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException("获取属性值失败", e);
}
return null;
}
}

View File

@ -1,179 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.device.mapper.DeDeviceProfileMapper">
<resultMap type="DeDeviceProfile" id="DeDeviceProfileResult">
<result property="id" column="id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="idDeDeviceRegistration" column="id_de_device_registration" />
<result property="id_de_device_terminal_config" column="id_de_device_terminal_config" />
<result property="paramName" column="param_name" />
<result property="defaultValue" column="default_value" />
<result property="paramType" column="param_type" />
</resultMap>
<sql id="selectDeDeviceProfileVo">
select id, create_by, create_time, update_by, update_time, remark, device_model , id_de_device_terminal_config ,id_de_device_registration, param_name, default_value , param_type from de_device_profile
</sql>
<select id="selectDeDeviceProfileList" parameterType="DeDeviceProfile" resultMap="DeDeviceProfileResult">
<include refid="selectDeDeviceProfileVo"/>
<where>
<if test="remark != null and remark != ''"> and remark = #{remark}</if>
<if test="idDeDeviceRegistration != null and idDeDeviceRegistration != ''"> and id_de_device_registration = #{idDeDeviceRegistration}</if>
<if test="paramName != null and paramName != ''"> and param_name like concat('%', #{paramName}, '%')</if>
<if test="paramType != null and paramType != ''"> and param_type = #{paramType}</if>
</where>
</select>
<select id="selectDeDeviceProfileById" parameterType="String" resultMap="DeDeviceProfileResult">
<include refid="selectDeDeviceProfileVo"/>
where id_de_device_registration = #{id}
</select>
<select id="selectProfileById" resultType="com.cmvr.device.domain.DeDeviceProfile">
<include refid="selectDeDeviceProfileVo"/> where id = #{id}
</select>
<select id="selectProfileByIdAndRemark" resultType="com.cmvr.device.domain.DeDeviceProfile">
<include refid="selectDeDeviceProfileVo"></include>where id_de_device_registration=#{id} and remark=#{remark}
</select>
<select id="selectParameterByIdAndRemark" resultType="com.cmvr.device.domain.vo.ParameterVo">
select id, param_name, default_value , param_type
from de_device_profile where id_de_device_registration=#{id} and remark=#{remark}
</select>
<!-- 参数信息基础映射 -->
<resultMap id="parameters" type="com.cmvr.device.domain.vo.ParameterVo">
<result property="id" column="param_id"></result>
<result property="paramName" column="param_name"></result>
<result property="defaultValue" column="default_value"></result>
<result property="paramType" column="param_type"></result>
</resultMap>
<!-- 设备信息基础映射 -->
<resultMap id="BaseResultMap" type="com.cmvr.device.domain.vo.ProfileVo">
<result column="id_de_device_registration" property="idDeDeviceRegistration" jdbcType="VARCHAR"/>
<result column="device_model" property="deviceModel" jdbcType="VARCHAR"/>
<result column="remark" property="remark" jdbcType="VARCHAR"/>
<result column="id_de_device_terminal_config" property="id_de_device_terminal_config" jdbcType="VARCHAR"/>
<result column="device_name" property="deviceName" jdbcType="VARCHAR"/>
<result column="terminal_name" property="terminalName" jdbcType="VARCHAR"/>
<collection property="parameters" ofType="com.cmvr.device.domain.vo.ParameterVo"
column="{id=id_de_device_registration, remark=remark}"
select="selectParametersByDeviceId"/>
</resultMap>
<!-- 查询所有设备配置信息 -->
<select id="selectAllProfilesVo" resultMap="BaseResultMap">
SELECT
t1.remark,
t1.device_model,
t1.id_de_device_registration,
t1.id_de_device_terminal_config,
t2.device_name,
t3.`name` as terminal_name
FROM
de_device_profile t1
JOIN de_device_registration t2 ON t1.id_de_device_registration = t2.id
JOIN de_device_terminal_config t3 ON t2.id_de_device_terminal_config = t3.id
GROUP BY
t1.remark,
t1.device_model,
t1.id_de_device_registration,
t1.id_de_device_terminal_config,
t2.device_name,
t3.`name`
</select>
<!-- 根据设备ID和备注查询参数列表 -->
<select id="selectParametersByDeviceId" resultMap="parameters">
SELECT
t1.id,
t1.param_name,
t1.param_type,
t1.default_value
FROM
de_device_profile t1
WHERE
t1.id_de_device_registration = #{id}
AND t1.remark = #{remark}
</select>
<insert id="insertDeDeviceProfile" parameterType="DeDeviceProfile">
insert into de_device_profile
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="deviceModel != null">device_model,</if>
<if test="idDeDeviceRegistration != null">id_de_device_registration,</if>
<if test="paramName != null">param_name,</if>
<if test="defaultValue != null">default_value,</if>
<if test="paramType != null">param_type,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="deviceModel != null">#{deviceModel},</if>
<if test="idDeDeviceRegistration != null">#{idDeDeviceRegistration},</if>
<if test="paramName != null">#{paramName},</if>
<if test="defaultValue != null">#{defaultValue},</if>
<if test="paramType != null">#{paramType},</if>
</trim>
</insert>
<update id="updateDeDeviceProfile" parameterType="com.cmvr.device.domain.DeDeviceProfile">
update de_device_profile
<trim prefix="SET" suffixOverrides=",">
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="idDeDeviceRegistration != null">id_de_device_registration = #{idDeDeviceRegistration},</if>
<if test="paramName != null">param_name = #{paramName},</if>
<if test="defaultValue != null">default_value = #{defaultValue},</if>
<if test="paramType != null">param_type = #{paramType},</if>
</trim>
where id = #{id}
</update>
<update id="updateProfileByIdAndByName" parameterType="com.cmvr.device.domain.vo.TeMechanicalArmVo">
UPDATE de_device_profile set default_value=#{value}
WHERE id_de_device_registration = #{id} AND param_name = #{name}
</update>
<delete id="deleteDeDeviceProfileById" parameterType="String">
delete from de_device_profile where id = #{id}
</delete>
<delete id="deleteDeDeviceProfileByIds" parameterType="String">
delete from de_device_profile where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.device.mapper.DeDeviceRegistrationMapper">
<resultMap type="DeDeviceRegistration" id="DeDeviceRegistrationResult">
<result property="id" column="id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remake" column="remake" />
<result property="idDeDeviceTerminalConfig" column="id_de_device_terminal_config" />
<result property="deviceCode" column="device_code" />
<result property="deviceName" column="device_name" />
<result property="deviceModel" column="device_model" />
<result property="status" column="status" />
</resultMap>
<sql id="selectDeDeviceRegistrationVo">
select id, create_by, create_time, update_by, update_time, remake, id_de_device_terminal_config, device_code, device_name, device_model, status from de_device_registration
</sql>
<select id="selectDeDeviceRegistrationList" parameterType="DeDeviceRegistration" resultMap="DeDeviceRegistrationResult">
<include refid="selectDeDeviceRegistrationVo"/>
<where>
<if test="deviceCode != null and deviceCode != ''"> and device_code = #{deviceCode}</if>
<if test="deviceName != null and deviceName != ''"> and device_name like concat('%', #{deviceName}, '%')</if>
<if test="deviceModel != null and deviceModel != ''"> and device_model = #{deviceModel}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
</where>
</select>
<select id="selectDeDeviceRegistrationById" parameterType="String" resultMap="DeDeviceRegistrationResult">
<include refid="selectDeDeviceRegistrationVo"/>
where id = #{id}
</select>
<select id="selectDeviceModelById" resultType="java.lang.String">
select device_model from de_device_registration where id = #{id}
</select>
<select id="selectAllDeviceCode" resultType="java.lang.String">
select device_code from de_device_registration
</select>
<insert id="insertDeDeviceRegistration" parameterType="DeDeviceRegistration">
insert into de_device_registration
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remake != null">remake,</if>
<if test="idDeDeviceTerminalConfig != null">id_de_device_terminal_config,</if>
<if test="deviceCode != null">device_code,</if>
<if test="deviceName != null">device_name,</if>
<if test="deviceModel != null">device_model,</if>
<if test="status != null">status,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remake != null">#{remake},</if>
<if test="idDeDeviceTerminalConfig != null">#{idDeDeviceTerminalConfig},</if>
<if test="deviceCode != null">#{deviceCode},</if>
<if test="deviceName != null">#{deviceName},</if>
<if test="deviceModel != null">#{deviceModel},</if>
<if test="status != null">#{status},</if>
</trim>
</insert>
<update id="updateDeDeviceRegistration" parameterType="DeDeviceRegistration">
update de_device_registration
<trim prefix="SET" suffixOverrides=",">
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remake != null">remake = #{remake},</if>
<if test="idDeDeviceTerminalConfig != null">id_de_device_terminal_config = #{idDeDeviceTerminalConfig},</if>
<if test="deviceCode != null">device_code = #{deviceCode},</if>
<if test="deviceName != null">device_name = #{deviceName},</if>
<if test="deviceModel != null">device_model = #{deviceModel},</if>
<if test="status != null">status = #{status},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteDeDeviceRegistrationById" parameterType="String">
delete from de_device_registration where id = #{id}
</delete>
<delete id="deleteDeDeviceRegistrationByIds" parameterType="String">
delete from de_device_registration where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -1,139 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.device.mapper.DeDeviceTerminalConfigMapper">
<resultMap type="DeDeviceTerminalConfig" id="DeDeviceTerminalConfigResult">
<result property="id" column="id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remake" column="remake" />
<result property="name" column="name" />
<result property="host" column="host" />
<result property="port" column="port" />
<result property="status" column="status" />
<result property="description" column="description" />
<result property="quicNodeId" column="quic_node_id" />
<result property="quicObservedIp" column="quic_observed_ip" />
<result property="quicSessionId" column="quic_session_id" />
<result property="quicBootId" column="quic_boot_id" />
<result property="quicSoftwareVersion" column="quic_software_version" />
<result property="quicOnlineStatus" column="quic_online_status" />
<result property="quicLastHeartbeatTime" column="quic_last_heartbeat_time" />
</resultMap>
<sql id="selectDeDeviceTerminalConfigVo">
select id, create_by, create_time, update_by, update_time, remake, name, host, port, status, description,
quic_node_id, quic_observed_ip, quic_session_id, quic_boot_id, quic_software_version,
quic_online_status, quic_last_heartbeat_time
from de_device_terminal_config
</sql>
<select id="selectDeDeviceTerminalConfigList" parameterType="DeDeviceTerminalConfig" resultMap="DeDeviceTerminalConfigResult">
<include refid="selectDeDeviceTerminalConfigVo"/>
<where>
<if test="remake != null and remake != ''"> and remake = #{remake}</if>
<if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if>
<if test="host != null and host != ''"> and host = #{host}</if>
<if test="port != null "> and port = #{port}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
<if test="description != null and description != ''"> and description = #{description}</if>
<if test="quicNodeId != null and quicNodeId != ''"> and quic_node_id = #{quicNodeId}</if>
<if test="quicOnlineStatus != null and quicOnlineStatus != ''"> and quic_online_status = #{quicOnlineStatus}</if>
</where>
</select>
<select id="selectByQuicNodeId" resultMap="DeDeviceTerminalConfigResult">
<include refid="selectDeDeviceTerminalConfigVo"/>
where quic_node_id = #{quicNodeId}
limit 1
</select>
<select id="selectDeDeviceTerminalConfigById" parameterType="String" resultMap="DeDeviceTerminalConfigResult">
<include refid="selectDeDeviceTerminalConfigVo"/>
where id = #{id}
</select>
<insert id="insertDeDeviceTerminalConfig" parameterType="DeDeviceTerminalConfig">
insert into de_device_terminal_config
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remake != null">remake,</if>
<if test="name != null">name,</if>
<if test="host != null">host,</if>
<if test="port != null">port,</if>
<if test="status != null">status,</if>
<if test="description != null">description,</if>
<if test="quicNodeId != null">quic_node_id,</if>
<if test="quicOnlineStatus != null">quic_online_status,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remake != null">#{remake},</if>
<if test="name != null">#{name},</if>
<if test="host != null">#{host},</if>
<if test="port != null">#{port},</if>
<if test="status != null">#{status},</if>
<if test="description != null">#{description},</if>
<if test="quicNodeId != null">#{quicNodeId},</if>
<if test="quicOnlineStatus != null">#{quicOnlineStatus},</if>
</trim>
</insert>
<update id="updateDeDeviceTerminalConfig" parameterType="DeDeviceTerminalConfig">
update de_device_terminal_config
<trim prefix="SET" suffixOverrides=",">
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remake != null">remake = #{remake},</if>
<if test="name != null">name = #{name},</if>
<if test="host != null">host = #{host},</if>
<if test="port != null">port = #{port},</if>
<if test="status != null">status = #{status},</if>
<if test="description != null">description = #{description},</if>
<if test="quicNodeId != null">quic_node_id = #{quicNodeId},</if>
<if test="quicOnlineStatus != null">quic_online_status = #{quicOnlineStatus},</if>
</trim>
where id = #{id}
</update>
<update id="updateQuicRuntimeState">
update de_device_terminal_config
<set>
<if test="host != null and host != ''">host = #{host},</if>
<if test="port != null">port = #{port},</if>
quic_observed_ip = #{observedIp},
quic_session_id = #{sessionId},
quic_boot_id = #{bootId},
quic_software_version = #{softwareVersion},
quic_online_status = #{onlineStatus},
quic_last_heartbeat_time = #{lastHeartbeatTime},
update_time = now()
</set>
where id = #{id}
</update>
<delete id="deleteDeDeviceTerminalConfigById" parameterType="String">
delete from de_device_terminal_config where id = #{id}
</delete>
<delete id="deleteDeDeviceTerminalConfigByIds" parameterType="String">
delete from de_device_terminal_config where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.device.mapper.DeRobotConfigMapper">
<resultMap type="DeRobotConfig" id="DeRobotConfigResult">
<result property="id" column="id" />
<result property="configId" column="config_id" />
<result property="configType" column="config_type" />
<result property="pid" column="pid" />
<result property="nodeName" column="node_name" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
<sql id="selectDeRobotConfigVo">
select id, config_id, config_type, pid, node_name, create_by, create_time, update_by, update_time, remark from de_robot_config
</sql>
<select id="selectDeRobotConfigList" parameterType="DeRobotConfig" resultMap="DeRobotConfigResult">
<include refid="selectDeRobotConfigVo"/>
<where>
<if test="configId != null and configId != ''"> and config_id = #{configId}</if>
<if test="configType != null and configType != ''"> and config_type = #{configType}</if>
<if test="pid != null and pid != ''"> and pid = #{pid}</if>
<if test="nodeName != null and nodeName != ''"> and node_name like concat('%', #{nodeName}, '%')</if>
</where>
</select>
<select id="selectDeRobotConfigById" parameterType="String" resultMap="DeRobotConfigResult">
<include refid="selectDeRobotConfigVo"/>
where id = #{id}
</select>
<insert id="insertDeRobotConfig" parameterType="DeRobotConfig">
insert into de_robot_config
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="configId != null">config_id,</if>
<if test="configType != null">config_type,</if>
<if test="pid != null">pid,</if>
<if test="nodeName != null">node_name,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="configId != null">#{configId},</if>
<if test="configType != null">#{configType},</if>
<if test="pid != null">#{pid},</if>
<if test="nodeName != null">#{nodeName},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
</trim>
</insert>
<update id="updateDeRobotConfig" parameterType="DeRobotConfig">
update de_robot_config
<trim prefix="SET" suffixOverrides=",">
<if test="configId != null">config_id = #{configId},</if>
<if test="configType != null">config_type = #{configType},</if>
<if test="pid != null">pid = #{pid},</if>
<if test="nodeName != null">node_name = #{nodeName},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteDeRobotConfigById" parameterType="String">
delete from de_robot_config where id = #{id}
</delete>
<delete id="deleteDeRobotConfigByIds" parameterType="String">
delete from de_robot_config where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -35,5 +35,10 @@
<artifactId>cmvr-iot-device</artifactId> <artifactId>cmvr-iot-device</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-resource</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,64 +0,0 @@
package com.cmvr.inspection.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
/**
* 巡检地图对象 inspection_map
*
* @author cmvr-iot
* @since 2026-05-29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ApiModel("巡检地图")
public class InspectionMap extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty("id")
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
/** 地图名称 */
@Excel(name = "地图名称")
@ApiModelProperty("地图名称")
private String mapName;
/** 地图来源的机器人id */
@Excel(name = "地图来源的机器人id")
@ApiModelProperty("地图来源的机器人id")
private String mapSourceRobotId;
/** 地图来源的地图名称 */
@Excel(name = "地图来源的地图名称")
@ApiModelProperty("地图来源的地图名称")
private String mapSourceName;
/** 地图文件路径 */
@Excel(name = "地图文件路径")
@ApiModelProperty("地图文件路径")
private String mapFilePath;
/** 状态 */
@Excel(name = "状态")
@ApiModelProperty("状态(0正常 1停用)")
private String status;
/** 地图描述 */
@Excel(name = "地图描述")
@ApiModelProperty("地图描述")
private String mapDesc;
}

View File

@ -1,116 +0,0 @@
package com.cmvr.inspection.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import java.util.Date;
/**
* 巡检机器人对象 inspection_robot
*
* @author cmvr-iot
* @since 2026-05-29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ApiModel("巡检机器人")
public class InspectionRobot extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty("id")
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
/** Permanent robot identity reported by QUIC. */
@Excel(name = "机器人ID")
@ApiModelProperty("机器人永久唯一ID")
private String robotId;
/** Business display code. It is not an edge device ID. */
@Excel(name = "机器人业务编号")
@ApiModelProperty("机器人业务编号,仅用于业务展示,不作为设备ID")
private String robotCode;
/** 机器人名称 */
@Excel(name = "机器人名称")
@ApiModelProperty("机器人名称")
private String robotName;
/** 机器人型号 */
@Excel(name = "机器人型号")
@ApiModelProperty("机器人型号")
private String robotModel;
/** 机器人类型 */
@Excel(name = "机器人类型")
@ApiModelProperty("机器人类型")
private String robotType;
/** IP地址 */
@Excel(name = "IP地址")
@ApiModelProperty("IP地址")
private String ipAddress;
/** 端口号 */
@Excel(name = "端口号")
@ApiModelProperty("端口号")
private Long port;
/** 当前地图 */
@Excel(name = "当前地图")
@ApiModelProperty("当前地图")
private String currentMapId;
/** 当前状态 */
@Excel(name = "当前状态")
@ApiModelProperty("AGV运行模式(0未知 1断开 2空闲 3手动 4自动 5充电 6暂停 7停止 8故障 9急停)")
private String status;
/** 电量百分比 */
@Excel(name = "电量百分比")
@ApiModelProperty("电量百分比")
private Integer batteryLevel;
/** 当前位置 */
@Excel(name = "当前位置")
@ApiModelProperty("当前位置")
private String currentPosition;
/** QUIC runtime state, independent from the robot business status. */
@ApiModelProperty("连接状态:0离线,1在线")
private String connectStatus;
@ApiModelProperty("当前QUIC节点ID")
private String quicNodeId;
@ApiModelProperty("当前QUIC会话ID")
private String quicSessionId;
@ApiModelProperty("边缘端启动ID")
private String quicBootId;
@ApiModelProperty("QUIC连接来源IP")
private String observedIp;
@ApiModelProperty("边缘端软件版本")
private String softwareVersion;
@ApiModelProperty("最后心跳时间")
private Date lastHeartbeatTime;
@ApiModelProperty("归档状态:0正常,1归档")
private String archivedStatus;
}

View File

@ -1,41 +0,0 @@
package com.cmvr.inspection.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.cmvr.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机器人设备")
public class InspectionRobotDevice extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@ApiModelProperty("机器人永久唯一ID")
private String robotId;
@ApiModelProperty("边缘端设备ID")
private String deviceId;
private String deviceName;
private Integer deviceKind;
private String typeName;
private String enabled;
private Integer managerState;
private Integer healthStatus;
private String onlineStatus;
private String hasError;
private String errorMessage;
private Date statusUpdatedTime;
private Date lastSeenTime;
private Date offlineTime;
}

View File

@ -1,76 +0,0 @@
package com.cmvr.inspection.domain.vo;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* 巡检地图视图对象(包含关联信息)
*
* @author cmvr-iot
* @since 2026-05-29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ApiModel("巡检地图视图对象")
public class InspectionMapVo extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty("id")
private String id;
/** 地图名称 */
@Excel(name = "地图名称")
@ApiModelProperty("地图名称")
private String mapName;
/** 地图来源的机器人id */
@Excel(name = "地图来源的机器人id")
@ApiModelProperty("地图来源的机器人id")
private String mapSourceRobotId;
/** 地图来源的机器人名称(关联字段) */
@Excel(name = "地图来源的机器人名称")
@ApiModelProperty("地图来源的机器人名称")
private String mapSourceRobotName;
/** 地图来源的地图名称 */
@Excel(name = "地图来源的地图名称")
@ApiModelProperty("地图来源的地图名称")
private String mapSourceName;
/** 地图文件路径 */
@Excel(name = "地图文件路径")
@ApiModelProperty("地图文件路径")
private String mapFilePath;
/** 状态 */
@Excel(name = "状态")
@ApiModelProperty("状态(0正常 1停用)")
private String status;
/** 地图描述 */
@Excel(name = "地图描述")
@ApiModelProperty("地图描述")
private String mapDesc;
/** 创建人昵称 */
@Excel(name = "创建人昵称")
@ApiModelProperty("创建人昵称")
private String createByName;
/** 修改人昵称 */
@Excel(name = "修改人昵称")
@ApiModelProperty("修改人昵称")
private String updateByName;
}

View File

@ -1,137 +0,0 @@
package com.cmvr.inspection.domain.vo;
import com.cmvr.common.annotation.Excel;
import com.cmvr.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 巡检机器人视图对象(包含关联信息)
*
* @author cmvr-iot
* @since 2026-05-29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ApiModel("巡检机器人视图对象")
public class InspectionRobotVo extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** id */
@ApiModelProperty("id")
private String id;
@Excel(name = "机器人ID")
@ApiModelProperty("机器人永久唯一ID")
private String robotId;
/** Business display code. It is not an edge device ID. */
@Excel(name = "机器人业务编号")
@ApiModelProperty("机器人业务编号,仅用于业务展示,不作为设备ID")
private String robotCode;
/** 机器人名称 */
@Excel(name = "机器人名称")
@ApiModelProperty("机器人名称")
private String robotName;
/** 机器人型号 */
@Excel(name = "机器人型号")
@ApiModelProperty("机器人型号")
private String robotModel;
/** 机器人类型 */
@Excel(name = "机器人类型")
@ApiModelProperty("机器人类型")
private String robotType;
/** IP地址 */
@Excel(name = "IP地址")
@ApiModelProperty("IP地址")
private String ipAddress;
/** 端口号 */
@Excel(name = "端口号")
@ApiModelProperty("端口号")
private Integer port;
/** 当前地图ID */
@Excel(name = "当前地图ID")
@ApiModelProperty("当前地图ID")
private String currentMapId;
/** 当前地图名称(关联字段) */
@Excel(name = "当前地图名称")
@ApiModelProperty("当前地图名称")
private String currentMapName;
/** 机器人中地图名称 */
@Excel(name = "机器人中地图名称")
@ApiModelProperty("机器人中地图名称")
private String robotMapName;
/** 当前状态 */
@Excel(name = "当前状态")
@ApiModelProperty("AGV运行模式(0未知 1断开 2空闲 3手动 4自动 5充电 6暂停 7停止 8故障 9急停)")
private String status;
/** 电量百分比 */
@Excel(name = "电量百分比")
@ApiModelProperty("电量百分比")
private Integer batteryLevel;
/** 当前位置 */
@Excel(name = "当前位置")
@ApiModelProperty("当前位置")
private String currentPosition;
/** 创建人昵称 */
@Excel(name = "创建人昵称")
@ApiModelProperty("创建人昵称")
private String createByName;
/** 修改人昵称 */
@Excel(name = "修改人昵称")
@ApiModelProperty("修改人昵称")
private String updateByName;
@ApiModelProperty("连接状态:0离线,1在线")
private String connectStatus;
@ApiModelProperty("当前QUIC节点ID")
private String quicNodeId;
@ApiModelProperty("当前QUIC会话ID")
private String quicSessionId;
@ApiModelProperty("边缘端启动ID")
private String quicBootId;
@ApiModelProperty("QUIC连接来源IP")
private String observedIp;
@ApiModelProperty("边缘端软件版本")
private String softwareVersion;
@ApiModelProperty("最后心跳时间")
private Date lastHeartbeatTime;
@ApiModelProperty("设备总数")
private Integer deviceTotal;
@ApiModelProperty("在线设备数")
private Integer onlineDeviceCount;
@ApiModelProperty("故障设备数")
private Integer faultDeviceCount;
}

View File

@ -1,64 +0,0 @@
package com.cmvr.inspection.mapper;
import java.util.List;
import com.cmvr.inspection.domain.InspectionMap;
import com.cmvr.inspection.domain.vo.InspectionMapVo;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* 巡检地图Mapper接口
*
* @author cmvr-iot
*/
public interface InspectionMapMapper extends MPJBaseMapper<InspectionMap>
{
/**
* 查询巡检地图
*
* @param id 巡检地图主键
* @return 巡检地图
*/
InspectionMap selectInspectionMapById(String id);
/**
* 新增巡检地图
*
* @param inspectionMap 巡检地图
* @return 结果
*/
int insertInspectionMap(InspectionMap inspectionMap);
/**
* 修改巡检地图
*
* @param inspectionMap 巡检地图
* @return 结果
*/
int updateInspectionMap(InspectionMap inspectionMap);
/**
* 批量删除巡检地图
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
int deleteInspectionMapByIds(String[] ids);
/**
* 查询巡检地图视图列表(包含关联信息)
*
* @param inspectionMap 巡检地图
* @return 巡检地图视图集合
*/
List<InspectionMapVo> selectInspectionMapVoList(InspectionMap inspectionMap);
/**
* 根据机器人ID查询地图列表
*
* @param robotId 机器人ID
* @return 地图列表
*/
List<InspectionMapVo> selectMapsByRobotId(@Param("robotId") String robotId);
}

View File

@ -1,29 +0,0 @@
package com.cmvr.inspection.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.inspection.domain.InspectionRobotDevice;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
public interface InspectionRobotDeviceMapper extends BaseMapper<InspectionRobotDevice> {
List<InspectionRobotDevice> selectByRobotId(@Param("robotId") String robotId);
List<InspectionRobotDevice> selectByRobotIdAndKind(@Param("robotId") String robotId,
@Param("deviceKind") Integer deviceKind,
@Param("onlineOnly") boolean onlineOnly);
List<InspectionRobotDevice> selectByIdentity(@Param("robotId") String robotId,
@Param("deviceId") String deviceId);
int updateRuntimeState(InspectionRobotDevice device);
int markMissingOffline(@Param("robotId") String robotId,
@Param("deviceIds") List<String> deviceIds,
@Param("offlineTime") Date offlineTime);
int markAllOffline(@Param("robotId") String robotId,
@Param("offlineTime") Date offlineTime);
}

View File

@ -1,81 +0,0 @@
package com.cmvr.inspection.mapper;
import java.util.List;
import java.util.Date;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.domain.vo.InspectionRobotVo;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* 巡检机器人Mapper接口
*
* @author cmvr-iot
* @since 2026-05-29
*/
public interface InspectionRobotMapper extends MPJBaseMapper<InspectionRobot>
{
/**
* 查询巡检机器人
*
* @param id 巡检机器人主键
* @return 巡检机器人
*/
InspectionRobot selectInspectionRobotById(String id);
List<InspectionRobot> selectByRobotId(String robotId);
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(@Param("heartbeatCutoff") Date heartbeatCutoff,
@Param("excludedRobotIds") List<String> excludedRobotIds);
List<InspectionRobot> selectStaleConnectedRobots(@Param("heartbeatCutoff") Date heartbeatCutoff,
@Param("excludedRobotIds") List<String> excludedRobotIds);
int markHeartbeatExpiredOffline(@Param("id") String id,
@Param("heartbeatCutoff") Date heartbeatCutoff,
@Param("offlineTime") Date offlineTime);
int archiveInspectionRobotByIds(@Param("ids") String[] ids, @Param("updateBy") String updateBy);
/**
* 新增巡检机器人
*
* @param inspectionRobot 巡检机器人
* @return 结果
*/
int insertInspectionRobot(InspectionRobot inspectionRobot);
/**
* 修改巡检机器人
*
* @param inspectionRobot 巡检机器人
* @return 结果
*/
int updateInspectionRobot(InspectionRobot inspectionRobot);
/**
* 批量删除巡检机器人
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
int deleteInspectionRobotByIds(String[] ids);
/**
* 查询巡检机器人视图列表(包含关联信息)
*
* @param inspectionRobot 巡检机器人
* @return 巡检机器人视图集合
*/
List<InspectionRobotVo> selectInspectionRobotVoList(InspectionRobot inspectionRobot);
}

View File

@ -1,66 +0,0 @@
package com.cmvr.inspection.service;
import java.util.List;
import com.baomidou.mybatisplus.spring.service.IService;
import com.cmvr.inspection.domain.InspectionMap;
import com.cmvr.inspection.domain.vo.InspectionMapVo;
/**
* 巡检地图Service接口
*
* @author cmvr-iot
* @since 2026-05-29
*/
public interface IInspectionMapService extends IService<InspectionMap>
{
/**
* 查询巡检地图
*
* @param id 巡检地图主键
* @return 巡检地图
*/
InspectionMap selectInspectionMapById(String id);
/**
* 查询巡检地图列表
*
* @param inspectionMap 巡检地图
* @return 巡检地图集合
*/
List<InspectionMapVo> selectInspectionMapList(InspectionMap inspectionMap);
/**
* 新增巡检地图
*
* @param inspectionMap 巡检地图
* @return 结果
*/
int insertInspectionMap(InspectionMap inspectionMap);
/**
* 修改巡检地图
*
* @param inspectionMap 巡检地图
* @return 结果
*/
int updateInspectionMap(InspectionMap inspectionMap);
/**
* 批量删除巡检地图
*
* @param ids 需要删除的巡检地图主键集合
* @return 结果
*/
int deleteInspectionMapByIds(String[] ids);
/**
* 根据机器人ID查询地图列表
*
* @param robotId 机器人ID
* @return 地图列表
*/
List<InspectionMapVo> getMapsByRobotId(String robotId);
}

View File

@ -1,143 +0,0 @@
package com.cmvr.inspection.service;
import java.util.List;
import cmvr.msgs.AgvUtils;
import com.baomidou.mybatisplus.spring.service.IService;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.domain.InspectionRobotDevice;
import com.cmvr.inspection.domain.vo.InspectionRobotVo;
/**
* 巡检机器人Service接口
*
* @author cmvr-iot
* @since 2026-05-29
*/
public interface IInspectionRobotService extends IService<InspectionRobot>
{
/**
* 查询巡检机器人
*
* @param id 巡检机器人主键
* @return 巡检机器人
*/
InspectionRobot selectInspectionRobotById(String id);
/**
* 查询巡检机器人列表
*
* @param inspectionRobot 巡检机器人
* @return 巡检机器人集合
*/
List<InspectionRobotVo> selectInspectionRobotList(InspectionRobot inspectionRobot);
/**
* 新增巡检机器人
*
* @param inspectionRobot 巡检机器人
* @return 结果
*/
int insertInspectionRobot(InspectionRobot inspectionRobot);
/**
* 修改巡检机器人
*
* @param inspectionRobot 巡检机器人
* @return 结果
*/
int updateInspectionRobot(InspectionRobot inspectionRobot);
/**
* 批量删除巡检机器人
*
* @param ids 需要删除的巡检机器人主键集合
* @return 结果
*/
int deleteInspectionRobotByIds(String[] ids);
List<InspectionRobotDevice> selectRobotDevices(String id);
List<InspectionRobotDevice> selectRobotDevicesByRobotId(String robotId, Integer deviceKind,
boolean onlineOnly);
/**
* 获取机器人地图列表(从AGV获取)
*
* @param robotId 机器人ID
* @return 地图列表
*/
List<String> getRobotMapList(String robotId);
/**
* 绑定机器人地图
*
* @param robotId 机器人ID
* @param mapId 地图ID
* @return 结果
*/
int bindRobotMap(String robotId, String mapId);
/**
* 从机器人下载地图
*
* @param robotId 机器人ID
* @param mapName 地图名称
* @return 地图内容
*/
String downloadMapFromRobot(String robotId, String mapName);
/**
* 上传地图(从源机器人下载到目标机器人)
*
* @param robotId 目标机器人ID
* @return 结果
*/
String uploadMapFromSource(String robotId);
/**
* 同步机器人状态(电量、位置等)
*
* @param robotId 机器人ID
* @return 更新后的机器人信息
*/
InspectionRobot syncRobotStatus(String robotId);
/**
* 获取机器人实时位置
*
* @param robotId 机器人ID
* @return 机器人位置信息
*/
AgvUtils.AgvPose2d getRobotRealtimeLocation(String robotId);
/**
* 导航到指定位置(坐标)
*
* @param robotId 机器人ID
* @param x X坐标(米)
* @param y Y坐标(米)
* @param theta 朝向角(弧度)
*/
void navigateToPosition(String robotId, Double x, Double y, Double theta);
/**
* 开始建图
*
* @param robotId 机器人ID
* @param dimension 建图维度(0:未指定 1:2D 2:3D 3:2D+3D)
* @param mapName 地图名称(可选)
* @param realTime 是否实时建图
* @return 会话ID
*/
String startMapping(String robotId, int dimension, String mapName, boolean realTime);
/**
* 停止建图
*
* @param robotId 机器人ID
*/
void stopMapping(String robotId);
}

View File

@ -1,109 +0,0 @@
package com.cmvr.inspection.service.impl;
import java.util.List;
import java.util.UUID;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.cmvr.common.utils.SecurityUtils;
import com.cmvr.inspection.domain.vo.InspectionMapVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.inspection.mapper.InspectionMapMapper;
import com.cmvr.inspection.domain.InspectionMap;
import com.cmvr.inspection.service.IInspectionMapService;
/**
* 巡检地图Service业务层处理
*
* @author cmvr-iot
* @since 2026-05-29
*/
@Service
public class InspectionMapServiceImpl extends ServiceImpl<InspectionMapMapper, InspectionMap> implements IInspectionMapService
{
@Autowired
private InspectionMapMapper inspectionMapMapper;
/**
* 查询巡检地图
*
* @param id 巡检地图主键
* @return 巡检地图
*/
@Override
public InspectionMap selectInspectionMapById(String id)
{
return inspectionMapMapper.selectInspectionMapById(id);
}
/**
* 查询巡检地图列表
*
* @param inspectionMap 巡检地图
* @return 巡检地图
*/
@Override
public List<InspectionMapVo> selectInspectionMapList(InspectionMap inspectionMap)
{
return inspectionMapMapper.selectInspectionMapVoList(inspectionMap);
}
/**
* 新增巡检地图
*
* @param inspectionMap 巡检地图
* @return 结果
*/
@Override
public int insertInspectionMap(InspectionMap inspectionMap)
{
// 生成UUID作为主键
if (inspectionMap.getId() == null || inspectionMap.getId().isEmpty()) {
inspectionMap.setId(UUID.randomUUID().toString().replace("-", ""));
}
inspectionMap.setCreateTime(DateUtils.getNowDate());
inspectionMap.setCreateBy(SecurityUtils.getUsername());
return inspectionMapMapper.insertInspectionMap(inspectionMap);
}
/**
* 修改巡检地图
*
* @param inspectionMap 巡检地图
* @return 结果
*/
@Override
public int updateInspectionMap(InspectionMap inspectionMap)
{
inspectionMap.setUpdateTime(DateUtils.getNowDate());
inspectionMap.setUpdateBy(SecurityUtils.getUsername());
return inspectionMapMapper.updateInspectionMap(inspectionMap);
}
/**
* 批量删除巡检地图
*
* @param ids 需要删除的巡检地图主键
* @return 结果
*/
@Override
public int deleteInspectionMapByIds(String[] ids)
{
return inspectionMapMapper.deleteInspectionMapByIds(ids);
}
/**
* 根据机器人ID查询地图列表
*
* @param robotId 机器人ID
* @return 地图列表
*/
@Override
public List<InspectionMapVo> getMapsByRobotId(String robotId)
{
return inspectionMapMapper.selectMapsByRobotId(robotId);
}
}

View File

@ -15,7 +15,6 @@ import com.cmvr.inspection.domain.InspectionAlarm;
import com.cmvr.inspection.domain.InspectionDetectionAlert; import com.cmvr.inspection.domain.InspectionDetectionAlert;
import com.cmvr.inspection.domain.InspectionResult; import com.cmvr.inspection.domain.InspectionResult;
import com.cmvr.inspection.domain.InspectionResultMedia; import com.cmvr.inspection.domain.InspectionResultMedia;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.domain.InspectionTask; import com.cmvr.inspection.domain.InspectionTask;
import com.cmvr.inspection.domain.InspectionTaskInstance; import com.cmvr.inspection.domain.InspectionTaskInstance;
import com.cmvr.inspection.domain.InspectionTaskLog; import com.cmvr.inspection.domain.InspectionTaskLog;
@ -28,7 +27,8 @@ import com.cmvr.inspection.enums.TaskStatusEnum;
import com.cmvr.inspection.mapper.InspectionAlarmMapper; import com.cmvr.inspection.mapper.InspectionAlarmMapper;
import com.cmvr.inspection.mapper.InspectionResultMapper; import com.cmvr.inspection.mapper.InspectionResultMapper;
import com.cmvr.inspection.mapper.InspectionResultMediaMapper; import com.cmvr.inspection.mapper.InspectionResultMediaMapper;
import com.cmvr.inspection.mapper.InspectionRobotMapper; import com.cmvr.resource.mapper.ResourceRobotMapper;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.inspection.mapper.InspectionTaskMapper; import com.cmvr.inspection.mapper.InspectionTaskMapper;
import com.cmvr.inspection.mapper.InspectionTaskInstanceMapper; import com.cmvr.inspection.mapper.InspectionTaskInstanceMapper;
import com.cmvr.inspection.service.IInspectionResultService; import com.cmvr.inspection.service.IInspectionResultService;
@ -76,7 +76,7 @@ public class InspectionResultServiceImpl extends ServiceImpl<InspectionResultMap
private final InspectionAlarmMapper alarmMapper; private final InspectionAlarmMapper alarmMapper;
private final InspectionTaskMapper taskMapper; private final InspectionTaskMapper taskMapper;
private final InspectionTaskInstanceMapper taskInstanceMapper; private final InspectionTaskInstanceMapper taskInstanceMapper;
private final InspectionRobotMapper robotMapper; private final ResourceRobotMapper robotMapper;
private final IInspectionTaskLogService taskLogService; private final IInspectionTaskLogService taskLogService;
private final MessagePushService messagePushService; private final MessagePushService messagePushService;
@ -442,7 +442,7 @@ public class InspectionResultServiceImpl extends ServiceImpl<InspectionResultMap
taskInstance = new InspectionTaskInstance(); taskInstance = new InspectionTaskInstance();
taskInstance.setId(result.getTaskInstanceId()); taskInstance.setId(result.getTaskInstanceId());
} }
InspectionRobot robot = null; ResourceRobot robot = null;
if (taskInstance != null) if (taskInstance != null)
{ {
// 仅在需要构建告警时查询完整任务实例。 // 仅在需要构建告警时查询完整任务实例。

View File

@ -1,464 +0,0 @@
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;
import com.cmvr.edge.client.service.EdgeAgvService;
import com.cmvr.inspection.domain.InspectionMap;
import com.cmvr.inspection.domain.InspectionRobotDevice;
import com.cmvr.inspection.domain.vo.InspectionRobotVo;
import com.cmvr.inspection.mapper.InspectionMapMapper;
import com.cmvr.inspection.mapper.InspectionRobotDeviceMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.inspection.mapper.InspectionRobotMapper;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.service.IInspectionRobotService;
import cn.hutool.core.util.StrUtil;
import com.cmvr.common.exception.GlobalException;
/**
* 巡检机器人Service业务层处理
*
* @author cmvr-iot
* @since 2026-05-29
*/
@Service
public class InspectionRobotServiceImpl extends ServiceImpl<InspectionRobotMapper, InspectionRobot> implements IInspectionRobotService
{
@Autowired
private InspectionRobotMapper inspectionRobotMapper;
@Autowired
private InspectionMapMapper inspectionMapMapper;
@Autowired
private EdgeAgvService edgeAgvService;
@Autowired
private InspectionRobotDeviceMapper inspectionRobotDeviceMapper;
/**
* 查询巡检机器人
*
* @param id 巡检机器人主键
* @return 巡检机器人
*/
@Override
public InspectionRobot selectInspectionRobotById(String id)
{
return inspectionRobotMapper.selectInspectionRobotById(id);
}
/**
* 查询巡检机器人列表
*
* @param inspectionRobot 巡检机器人
* @return 巡检机器人
*/
@Override
public List<InspectionRobotVo> selectInspectionRobotList(InspectionRobot inspectionRobot)
{
return inspectionRobotMapper.selectInspectionRobotVoList(inspectionRobot);
}
/**
* 新增巡检机器人
*
* @param inspectionRobot 巡检机器人
* @return 结果
*/
@Override
public synchronized int insertInspectionRobot(InspectionRobot inspectionRobot)
{
String robotId = normalizeRobotId(inspectionRobot.getRobotId());
if (StrUtil.isBlank(robotId)) {
throw new GlobalException("机器人ID不能为空");
}
if (!inspectionRobotMapper.selectByRobotId(robotId).isEmpty()) {
throw new GlobalException("机器人ID已存在:" + robotId);
}
inspectionRobot.setRobotId(robotId);
inspectionRobot.setIpAddress(null);
inspectionRobot.setPort(null);
inspectionRobot.setConnectStatus("0");
inspectionRobot.setArchivedStatus("0");
// 生成UUID作为主键
if (inspectionRobot.getId() == null || inspectionRobot.getId().isEmpty()) {
inspectionRobot.setId(UUID.randomUUID().toString().replace("-", ""));
}
inspectionRobot.setCreateTime(DateUtils.getNowDate());
inspectionRobot.setCreateBy(SecurityUtils.getUsername());
return inspectionRobotMapper.insertInspectionRobot(inspectionRobot);
}
/**
* 修改巡检机器人
*
* @param inspectionRobot 巡检机器人
* @return 结果
*/
@Override
public int updateInspectionRobot(InspectionRobot inspectionRobot)
{
InspectionRobot existing = inspectionRobotMapper.selectInspectionRobotById(inspectionRobot.getId());
if (existing == null) {
throw new GlobalException("机器人不存在");
}
if (!java.util.Objects.equals(existing.getRobotId(), normalizeRobotId(inspectionRobot.getRobotId()))) {
throw new GlobalException("机器人ID是永久身份标识,不允许修改");
}
inspectionRobot.setRobotId(null);
inspectionRobot.setIpAddress(null);
inspectionRobot.setPort(null);
inspectionRobot.setUpdateTime(DateUtils.getNowDate());
inspectionRobot.setUpdateBy(SecurityUtils.getUsername());
return inspectionRobotMapper.updateInspectionRobot(inspectionRobot);
}
/**
* 批量删除巡检机器人
*
* @param ids 需要删除的巡检机器人主键
* @return 结果
*/
@Override
public int deleteInspectionRobotByIds(String[] ids)
{
return inspectionRobotMapper.archiveInspectionRobotByIds(ids, SecurityUtils.getUsername());
}
@Override
public List<InspectionRobotDevice> selectRobotDevices(String id)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(id);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
if (StrUtil.isBlank(robot.getRobotId())) {
return java.util.Collections.emptyList();
}
return inspectionRobotDeviceMapper.selectByRobotId(robot.getRobotId());
}
@Override
public List<InspectionRobotDevice> selectRobotDevicesByRobotId(String robotId, Integer deviceKind,
boolean onlineOnly)
{
String normalizedRobotId = normalizeRobotId(robotId);
if (StrUtil.isBlank(normalizedRobotId)) {
throw new GlobalException("机器人ID不能为空");
}
if (inspectionRobotMapper.selectByRobotId(normalizedRobotId).isEmpty()) {
throw new GlobalException("机器人不存在:" + normalizedRobotId);
}
return inspectionRobotDeviceMapper.selectByRobotIdAndKind(
normalizedRobotId, deviceKind, onlineOnly);
}
private static String normalizeRobotId(String robotId)
{
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获取)
*
* @param robotId 机器人ID
* @return 地图列表
*/
@Override
public List<String> getRobotMapList(String robotId)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
return edgeAgvService.listMaps(buildAgvRequest(robot));
}
/**
* 绑定机器人地图
*
* @param robotId 机器人ID
* @param mapId 地图ID
* @return 结果
*/
@Override
public int bindRobotMap(String robotId, String mapId)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
InspectionMap map = inspectionMapMapper.selectInspectionMapById(mapId);
if (map == null) {
throw new GlobalException("地图不存在");
}
robot.setCurrentMapId(mapId);
robot.setUpdateTime(DateUtils.getNowDate());
robot.setUpdateBy(SecurityUtils.getUsername());
return inspectionRobotMapper.updateInspectionRobot(robot);
}
/**
* 从机器人下载地图
*
* @param robotId 机器人ID
* @param mapName 地图名称
* @return 地图内容
*/
@Override
public String downloadMapFromRobot(String robotId, String mapName)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
return edgeAgvService.downloadMap(buildAgvRequest(robot), mapName);
}
/**
* 上传地图(从源机器人下载到目标机器人)
*
* @param robotId 目标机器人ID
* @return 结果
*/
@Override
public String uploadMapFromSource(String robotId)
{
// 1. 获取目标机器人信息
InspectionRobot targetRobot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (targetRobot == null) {
throw new GlobalException("目标机器人不存在");
}
// 2. 获取地图信息
InspectionMap map = inspectionMapMapper.selectInspectionMapById(targetRobot.getCurrentMapId());
if (map == null) {
throw new GlobalException("地图不存在");
}
// 3. 检查地图是否有来源机器人ID和地图名称
if (StrUtil.isBlank(map.getMapSourceRobotId())) {
throw new GlobalException("地图未配置来源机器人ID");
}
if (StrUtil.isBlank(map.getMapSourceName())) {
throw new GlobalException("地图未配置来源地图名称");
}
// 4. 获取来源机器人信息
InspectionRobot sourceRobot = inspectionRobotMapper.selectInspectionRobotById(map.getMapSourceRobotId());
if (sourceRobot == null) {
throw new GlobalException("来源机器人不存在");
}
// 5. 从来源机器人下载地图JSON
String mapContent = edgeAgvService.downloadMap(
buildAgvRequest(sourceRobot), map.getMapSourceName());
if (StrUtil.isBlank(mapContent)) {
throw new GlobalException("下载的地图内容为空");
}
// 6. 上传地图到目标机器人
edgeAgvService.uploadMap(
buildAgvRequest(targetRobot), map.getMapSourceName(), mapContent);
return "上传成功";
}
/**
* 同步机器人状态(电量、位置等)
*
* @param robotId 机器人ID
* @return 更新后的机器人信息
*/
@Override
public InspectionRobot syncRobotStatus(String robotId)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
try {
AgvUtils.AgvRuntimeState runtimeState = edgeAgvService.getRuntimeState(buildAgvRequest(robot));
if (runtimeState == null) {
throw new GlobalException("AGV未返回运行状态");
}
// 设置电池电量
if (runtimeState.hasBattery()) {
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(String.format(Locale.ROOT, "%.3f,%.3f,%.3f",
pose.getX(), pose.getY(), pose.getTheta()));
}
// Keep the platform status identical to the edge AgvMode value.
robot.setStatus(String.valueOf(runtimeState.getMode()));
inspectionRobotMapper.updateRuntimeStatus(robot);
} catch (Exception e) {
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;
}
/**
* 获取机器人实时位置
*
* @param robotId 机器人ID
* @return 机器人位置信息
*/
@Override
public AgvUtils.AgvPose2d getRobotRealtimeLocation(String robotId)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
AgvUtils.AgvRuntimeState runtimeState = edgeAgvService.getRuntimeState(buildAgvRequest(robot));
return runtimeState.hasPose() ? runtimeState.getPose() : null;
}
/**
* 导航到指定位置(坐标)
*
* @param robotId 机器人ID
* @param x X坐标(米)
* @param y Y坐标(米)
* @param theta 朝向角(弧度)
*/
@Override
public void navigateToPosition(String robotId, Double x, Double y, Double theta)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
if (x == null || y == null) {
throw new GlobalException("X和Y坐标不能为空");
}
// 构建目标位姿
AgvUtils.AgvPose2d pose = AgvUtils.AgvPose2d.newBuilder()
.setX(x)
.setY(y)
.setTheta(theta != null ? theta : 0.0)
.build();
// 调用边缘端接口进行导航
edgeAgvService.navigateToPose(buildAgvRequest(robot), pose);
}
/**
* 开始建图
*
* @param robotId 机器人ID
* @param dimension 建图维度(0:未指定 1:2D 2:3D 3:2D+3D)
* @param mapName 地图名称(可选)
* @param realTime 是否实时建图
* @return 会话ID
*/
@Override
public String startMapping(String robotId, int dimension, String mapName, boolean realTime)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
AgvUtils.AgvMapDimension mapDimension = AgvUtils.AgvMapDimension.forNumber(dimension);
return edgeAgvService.startMapping(
buildAgvRequest(robot), mapDimension, mapName, realTime);
}
/**
* 停止建图
*
* @param robotId 机器人ID
*/
@Override
public void stopMapping(String robotId)
{
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(robotId);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
edgeAgvService.stopMapping(buildAgvRequest(robot));
}
}

View File

@ -10,12 +10,12 @@ import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.cmvr.common.exception.ServiceException; import com.cmvr.common.exception.ServiceException;
import com.cmvr.common.utils.SecurityUtils; import com.cmvr.common.utils.SecurityUtils;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.domain.InspectionTask; import com.cmvr.inspection.domain.InspectionTask;
import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery; import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery;
import com.cmvr.inspection.domain.vo.InspectionTaskInstanceVo; import com.cmvr.inspection.domain.vo.InspectionTaskInstanceVo;
import com.cmvr.inspection.enums.TaskStatusEnum; import com.cmvr.inspection.enums.TaskStatusEnum;
import com.cmvr.inspection.mapper.InspectionRobotMapper; import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import com.cmvr.inspection.service.IInspectionTaskService; import com.cmvr.inspection.service.IInspectionTaskService;
import com.cmvr.test.flow.control.FlowControlService; import com.cmvr.test.flow.control.FlowControlService;
import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService; import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService;
@ -50,7 +50,7 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
@Autowired @Autowired
private InspectionTaskInstanceMapper inspectionTaskInstanceMapper; private InspectionTaskInstanceMapper inspectionTaskInstanceMapper;
@Autowired @Autowired
private InspectionRobotMapper inspectionRobotMapper; private ResourceRobotMapper resourceRobotMapper;
@Autowired @Autowired
private ITeTaskOrchestrationService teTaskOrchestrationService; private ITeTaskOrchestrationService teTaskOrchestrationService;
@ -179,13 +179,13 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
robotId = firstAssignedRobotId(runtimeAssignments); robotId = firstAssignedRobotId(runtimeAssignments);
} }
if (StrUtil.isBlank(robotId) && StrUtil.isNotBlank(instance.getRobotId())) { if (StrUtil.isBlank(robotId) && StrUtil.isNotBlank(instance.getRobotId())) {
InspectionRobot legacyRobot = inspectionRobotMapper.selectInspectionRobotById(instance.getRobotId()); ResourceRobot storedRobot = resourceRobotMapper.selectById(instance.getRobotId());
if (legacyRobot == null || StrUtil.isBlank(legacyRobot.getRobotId())) { if (storedRobot == null || StrUtil.isBlank(storedRobot.getRobotId())) {
throw new ServiceException("巡检任务实例关联的旧机器人不存在"); throw new ServiceException("巡检任务实例关联的机器人不存在");
} }
robotId = legacyRobot.getRobotId(); robotId = storedRobot.getRobotId();
} }
InspectionRobot primaryRobot = findRobotByIdentity(robotId); ResourceRobot primaryRobot = findRobotByIdentity(robotId);
if (primaryRobot != null) { if (primaryRobot != null) {
update.setRobotId(primaryRobot.getId()); update.setRobotId(primaryRobot.getId());
} }
@ -278,7 +278,7 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
if (StrUtil.isBlank(value)) { if (StrUtil.isBlank(value)) {
return value; return value;
} }
InspectionRobot robot = inspectionRobotMapper.selectInspectionRobotById(value); ResourceRobot robot = resourceRobotMapper.selectById(value);
return robot != null && StrUtil.isNotBlank(robot.getRobotId()) ? robot.getRobotId() : value; return robot != null && StrUtil.isNotBlank(robot.getRobotId()) ? robot.getRobotId() : value;
} }
@ -299,12 +299,11 @@ public class InspectionTaskInstanceServiceImpl extends ServiceImpl<InspectionTas
return null; return null;
} }
private InspectionRobot findRobotByIdentity(String robotId) { private ResourceRobot findRobotByIdentity(String robotId) {
if (StrUtil.isBlank(robotId)) { if (StrUtil.isBlank(robotId)) {
return null; return null;
} }
List<InspectionRobot> robots = inspectionRobotMapper.selectByRobotId(robotId); return resourceRobotMapper.selectByRobotIdentity(robotId);
return robots == null || robots.isEmpty() ? null : robots.get(0);
} }
/** /**

View File

@ -1,21 +0,0 @@
package com.cmvr.inspection.task;
import com.cmvr.inspection.service.RobotQuicStateService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "cmvr.quic", name = "enabled", havingValue = "true")
public class RobotHeartbeatExpiryTask {
private final RobotQuicStateService robotQuicStateService;
@Scheduled(initialDelayString = "${cmvr.inspection.robot-state.initial-delay-ms:3000}",
fixedDelayString = "${cmvr.inspection.robot-state.heartbeat-check-ms:5000}")
public void expireStaleRobots() {
robotQuicStateService.expireStaleRobots();
}
}

View File

@ -1,71 +0,0 @@
package com.cmvr.inspection.task;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.config.RobotStateProperties;
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.Date;
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 RobotStateProperties robotStateProperties;
private final Set<String> syncingRobotIds = ConcurrentHashMap.newKeySet();
public RobotRuntimeStateRefreshTask(InspectionRobotMapper inspectionRobotMapper,
IInspectionRobotService inspectionRobotService,
@Qualifier("threadPoolTaskExecutor") TaskExecutor taskExecutor,
RobotStateProperties robotStateProperties)
{
this.inspectionRobotMapper = inspectionRobotMapper;
this.inspectionRobotService = inspectionRobotService;
this.taskExecutor = taskExecutor;
this.robotStateProperties = robotStateProperties;
}
@Scheduled(initialDelayString = "${cmvr.inspection.robot-state.initial-delay-ms:3000}",
fixedDelayString = "${cmvr.inspection.robot-state.refresh-ms:5000}")
public void refreshConnectedRobots()
{
Date cutoff = robotStateProperties.heartbeatCutoff(new Date());
for (InspectionRobot robot : inspectionRobotMapper.selectRuntimeSyncCandidates(
cutoff, robotStateProperties.normalizedVirtualRobotIds())) {
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());
}
}
}

View File

@ -206,7 +206,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
u1.nick_name as create_by_name, u2.nick_name as update_by_name u1.nick_name as create_by_name, u2.nick_name as update_by_name
from inspection_alarm a from inspection_alarm a
left join inspection_task t on a.task_id = t.id left join inspection_task t on a.task_id = t.id
left join inspection_robot r on a.robot_id = r.id left join iot_robot r on a.robot_id = r.id
left join sys_user u1 on a.create_by = u1.user_name <!-- 创建人关联用户 --> left join sys_user u1 on a.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on a.update_by = u2.user_name <!-- 更新人关联用户 --> left join sys_user u2 on a.update_by = u2.user_name <!-- 更新人关联用户 -->
<where> <where>

View File

@ -1,162 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.inspection.mapper.InspectionMapMapper">
<resultMap type="InspectionMap" id="InspectionMapResult">
<result property="id" column="id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="mapName" column="map_name" />
<result property="mapSourceRobotId" column="map_source_robot_id" />
<result property="mapSourceName" column="map_source_name" />
<result property="mapFilePath" column="map_file_path" />
<result property="status" column="status" />
<result property="mapDesc" column="map_desc" />
</resultMap>
<resultMap type="com.cmvr.inspection.domain.vo.InspectionMapVo" id="InspectionMapVoResult">
<result property="id" column="id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="mapName" column="map_name" />
<result property="mapSourceRobotId" column="map_source_robot_id" />
<result property="mapSourceRobotName" column="map_source_robot_name" />
<result property="mapSourceName" column="map_source_name" />
<result property="mapFilePath" column="map_file_path" />
<result property="status" column="status" />
<result property="mapDesc" column="map_desc" />
<result property="createByName" column="create_by_name" />
<result property="updateByName" column="update_by_name" />
</resultMap>
<sql id="selectInspectionMapVo">
select id, create_by, create_time, update_by, update_time, remark, map_name, map_source_robot_id, map_source_name, map_file_path, status, map_desc from inspection_map
</sql>
<select id="selectInspectionMapList" parameterType="InspectionMap" resultMap="InspectionMapResult">
<include refid="selectInspectionMapVo"/>
<where>
<if test="mapName != null and mapName != ''"> and map_name like concat('%', #{mapName}, '%')</if>
<if test="status != null and status != ''"> and status = #{status}</if>
</where>
</select>
<select id="selectInspectionMapById" parameterType="String" resultMap="InspectionMapResult">
<include refid="selectInspectionMapVo"/>
where id = #{id}
</select>
<insert id="insertInspectionMap" parameterType="InspectionMap">
insert into inspection_map
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="mapName != null">map_name,</if>
<if test="mapSourceRobotId != null">map_source_robot_id,</if>
<if test="mapSourceName != null">map_source_name,</if>
<if test="mapFilePath != null">map_file_path,</if>
<if test="status != null">status,</if>
<if test="mapDesc != null">map_desc,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="mapName != null">#{mapName},</if>
<if test="mapSourceRobotId != null">#{mapSourceRobotId},</if>
<if test="mapSourceName != null">#{mapSourceName},</if>
<if test="mapFilePath != null">#{mapFilePath},</if>
<if test="status != null">#{status},</if>
<if test="mapDesc != null">#{mapDesc},</if>
</trim>
</insert>
<update id="updateInspectionMap" parameterType="InspectionMap">
update inspection_map
<trim prefix="SET" suffixOverrides=",">
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="mapName != null">map_name = #{mapName},</if>
<if test="mapSourceRobotId != null">map_source_robot_id = #{mapSourceRobotId},</if>
<if test="mapSourceName != null">map_source_name = #{mapSourceName},</if>
<if test="mapFilePath != null">map_file_path = #{mapFilePath},</if>
<if test="status != null">status = #{status},</if>
<if test="mapDesc != null">map_desc = #{mapDesc},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteInspectionMapById" parameterType="String">
delete from inspection_map where id = #{id}
</delete>
<delete id="deleteInspectionMapByIds" parameterType="String">
delete from inspection_map where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<select id="selectInspectionMapVoList" parameterType="InspectionMap" resultMap="InspectionMapVoResult">
select m.id, m.create_by, m.create_time, m.update_by, m.update_time, m.remark,
m.map_name, m.map_source_robot_id, r.robot_name as map_source_robot_name,
m.map_source_name, m.map_file_path, m.status, m.map_desc,
u1.nick_name as create_by_name, u2.nick_name as update_by_name
from inspection_map m
left join inspection_robot r on m.map_source_robot_id = r.id
left join sys_user u1 on m.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on m.update_by = u2.user_name <!-- 更新人关联用户 -->
<where>
<if test="mapName != null and mapName != ''"> and m.map_name like concat('%', #{mapName}, '%')</if>
<if test="mapDesc != null and mapDesc != ''"> and m.map_desc like concat('%', #{mapDesc}, '%')</if>
<if test="status != null and status != ''"> and m.status = #{status}</if>
</where>
</select>
<select id="selectInspectionMapVoList" parameterType="InspectionMap" resultMap="InspectionMapVoResult">
select m.id, m.create_by, m.create_time, m.update_by, m.update_time, m.remark,
m.map_name, m.map_source_robot_id, r.robot_name as map_source_robot_name,
m.map_source_name, m.map_file_path, m.status, m.map_desc,
u1.nick_name as create_by_name, u2.nick_name as update_by_name
from inspection_map m
left join inspection_robot r on m.map_source_robot_id = r.id
left join sys_user u1 on m.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on m.update_by = u2.user_name <!-- 更新人关联用户 -->
<where>
<if test="mapName != null and mapName != ''"> and m.map_name like concat('%', #{mapName}, '%')</if>
<if test="mapDesc != null and mapDesc != ''"> and m.map_desc like concat('%', #{mapDesc}, '%')</if>
<if test="status != null and status != ''"> and m.status = #{status}</if>
</where>
</select>
<select id="selectMapsByRobotId" resultMap="InspectionMapVoResult">
select m.id, m.create_by, m.create_time, m.update_by, m.update_time, m.remark,
m.map_name, m.map_source_robot_id, r.robot_name as map_source_robot_name,
m.map_source_name, m.map_file_path, m.status, m.map_desc,
u1.nick_name as create_by_name, u2.nick_name as update_by_name
from inspection_map m
left join inspection_robot r on m.map_source_robot_id = r.id
left join sys_user u1 on m.create_by = u1.user_name
left join sys_user u2 on m.update_by = u2.user_name
where m.map_source_robot_id = #{robotId}
order by m.create_time desc
</select>
</mapper>

View File

@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.inspection.mapper.InspectionRobotDeviceMapper">
<resultMap id="RobotDeviceResult" type="com.cmvr.inspection.domain.InspectionRobotDevice">
<id property="id" column="id"/>
<result property="robotId" column="robot_id"/>
<result property="deviceId" column="device_id"/>
<result property="deviceName" column="device_name"/>
<result property="deviceKind" column="device_kind"/>
<result property="typeName" column="type_name"/>
<result property="enabled" column="enabled"/>
<result property="managerState" column="manager_state"/>
<result property="healthStatus" column="health_status"/>
<result property="onlineStatus" column="online_status"/>
<result property="hasError" column="has_error"/>
<result property="errorMessage" column="error_message"/>
<result property="statusUpdatedTime" column="status_updated_time"/>
<result property="lastSeenTime" column="last_seen_time"/>
<result property="offlineTime" column="offline_time"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="columns">id, robot_id, device_id, device_name, device_kind, type_name, enabled,
manager_state, health_status, online_status, has_error, error_message,
status_updated_time, last_seen_time, offline_time, create_by, create_time,
update_by, update_time, remark</sql>
<select id="selectByRobotId" resultMap="RobotDeviceResult">
select <include refid="columns"/> from inspection_robot_device
where robot_id = #{robotId}
order by online_status desc, device_kind, device_id
</select>
<select id="selectByRobotIdAndKind" resultMap="RobotDeviceResult">
select <include refid="columns"/> from inspection_robot_device
where robot_id = #{robotId}
<if test="deviceKind != null">
and device_kind = #{deviceKind}
</if>
<if test="onlineOnly">
and online_status = '1' and enabled = '1'
</if>
order by online_status desc, device_kind, device_name, device_id
</select>
<select id="selectByIdentity" resultMap="RobotDeviceResult">
select <include refid="columns"/> from inspection_robot_device
where robot_id = #{robotId} and device_id = #{deviceId}
</select>
<update id="updateRuntimeState" parameterType="com.cmvr.inspection.domain.InspectionRobotDevice">
update inspection_robot_device
set device_kind = #{deviceKind}, type_name = #{typeName}, enabled = #{enabled},
manager_state = #{managerState}, health_status = #{healthStatus},
online_status = #{onlineStatus}, has_error = #{hasError},
error_message = #{errorMessage}, status_updated_time = #{statusUpdatedTime},
last_seen_time = #{lastSeenTime}, offline_time = #{offlineTime},
update_by = #{updateBy}, update_time = #{updateTime}
where id = #{id}
</update>
<update id="markMissingOffline">
update inspection_robot_device
set online_status = '0', offline_time = #{offlineTime},
update_by = 'quic', update_time = #{offlineTime}
where robot_id = #{robotId} and online_status != '0'
<if test="deviceIds != null and deviceIds.size() > 0">
and device_id not in
<foreach collection="deviceIds" item="deviceId" open="(" separator="," close=")">
#{deviceId}
</foreach>
</if>
</update>
<update id="markAllOffline">
update inspection_robot_device
set online_status = '0', offline_time = #{offlineTime},
update_by = 'quic', update_time = #{offlineTime}
where robot_id = #{robotId} and online_status != '0'
</update>
</mapper>

View File

@ -1,283 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.inspection.mapper.InspectionRobotMapper">
<resultMap type="InspectionRobot" id="InspectionRobotResult">
<result property="id" column="id" />
<result property="robotId" column="robot_id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="robotCode" column="robot_code" />
<result property="robotName" column="robot_name" />
<result property="robotModel" column="robot_model" />
<result property="robotType" column="robot_type" />
<result property="ipAddress" column="ip_address" />
<result property="port" column="port" />
<result property="currentMapId" column="current_map_id" />
<result property="status" column="status" />
<result property="batteryLevel" column="battery_level" />
<result property="currentPosition" column="current_position" />
<result property="connectStatus" column="connect_status" />
<result property="quicNodeId" column="quic_node_id" />
<result property="quicSessionId" column="quic_session_id" />
<result property="quicBootId" column="quic_boot_id" />
<result property="observedIp" column="observed_ip" />
<result property="softwareVersion" column="software_version" />
<result property="lastHeartbeatTime" column="last_heartbeat_time" />
<result property="archivedStatus" column="archived_status" />
</resultMap>
<resultMap type="com.cmvr.inspection.domain.vo.InspectionRobotVo" id="InspectionRobotVoResult">
<result property="id" column="id" />
<result property="robotId" column="robot_id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
<result property="robotCode" column="robot_code" />
<result property="robotName" column="robot_name" />
<result property="robotModel" column="robot_model" />
<result property="robotType" column="robot_type" />
<result property="ipAddress" column="ip_address" />
<result property="port" column="port" />
<result property="currentMapId" column="current_map_id" />
<result property="currentMapName" column="current_map_name" />
<result property="robotMapName" column="robot_map_name" />
<result property="status" column="status" />
<result property="batteryLevel" column="battery_level" />
<result property="currentPosition" column="current_position" />
<result property="createByName" column="create_by_name" />
<result property="updateByName" column="update_by_name" />
<result property="connectStatus" column="connect_status" />
<result property="quicNodeId" column="quic_node_id" />
<result property="quicSessionId" column="quic_session_id" />
<result property="quicBootId" column="quic_boot_id" />
<result property="observedIp" column="observed_ip" />
<result property="softwareVersion" column="software_version" />
<result property="lastHeartbeatTime" column="last_heartbeat_time" />
<result property="deviceTotal" column="device_total" />
<result property="onlineDeviceCount" column="online_device_count" />
<result property="faultDeviceCount" column="fault_device_count" />
</resultMap>
<sql id="selectInspectionRobotVo">
select id, robot_id, create_by, create_time, update_by, update_time, remark,
robot_code, robot_name, robot_model, robot_type, ip_address, port,
current_map_id, status, battery_level, current_position,
connect_status, quic_node_id, quic_session_id, quic_boot_id,
observed_ip, software_version, last_heartbeat_time, archived_status
from inspection_robot
</sql>
<select id="selectInspectionRobotList" parameterType="InspectionRobot" resultMap="InspectionRobotResult">
<include refid="selectInspectionRobotVo"/>
<where>
<if test="robotCode != null and robotCode != ''"> and robot_code = #{robotCode}</if>
<if test="robotId != null and robotId != ''"> and robot_id = #{robotId}</if>
<if test="robotName != null and robotName != ''"> and robot_name like concat('%', #{robotName}, '%')</if>
<if test="robotModel != null and robotModel != ''"> and robot_model = #{robotModel}</if>
<if test="robotType != null and robotType != ''"> and robot_type = #{robotType}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
<if test="currentMapId != null and currentMapId != ''"> and current_map_id = #{currentMapId}</if>
</where>
</select>
<select id="selectInspectionRobotById" parameterType="String" resultMap="InspectionRobotResult">
<include refid="selectInspectionRobotVo"/>
where id = #{id}
</select>
<select id="selectByRobotId" parameterType="String" resultMap="InspectionRobotResult">
<include refid="selectInspectionRobotVo"/>
where robot_id = #{robotId}
</select>
<insert id="insertInspectionRobot" parameterType="InspectionRobot">
insert into inspection_robot
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="robotId != null">robot_id,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="robotCode != null">robot_code,</if>
<if test="robotName != null">robot_name,</if>
<if test="robotModel != null">robot_model,</if>
<if test="robotType != null">robot_type,</if>
<if test="ipAddress != null">ip_address,</if>
<if test="port != null">port,</if>
<if test="currentMapId != null">current_map_id,</if>
<if test="status != null">status,</if>
<if test="batteryLevel != null">battery_level,</if>
<if test="currentPosition != null">current_position,</if>
<if test="connectStatus != null">connect_status,</if>
<if test="archivedStatus != null">archived_status,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="robotId != null">#{robotId},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="robotCode != null">#{robotCode},</if>
<if test="robotName != null">#{robotName},</if>
<if test="robotModel != null">#{robotModel},</if>
<if test="robotType != null">#{robotType},</if>
<if test="ipAddress != null">#{ipAddress},</if>
<if test="port != null">#{port},</if>
<if test="currentMapId != null">#{currentMapId},</if>
<if test="status != null">#{status},</if>
<if test="batteryLevel != null">#{batteryLevel},</if>
<if test="currentPosition != null">#{currentPosition},</if>
<if test="connectStatus != null">#{connectStatus},</if>
<if test="archivedStatus != null">#{archivedStatus},</if>
</trim>
</insert>
<update id="updateInspectionRobot" parameterType="InspectionRobot">
update inspection_robot
<trim prefix="SET" suffixOverrides=",">
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="robotCode != null">robot_code = #{robotCode},</if>
<if test="robotName != null">robot_name = #{robotName},</if>
<if test="robotModel != null">robot_model = #{robotModel},</if>
<if test="robotType != null">robot_type = #{robotType},</if>
<if test="ipAddress != null">ip_address = #{ipAddress},</if>
<if test="port != null">port = #{port},</if>
<if test="currentMapId != null">current_map_id = #{currentMapId},</if>
<if test="status != null">status = #{status},</if>
<if test="batteryLevel != null">battery_level = #{batteryLevel},</if>
<if test="currentPosition != null">current_position = #{currentPosition},</if>
</trim>
where id = #{id}
</update>
<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},
update_by = 'quic', update_time = #{updateTime}
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 r.last_heartbeat_time is not null
and r.last_heartbeat_time &gt;= #{heartbeatCutoff}
<if test="excludedRobotIds != null and excludedRobotIds.size() > 0">
and lower(r.robot_id) not in
<foreach collection="excludedRobotIds" item="robotId" open="(" separator="," close=")">
#{robotId}
</foreach>
</if>
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>
<select id="selectStaleConnectedRobots" resultMap="InspectionRobotResult">
select r.id, r.robot_id, r.connect_status, r.last_heartbeat_time, r.archived_status
from inspection_robot r
where r.connect_status = '1'
and coalesce(r.archived_status, '0') = '0'
and (r.last_heartbeat_time is null or r.last_heartbeat_time &lt; #{heartbeatCutoff})
<if test="excludedRobotIds != null and excludedRobotIds.size() > 0">
and lower(r.robot_id) not in
<foreach collection="excludedRobotIds" item="robotId" open="(" separator="," close=")">
#{robotId}
</foreach>
</if>
</select>
<update id="markHeartbeatExpiredOffline">
update inspection_robot
set connect_status = '0', status = '1',
update_by = 'heartbeat-timeout', update_time = #{offlineTime}
where id = #{id}
and connect_status = '1'
and (last_heartbeat_time is null or last_heartbeat_time &lt; #{heartbeatCutoff})
</update>
<update id="archiveInspectionRobotByIds" parameterType="String">
update inspection_robot set archived_status = '1', connect_status = '0',
update_time = sysdate(), update_by = #{updateBy}
where id in
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<delete id="deleteInspectionRobotById" parameterType="String">
delete from inspection_robot where id = #{id}
</delete>
<delete id="deleteInspectionRobotByIds" parameterType="String">
delete from inspection_robot where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<select id="selectInspectionRobotVoList" parameterType="InspectionRobot" resultMap="InspectionRobotVoResult">
select r.id, r.create_by, r.create_time, r.update_by, r.update_time, r.remark,
r.robot_id, r.robot_code, r.robot_name, r.robot_model, r.robot_type,
r.ip_address, r.port, r.current_map_id,
m.map_name as current_map_name,
r.status, r.battery_level, r.current_position,
r.connect_status, r.quic_node_id, r.quic_session_id, r.quic_boot_id,
r.observed_ip, r.software_version, r.last_heartbeat_time,
(select count(*) from inspection_robot_device d where d.robot_id = r.robot_id) as device_total,
(select count(*) from inspection_robot_device d where d.robot_id = r.robot_id and d.online_status = '1') as online_device_count,
(select count(*) from inspection_robot_device d where d.robot_id = r.robot_id and (d.health_status = 3 or d.has_error = '1')) as fault_device_count,
u1.nick_name as create_by_name, u2.nick_name as update_by_name, m.map_source_name as robot_map_name
from inspection_robot r
left join inspection_map m on r.current_map_id = m.id
left join sys_user u1 on r.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on r.update_by = u2.user_name <!-- 更新人关联用户 -->
<where>
and coalesce(r.archived_status, '0') = '0'
<if test="robotId != null and robotId != ''"> and r.robot_id = #{robotId}</if>
<if test="robotCode != null and robotCode != ''"> and r.robot_code = #{robotCode}</if>
<if test="robotName != null and robotName != ''"> and r.robot_name like concat('%', #{robotName}, '%')</if>
<if test="robotModel != null and robotModel != ''"> and r.robot_model = #{robotModel}</if>
<if test="robotType != null and robotType != ''"> and r.robot_type = #{robotType}</if>
<if test="connectStatus != null and connectStatus != ''"> and r.connect_status = #{connectStatus}</if>
<if test="status != null and status != ''"> and r.status = #{status}</if>
<if test="currentMapId != null and currentMapId != ''"> and r.current_map_id = #{currentMapId}</if>
<if test="ipAddress != null and ipAddress != ''"> and r.ip_address = #{ipAddress}</if>
</where>
</select>
</mapper>

View File

@ -131,13 +131,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select ti.id, ti.create_by, ti.create_time, ti.update_by, ti.update_time, ti.remark,ti.waypoint_count, select ti.id, ti.create_by, ti.create_time, ti.update_by, ti.update_time, ti.remark,ti.waypoint_count,
ti.task_id, t.task_name, t.task_code, ti.task_id, t.task_name, t.task_code,
ti.robot_id, r.robot_name, r.robot_id as robot_identity, ti.robot_id, r.robot_name, r.robot_id as robot_identity,
r.current_map_id as robot_current_map_id, m.map_name, r.active_map_id as robot_current_map_id, m.map_name,
ti.status, ti.start_time, ti.end_time, ti.task_ins_id, ti.status, ti.start_time, ti.end_time, ti.task_ins_id,
u1.nick_name as create_by_name, u2.nick_name as update_by_name u1.nick_name as create_by_name, u2.nick_name as update_by_name
from inspection_task_instance ti from inspection_task_instance ti
left join inspection_task t on ti.task_id = t.id left join inspection_task t on ti.task_id = t.id
left join inspection_robot r on ti.robot_id = r.id left join iot_robot r on ti.robot_id = r.id
left join inspection_map m on r.current_map_id = m.id left join iot_map m on r.active_map_id = m.id
left join sys_user u1 on ti.create_by = u1.user_name left join sys_user u1 on ti.create_by = u1.user_name
left join sys_user u2 on ti.update_by = u2.user_name left join sys_user u2 on ti.update_by = u2.user_name
<where> <where>
@ -170,12 +170,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select ti.id, ti.create_by, ti.create_time, ti.update_by, ti.update_time, ti.remark, select ti.id, ti.create_by, ti.create_time, ti.update_by, ti.update_time, ti.remark,
ti.task_id, t.task_name, t.task_code, ti.task_id, t.task_name, t.task_code,
ti.robot_id, r.robot_name, r.robot_id as robot_identity, ti.robot_id, r.robot_name, r.robot_id as robot_identity,
r.current_map_id as robot_current_map_id, m.map_name, r.active_map_id as robot_current_map_id, m.map_name,
ti.status, ti.start_time, ti.end_time, ti.task_ins_id ti.status, ti.start_time, ti.end_time, ti.task_ins_id
from inspection_task_instance ti from inspection_task_instance ti
left join inspection_task t on ti.task_id = t.id left join inspection_task t on ti.task_id = t.id
left join inspection_robot r on ti.robot_id = r.id left join iot_robot r on ti.robot_id = r.id
left join inspection_map m on r.current_map_id = m.id left join iot_map m on r.active_map_id = m.id
where ti.id = #{id} where ti.id = #{id}
</select> </select>
</mapper> </mapper>

View File

@ -123,7 +123,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join sys_user u1 on t.create_by = u1.user_name <!-- 创建人关联用户 --> left join sys_user u1 on t.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on t.update_by = u2.user_name <!-- 更新人关联用户 --> left join sys_user u2 on t.update_by = u2.user_name <!-- 更新人关联用户 -->
<!-- 任务关联地图 --> <!-- 任务关联地图 -->
left join inspection_map m on t.map_id = m.id left join iot_map m on t.map_id = m.id
<where> <where>
<if test="taskCode != null and taskCode != ''"> and t.task_code = #{taskCode}</if> <if test="taskCode != null and taskCode != ''"> and t.task_code = #{taskCode}</if>
<if test="taskName != null and taskName != ''"> and t.task_name like concat('%', #{taskName}, '%')</if> <if test="taskName != null and taskName != ''"> and t.task_name like concat('%', #{taskName}, '%')</if>

View File

@ -110,7 +110,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
u1.nick_name as create_by_name, u2.nick_name as update_by_name u1.nick_name as create_by_name, u2.nick_name as update_by_name
from inspection_waypoint w from inspection_waypoint w
left join te_detection_item d on w.detect_item_id = d.id left join te_detection_item d on w.detect_item_id = d.id
left join inspection_map m on w.map_id = m.id left join iot_map m on w.map_id = m.id
left join sys_user u1 on w.create_by = u1.user_name <!-- 创建人关联用户 --> left join sys_user u1 on w.create_by = u1.user_name <!-- 创建人关联用户 -->
left join sys_user u2 on w.update_by = u2.user_name <!-- 更新人关联用户 --> left join sys_user u2 on w.update_by = u2.user_name <!-- 更新人关联用户 -->
<where> <where>

View File

@ -15,7 +15,7 @@ cmvr-iot-admin
|-- NodeEventBroker |-- NodeEventBroker
`-- RobotQuicEventSubscriber(进程内直接订阅) `-- RobotQuicEventSubscriber(进程内直接订阅)
v v
inspection_robot + inspection_robot_device + 原有机器人 gRPC 客户端 iot_robot + iot_device + 原有机器人 gRPC 客户端
``` ```
QUIC 链路不启动内部 gRPC Server,也不经过本机 TCP 端口。QUIC 注册中的永久 `robotId` 是机器人唯一业务身份;平台据此同步机器人 gRPC endpoint、设备快照,并在地址变化时清理对应 channel。 QUIC 链路不启动内部 gRPC Server,也不经过本机 TCP 端口。QUIC 注册中的永久 `robotId` 是机器人唯一业务身份;平台据此同步机器人 gRPC endpoint、设备快照,并在地址变化时清理对应 channel。
@ -80,14 +80,13 @@ Linux 部署时将证书路径换成实际绝对路径。对应配置也可以
首次使用前执行: 首次使用前执行:
```text ```text
sql/robot_quic_identity_migration.sql sql/resource_center_v1.sql
sql/robot_id_only_migration.sql
``` ```
先在机器人管理页面新增机器人并填写边缘端上报的永久 `robotId`。机器人首次连接后,平台自动补全地址、端口、QUIC 会话信息和设备清单,无需绑定终端: 先在机器人管理页面新增机器人并填写边缘端上报的永久 `robotId`。机器人首次连接后,平台自动补全地址、端口、QUIC 会话信息和设备清单,无需绑定终端:
```sql ```sql
INSERT INTO inspection_robot (id, robot_name, robot_id, connect_status, archived_status) INSERT INTO iot_robot (id, robot_name, robot_id, connect_status, archived_status)
VALUES ('业务主键', '测试机器人', '边缘端永久机器人ID', '0', '0'); VALUES ('业务主键', '测试机器人', '边缘端永久机器人ID', '0', '0');
``` ```

33
cmvr-iot-resource/pom.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot</artifactId>
<version>3.9.2</version>
</parent>
<artifactId>cmvr-iot-resource</artifactId>
<description>Platform robot, device, terminal and map resources</description>
<dependencies>
<dependency>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-common</artifactId>
</dependency>
<dependency>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-test</artifactId>
</dependency>
<dependency>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-grpc-client</artifactId>
</dependency>
<dependency>
<groupId>com.cmvr</groupId>
<artifactId>cmvr-iot-quic-contract</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,4 +1,4 @@
package com.cmvr.inspection.config; package com.cmvr.resource.config;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ -10,9 +10,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
@Component @Component
@ConfigurationProperties(prefix = "cmvr.inspection.robot-state") @ConfigurationProperties(prefix = "cmvr.resource.robot-state")
public class RobotStateProperties { public class ResourceRobotStateProperties {
private long heartbeatTimeoutMs = 15_000L; private long heartbeatTimeoutMs = 15_000L;
private List<String> virtualRobotIds = new ArrayList<>(); private List<String> virtualRobotIds = new ArrayList<>();
@ -33,8 +32,7 @@ public class RobotStateProperties {
} }
public Date heartbeatCutoff(Date now) { public Date heartbeatCutoff(Date now) {
long timeout = Math.max(1_000L, heartbeatTimeoutMs); return new Date(now.getTime() - Math.max(1_000L, heartbeatTimeoutMs));
return new Date(now.getTime() - timeout);
} }
public boolean isVirtualRobot(String robotId) { public boolean isVirtualRobot(String robotId) {
@ -44,7 +42,7 @@ public class RobotStateProperties {
public List<String> normalizedVirtualRobotIds() { public List<String> normalizedVirtualRobotIds() {
return virtualRobotIds.stream() return virtualRobotIds.stream()
.map(RobotStateProperties::normalize) .map(ResourceRobotStateProperties::normalize)
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)
.distinct() .distinct()
.toList(); .toList();

View File

@ -0,0 +1,51 @@
package com.cmvr.resource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cmvr.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/** Device discovered under a platform robot. Identity is robotId + deviceId. */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("iot_device")
@ApiModel(value = "ResourceDevice", description = "机器人自动发现并上报的设备资源")
public class ResourceDevice extends BaseEntity {
@TableId
@ApiModelProperty("设备数据主键")
private String id;
@ApiModelProperty("所属机器人的永久robotId")
private String robotId;
@ApiModelProperty("边缘端设备ID,与robotId共同确定设备身份")
private String deviceId;
@ApiModelProperty("设备显示名称")
private String deviceName;
@ApiModelProperty("设备类型编码")
private Integer deviceKind;
@com.baomidou.mybatisplus.annotation.TableField("device_type")
@ApiModelProperty("边缘端设备实现类型")
private String typeName;
@ApiModelProperty("业务用途,例如前摄像头、左机械臂")
private String deviceRole;
private Integer deviceIndex;
private String terminalId;
@ApiModelProperty("是否启用:1启用,0停用")
private String enabled;
private Integer managerState;
private Integer healthStatus;
@ApiModelProperty("在线状态:1在线,0离线")
private String onlineStatus;
@ApiModelProperty("是否存在故障:1是,0否")
private String hasError;
@ApiModelProperty("边缘端上报的故障信息")
private String errorMessage;
private Date statusUpdatedTime;
@ApiModelProperty("最后发现时间")
private Date lastSeenTime;
private Date offlineTime;
}

View File

@ -0,0 +1,43 @@
package com.cmvr.resource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cmvr.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("iot_map")
@ApiModel(value = "ResourceMap", description = "可供AGV机器人和导航任务使用的地图")
public class ResourceMap extends BaseEntity {
@TableId
private String id;
@ApiModelProperty("地图名称")
private String mapName;
@ApiModelProperty("地图版本")
private String mapVersion;
@ApiModelProperty("地图类型,例如2D")
private String mapType;
@ApiModelProperty("坐标系,例如map")
private String coordinateSystem;
@ApiModelProperty("来源类型:ROBOT、UPLOAD或OTHER")
private String sourceType;
@ApiModelProperty("来源机器人的永久robotId")
private String sourceRobotId;
@ApiModelProperty("机器人侧原地图名称")
private String sourceMapName;
@ApiModelProperty("地图文件地址")
private String mapFilePath;
@ApiModelProperty("状态:0启用,1停用")
private String status;
@ApiModelProperty("地图描述")
private String mapDesc;
@TableField(exist = false)
private String mapSourceRobotId;
@TableField(exist = false)
private String mapSourceName;
}

View File

@ -0,0 +1,63 @@
package com.cmvr.resource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cmvr.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/** Platform robot identity. robotId is the identity reported by QUIC. */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("iot_robot")
@ApiModel(value = "ResourceRobot", description = "平台统一管理的机器人身份和运行状态")
public class ResourceRobot extends BaseEntity {
@TableId
private String id;
@ApiModelProperty("永久机器人ID,由QUIC上报且创建后不可修改")
private String robotId;
@ApiModelProperty("机器人业务编号")
private String robotCode;
@ApiModelProperty("机器人名称")
private String robotName;
@ApiModelProperty("机器人型号")
private String robotModel;
@ApiModelProperty("机器人类型")
private String robotType;
@ApiModelProperty("当前gRPC地址")
private String ipAddress;
@ApiModelProperty("当前gRPC端口")
private Long port;
@TableField("active_map_id")
@ApiModelProperty("当前地图ID,仅带AGV设备的机器人需要")
private String currentMapId;
@TableField("runtime_status")
@ApiModelProperty("机器人运行状态")
private String status;
@ApiModelProperty("电量百分比")
private Integer batteryLevel;
private String currentPosition;
@ApiModelProperty("连接状态:1在线,0离线")
private String connectStatus;
private String quicNodeId;
private String quicSessionId;
private String quicBootId;
private String observedIp;
private String softwareVersion;
@ApiModelProperty("最后心跳时间")
private Date lastHeartbeatTime;
private String archivedStatus;
@TableField(exist = false)
private String currentMapName;
@TableField(exist = false)
private Integer deviceTotal;
@TableField(exist = false)
private Integer onlineDeviceCount;
@TableField(exist = false)
private Integer faultDeviceCount;
}

View File

@ -0,0 +1,24 @@
package com.cmvr.resource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cmvr.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("iot_robot_map")
public class ResourceRobotMap extends BaseEntity {
@TableId
private String id;
private String robotId;
private String mapId;
private String enabled;
private String isDefault;
private String loadedStatus;
private String compatibilityStatus;
private Date lastLoadedTime;
}

View File

@ -0,0 +1,21 @@
package com.cmvr.resource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("iot_robot_terminal")
public class ResourceRobotTerminal {
@TableId
private String id;
private String robotId;
private String terminalId;
private String active;
private Date bindTime;
private Date unbindTime;
private Date createTime;
private Date updateTime;
}

View File

@ -0,0 +1,41 @@
package com.cmvr.resource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cmvr.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("iot_terminal")
@ApiModel(value = "ResourceTerminal", description = "通过QUIC接入平台的边缘运行终端")
public class ResourceTerminal extends BaseEntity {
@TableId
private String id;
@ApiModelProperty("终端编码,对应QUIC节点身份")
private String terminalCode;
@ApiModelProperty("终端显示名称")
private String terminalName;
@ApiModelProperty("终端网络地址")
private String ipAddress;
@ApiModelProperty("终端gRPC端口")
private Long port;
@ApiModelProperty("边缘软件版本")
private String softwareVersion;
@ApiModelProperty("连接状态:1在线,0离线")
private String connectStatus;
@ApiModelProperty("最后心跳时间")
private Date lastHeartbeatTime;
@ApiModelProperty("终端状态")
private String status;
@TableField(exist = false)
private String robotIdsCsv;
@TableField(exist = false)
private Integer robotCount;
}

View File

@ -0,0 +1,28 @@
package com.cmvr.resource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.resource.domain.ResourceDevice;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Date;
public interface ResourceDeviceMapper extends BaseMapper<ResourceDevice> {
List<ResourceDevice> selectResourceDevices(ResourceDevice query,
@Param("onlineOnly") boolean onlineOnly);
List<ResourceDevice> selectByRobotId(@Param("robotId") String robotId,
@Param("deviceKind") Integer deviceKind,
@Param("onlineOnly") boolean onlineOnly);
List<ResourceDevice> selectByIdentity(@Param("robotId") String robotId,
@Param("deviceId") String deviceId);
int updateRuntimeState(ResourceDevice device);
int markMissingOffline(@Param("robotId") String robotId,
@Param("deviceIds") List<String> deviceIds,
@Param("offlineTime") Date offlineTime);
int markAllOffline(@Param("robotId") String robotId, @Param("offlineTime") Date offlineTime);
}

View File

@ -0,0 +1,12 @@
package com.cmvr.resource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.resource.domain.ResourceMap;
import java.util.List;
public interface ResourceMapMapper extends BaseMapper<ResourceMap> {
List<ResourceMap> selectResourceMaps(ResourceMap query);
List<ResourceMap> selectByRobotId(String robotId);
}

View File

@ -0,0 +1,14 @@
package com.cmvr.resource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.resource.domain.ResourceRobotMap;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ResourceRobotMapMapper extends BaseMapper<ResourceRobotMap> {
int clearDefault(@Param("robotId") String robotId, @Param("exceptMapId") String exceptMapId);
int deleteBinding(@Param("robotId") String robotId, @Param("mapId") String mapId);
int countByMapIds(@Param("mapIds") List<String> mapIds);
}

View File

@ -0,0 +1,37 @@
package com.cmvr.resource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.resource.domain.ResourceRobot;
import java.util.List;
import java.util.Date;
import org.apache.ibatis.annotations.Param;
public interface ResourceRobotMapper extends BaseMapper<ResourceRobot> {
List<ResourceRobot> selectResourceRobots(ResourceRobot query);
ResourceRobot selectByRobotIdentity(String robotId);
ResourceRobot selectById(String id);
int archiveByIds(@Param("ids") String[] ids, @Param("updateBy") String updateBy);
int clearActiveMap(@Param("id") String id, @Param("mapId") String mapId,
@Param("updateBy") String updateBy);
int updateQuicRuntimeState(ResourceRobot robot);
List<ResourceRobot> selectStaleConnected(@Param("heartbeatCutoff") Date heartbeatCutoff,
@Param("excludedRobotIds") List<String> excludedRobotIds);
List<ResourceRobot> selectRuntimeSyncCandidates(@Param("heartbeatCutoff") Date heartbeatCutoff,
@Param("excludedRobotIds") List<String> excludedRobotIds);
int updateAgvRuntimeStatus(ResourceRobot robot);
List<String> lockByRobotIds(@Param("robotIds") List<String> robotIds);
int markHeartbeatExpiredOffline(@Param("id") String id,
@Param("heartbeatCutoff") Date heartbeatCutoff,
@Param("offlineTime") Date offlineTime);
}

View File

@ -0,0 +1,19 @@
package com.cmvr.resource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.resource.domain.ResourceRobotTerminal;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ResourceRobotTerminalMapper extends BaseMapper<ResourceRobotTerminal> {
int deactivateOtherBindings(@Param("robotId") String robotId, @Param("terminalId") String terminalId);
List<String> selectActiveRobotIds(@Param("terminalId") String terminalId);
int deactivateTerminalBindings(@Param("terminalId") String terminalId);
int deleteTerminalBindings(@Param("terminalId") String terminalId);
int activateBinding(@Param("id") String id, @Param("robotId") String robotId,
@Param("terminalId") String terminalId);
}

View File

@ -0,0 +1,12 @@
package com.cmvr.resource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cmvr.resource.domain.ResourceTerminal;
import java.util.List;
public interface ResourceTerminalMapper extends BaseMapper<ResourceTerminal> {
List<ResourceTerminal> selectResourceTerminals(ResourceTerminal query);
ResourceTerminal selectByCode(String terminalCode);
}

View File

@ -0,0 +1,44 @@
package com.cmvr.resource.service;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.common.utils.SecurityUtils;
import com.cmvr.resource.domain.ResourceDevice;
import com.cmvr.resource.mapper.ResourceDeviceMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class ResourceDeviceService {
private final ResourceDeviceMapper deviceMapper;
public ResourceDeviceService(ResourceDeviceMapper deviceMapper) {
this.deviceMapper = deviceMapper;
}
public List<ResourceDevice> list(ResourceDevice query, boolean onlineOnly) {
return deviceMapper.selectResourceDevices(query, onlineOnly);
}
public ResourceDevice get(String id) {
return StringUtils.hasText(id) ? deviceMapper.selectById(id.trim()) : null;
}
public int updateBusinessConfig(ResourceDevice input) {
ResourceDevice existing = get(input.getId());
if (existing == null) {
throw new GlobalException("设备不存在");
}
ResourceDevice update = new ResourceDevice();
update.setId(existing.getId());
update.setDeviceName(input.getDeviceName());
update.setDeviceRole(input.getDeviceRole());
update.setEnabled("0".equals(input.getEnabled()) ? "0" : "1");
update.setRemark(input.getRemark());
update.setUpdateBy(SecurityUtils.getUsername());
update.setUpdateTime(DateUtils.getNowDate());
return deviceMapper.updateById(update);
}
}

View File

@ -0,0 +1,69 @@
package com.cmvr.resource.service;
import com.cmvr.resource.domain.ResourceMap;
import com.cmvr.resource.mapper.ResourceMapMapper;
import com.cmvr.resource.mapper.ResourceRobotMapMapper;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.common.utils.SecurityUtils;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@Service
public class ResourceMapService {
private final ResourceMapMapper mapMapper;
private final ResourceRobotMapMapper robotMapMapper;
public ResourceMapService(ResourceMapMapper mapMapper, ResourceRobotMapMapper robotMapMapper) {
this.mapMapper = mapMapper;
this.robotMapMapper = robotMapMapper;
}
public List<ResourceMap> list(ResourceMap query) {
return mapMapper.selectResourceMaps(query);
}
public List<ResourceMap> listByRobotId(String robotId) {
return StringUtils.hasText(robotId)
? mapMapper.selectByRobotId(robotId.trim())
: Collections.emptyList();
}
public ResourceMap get(String id) {
return mapMapper.selectById(id);
}
public int create(ResourceMap map) {
if (!StringUtils.hasText(map.getMapName())) {
throw new GlobalException("地图名称不能为空");
}
map.setId(UUID.randomUUID().toString().replace("-", ""));
map.setStatus(StringUtils.hasText(map.getStatus()) ? map.getStatus() : "0");
map.setCreateBy(SecurityUtils.getUsername());
map.setCreateTime(DateUtils.getNowDate());
return mapMapper.insert(map);
}
public int update(ResourceMap map) {
if (!StringUtils.hasText(map.getId()) || mapMapper.selectById(map.getId()) == null) {
throw new GlobalException("地图不存在");
}
map.setUpdateBy(SecurityUtils.getUsername());
map.setUpdateTime(DateUtils.getNowDate());
return mapMapper.updateById(map);
}
@Transactional(rollbackFor = Exception.class)
public int remove(List<String> ids) {
if (ids == null || ids.isEmpty()) return 0;
if (robotMapMapper.countByMapIds(ids) > 0) {
throw new GlobalException("地图仍被机器人绑定,请先解除机器人地图关系");
}
return mapMapper.deleteByIds(ids);
}
}

View File

@ -1,4 +1,4 @@
package com.cmvr.inspection.service; package com.cmvr.resource.service;
import com.cmvr.quic.contract.QuicEventBus; import com.cmvr.quic.contract.QuicEventBus;
import com.cmvr.quic.gateway.v1.NodeEvent; import com.cmvr.quic.gateway.v1.NodeEvent;
@ -17,12 +17,11 @@ import java.util.concurrent.TimeUnit;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class RobotQuicEventSubscriber { public class ResourceQuicEventSubscriber {
private static final Logger log = LoggerFactory.getLogger(ResourceQuicEventSubscriber.class);
private static final Logger log = LoggerFactory.getLogger(RobotQuicEventSubscriber.class);
private final QuicEventBus eventBus; private final QuicEventBus eventBus;
private final RobotQuicStateService stateService; private final ResourceQuicStateSynchronizer synchronizer;
@Value("${cmvr.quic.enabled:false}") @Value("${cmvr.quic.enabled:false}")
private boolean enabled; private boolean enabled;
@ -36,27 +35,26 @@ public class RobotQuicEventSubscriber {
return; return;
} }
executor = Executors.newSingleThreadExecutor(runnable -> { executor = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = new Thread(runnable, "quic-robot-sync"); Thread thread = new Thread(runnable, "quic-resource-sync");
thread.setDaemon(true); thread.setDaemon(true);
return thread; return thread;
}); });
subscription = eventBus.watchNodes(true, this::enqueue); subscription = eventBus.watchNodes(true, this::enqueue);
log.info("已启动QUIC机器人身份和设备同步"); log.info("QUIC resource synchronization started");
} }
private void enqueue(NodeEvent event) { private void enqueue(NodeEvent event) {
try { try {
executor.execute(() -> { executor.execute(() -> {
try { try {
stateService.synchronize(event); synchronizer.synchronize(event);
} catch (RuntimeException error) { } catch (RuntimeException error) {
log.error("同步QUIC机器人失败,robotId={},nodeId={},eventType={}", log.error("Failed to synchronize QUIC resource, robotId={}, nodeId={}, eventType={}",
event.getNode().getRobotId(), event.getNode().getNodeId(), event.getNode().getRobotId(), event.getNode().getNodeId(), event.getType(), error);
event.getType(), error);
} }
}); });
} catch (RejectedExecutionException ignored) { } catch (RejectedExecutionException ignored) {
log.debug("QUIC机器人同步线程已停止"); log.debug("QUIC resource synchronization executor is stopped");
} }
} }
@ -65,7 +63,9 @@ public class RobotQuicEventSubscriber {
if (subscription != null) { if (subscription != null) {
subscription.close(); subscription.close();
} }
if (executor != null) { if (executor == null) {
return;
}
executor.shutdown(); executor.shutdown();
try { try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
@ -76,5 +76,4 @@ public class RobotQuicEventSubscriber {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
} }
}
} }

View File

@ -1,16 +1,21 @@
package com.cmvr.inspection.service; package com.cmvr.resource.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmvr.common.utils.DateUtils; import com.cmvr.common.utils.DateUtils;
import com.cmvr.edge.client.manage.GrpcServiceManager; import com.cmvr.edge.client.manage.GrpcServiceManager;
import com.cmvr.inspection.config.RobotStateProperties;
import com.cmvr.inspection.domain.InspectionRobot;
import com.cmvr.inspection.domain.InspectionRobotDevice;
import com.cmvr.inspection.mapper.InspectionRobotDeviceMapper;
import com.cmvr.inspection.mapper.InspectionRobotMapper;
import com.cmvr.quic.gateway.v1.ManagedDeviceStatus; import com.cmvr.quic.gateway.v1.ManagedDeviceStatus;
import com.cmvr.quic.gateway.v1.NodeEvent; import com.cmvr.quic.gateway.v1.NodeEvent;
import com.cmvr.quic.gateway.v1.NodeEventType; import com.cmvr.quic.gateway.v1.NodeEventType;
import com.cmvr.quic.gateway.v1.NodeSnapshot; import com.cmvr.quic.gateway.v1.NodeSnapshot;
import com.cmvr.resource.config.ResourceRobotStateProperties;
import com.cmvr.resource.domain.ResourceDevice;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.domain.ResourceRobotTerminal;
import com.cmvr.resource.domain.ResourceTerminal;
import com.cmvr.resource.mapper.ResourceDeviceMapper;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import com.cmvr.resource.mapper.ResourceRobotTerminalMapper;
import com.cmvr.resource.mapper.ResourceTerminalMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -23,72 +28,53 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class RobotQuicStateService { public class ResourceQuicStateSynchronizer {
private static final Logger log = LoggerFactory.getLogger(ResourceQuicStateSynchronizer.class);
private static final Logger log = LoggerFactory.getLogger(RobotQuicStateService.class);
private static final int AGV_DEVICE_KIND = 1; private static final int AGV_DEVICE_KIND = 1;
private final InspectionRobotMapper robotMapper; private final ResourceRobotMapper robotMapper;
private final InspectionRobotDeviceMapper deviceMapper; private final ResourceDeviceMapper deviceMapper;
private final ResourceTerminalMapper terminalMapper;
private final ResourceRobotTerminalMapper robotTerminalMapper;
private final ResourceRobotStateProperties stateProperties;
private final GrpcServiceManager grpcServiceManager; private final GrpcServiceManager grpcServiceManager;
private final RobotStateProperties robotStateProperties;
@Value("${cmvr.quic.grpc-host-source:observed-source}") @Value("${cmvr.quic.grpc-host-source:observed-source}")
private String grpcHostSource; private String grpcHostSource;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void synchronize(NodeEvent event) { public boolean synchronize(NodeEvent event) {
NodeSnapshot node = event.getNode(); NodeSnapshot node = event.getNode();
String robotId = StringUtils.trimToNull(node.getRobotId()); String robotId = StringUtils.trimToNull(node.getRobotId());
if (robotId == null) { if (robotId == null || stateProperties.isVirtualRobot(robotId)) {
log.warn("忽略未携带机器人ID的QUIC事件,nodeId={},eventType={}", return false;
node.getNodeId(), event.getType());
return;
} }
if (robotStateProperties.isVirtualRobot(robotId)) { ResourceRobot robot = robotMapper.selectByRobotIdentity(robotId);
log.debug("Ignoring QUIC state for virtual robot, robotId={}, eventType={}", robotId, event.getType()); if (robot == null || "1".equals(robot.getArchivedStatus())) {
return; log.warn("QUIC robot is not registered in resource center, robotId={}, nodeId={}",
} robotId, node.getNodeId());
return false;
List<InspectionRobot> matches = robotMapper.selectByRobotId(robotId);
if (matches.isEmpty()) {
log.warn("QUIC机器人尚未在平台登记,robotId={},nodeId={}", robotId, node.getNodeId());
return;
}
if (matches.size() != 1) {
log.error("机器人ID存在{}条重复记录,拒绝同步,robotId={}", matches.size(), robotId);
return;
}
InspectionRobot robot = matches.get(0);
if ("1".equals(robot.getArchivedStatus())) {
log.warn("已归档机器人尝试连接,拒绝同步,robotId={}", robotId);
return;
} }
boolean offline = event.getType() == NodeEventType.NODE_EVENT_TYPE_OFFLINE || !node.getOnline(); boolean offline = event.getType() == NodeEventType.NODE_EVENT_TYPE_OFFLINE || !node.getOnline();
if (offline && StringUtils.isNotBlank(robot.getQuicSessionId()) if (offline && StringUtils.isNotBlank(robot.getQuicSessionId())
&& !robot.getQuicSessionId().equals(node.getSessionId())) { && !robot.getQuicSessionId().equals(node.getSessionId())) {
log.info("忽略旧QUIC会话的离线事件,robotId={},activeSession={},eventSession={}", return true;
robotId, robot.getQuicSessionId(), node.getSessionId());
return;
} }
Date eventTime = new Date(event.getEventTimeUnixMs() == 0
? System.currentTimeMillis() : event.getEventTimeUnixMs());
String oldHost = robot.getIpAddress(); String oldHost = robot.getIpAddress();
Long oldPort = robot.getPort(); Long oldPort = robot.getPort();
Date eventTime = new Date(event.getEventTimeUnixMs() == 0
? System.currentTimeMillis() : event.getEventTimeUnixMs());
if (!offline) { if (!offline) {
String host = resolveGrpcHost(node); robot.setIpAddress(resolveGrpcHost(node));
Long port = node.getGrpcEndpoint().getPort() == 0 robot.setPort(node.getGrpcEndpoint().getPort() == 0
? null : Long.valueOf(node.getGrpcEndpoint().getPort()); ? null : (long) node.getGrpcEndpoint().getPort());
robot.setIpAddress(host);
robot.setPort(port);
} }
robot.setConnectStatus(offline ? "0" : "1"); robot.setConnectStatus(offline ? "0" : "1");
robot.setQuicNodeId(node.getNodeId()); robot.setQuicNodeId(node.getNodeId());
robot.setQuicSessionId(node.getSessionId()); robot.setQuicSessionId(node.getSessionId());
@ -99,50 +85,40 @@ public class RobotQuicStateService {
|| event.getType() == NodeEventType.NODE_EVENT_TYPE_HEARTBEAT) { || event.getType() == NodeEventType.NODE_EVENT_TYPE_HEARTBEAT) {
robot.setLastHeartbeatTime(eventTime); robot.setLastHeartbeatTime(eventTime);
} }
robot.setUpdateTime(DateUtils.getNowDate());
if (offline || !hasEnabledAgv(node)) { if (offline || !hasEnabledAgv(node)) {
robot.setStatus("1"); robot.setStatus("1");
} }
robot.setUpdateTime(DateUtils.getNowDate());
robotMapper.updateQuicRuntimeState(robot); robotMapper.updateQuicRuntimeState(robot);
synchronizeTerminal(robotId, node, eventTime, offline);
if (offline) { if (offline) {
deviceMapper.markAllOffline(robotId, eventTime); deviceMapper.markAllOffline(robotId, eventTime);
} else { } else {
synchronizeDevices(robotId, node, eventTime); synchronizeDevices(robotId, node, eventTime);
} }
if (offline || !Objects.equals(oldHost, robot.getIpAddress()) if (offline || !Objects.equals(oldHost, robot.getIpAddress())
|| !Objects.equals(oldPort, robot.getPort())) { || !Objects.equals(oldPort, robot.getPort())) {
grpcServiceManager.invalidateRobot(robotId); grpcServiceManager.invalidateRobot(robotId);
} }
if (event.getType() == NodeEventType.NODE_EVENT_TYPE_HEARTBEAT) { return true;
log.debug("QUIC机器人心跳已同步,robotId={},nodeId={},grpc={}:{},devices={}",
robotId, node.getNodeId(), robot.getIpAddress(), robot.getPort(),
node.getDeviceManager().getDevicesCount());
} else {
log.info("QUIC机器人状态已同步,robotId={},nodeId={},online={},grpc={}:{},devices={}",
robotId, node.getNodeId(), !offline, robot.getIpAddress(), robot.getPort(),
node.getDeviceManager().getDevicesCount());
}
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int expireStaleRobots() { public int expireStaleRobots() {
Date now = DateUtils.getNowDate(); Date now = DateUtils.getNowDate();
Date cutoff = robotStateProperties.heartbeatCutoff(now); Date cutoff = stateProperties.heartbeatCutoff(now);
List<String> excludedRobotIds = robotStateProperties.normalizedVirtualRobotIds(); int count = 0;
int expired = 0; for (ResourceRobot robot : robotMapper.selectStaleConnected(
for (InspectionRobot robot : robotMapper.selectStaleConnectedRobots(cutoff, excludedRobotIds)) { cutoff, stateProperties.normalizedVirtualRobotIds())) {
if (robotMapper.markHeartbeatExpiredOffline(robot.getId(), cutoff, now) == 0) { if (robotMapper.markHeartbeatExpiredOffline(robot.getId(), cutoff, now) == 0) {
continue; continue;
} }
deviceMapper.markAllOffline(robot.getRobotId(), now); deviceMapper.markAllOffline(robot.getRobotId(), now);
grpcServiceManager.invalidateRobot(robot.getRobotId()); grpcServiceManager.invalidateRobot(robot.getRobotId());
expired++; count++;
log.warn("Robot heartbeat expired and was marked offline, robotId={}, lastHeartbeatTime={}, timeoutMs={}",
robot.getRobotId(), robot.getLastHeartbeatTime(), robotStateProperties.getHeartbeatTimeoutMs());
} }
return expired; return count;
} }
private void synchronizeDevices(String robotId, NodeSnapshot node, Date eventTime) { private void synchronizeDevices(String robotId, NodeSnapshot node, Date eventTime) {
@ -153,13 +129,12 @@ public class RobotQuicStateService {
continue; continue;
} }
reportedIds.add(deviceId); reportedIds.add(deviceId);
List<InspectionRobotDevice> matches = deviceMapper.selectByIdentity(robotId, deviceId); List<ResourceDevice> matches = deviceMapper.selectByIdentity(robotId, deviceId);
if (matches.size() > 1) { if (matches.size() > 1) {
log.error("机器人设备存在重复记录,跳过更新,robotId={},deviceId={},count={}", log.error("Duplicate resource device identity, robotId={}, deviceId={}", robotId, deviceId);
robotId, deviceId, matches.size());
continue; continue;
} }
InspectionRobotDevice device = matches.isEmpty() ResourceDevice device = matches.isEmpty()
? createDevice(robotId, deviceId, eventTime) : matches.get(0); ? createDevice(robotId, deviceId, eventTime) : matches.get(0);
device.setDeviceKind(status.getKind()); device.setDeviceKind(status.getKind());
device.setTypeName(status.getTypeName()); device.setTypeName(status.getTypeName());
@ -184,8 +159,9 @@ public class RobotQuicStateService {
deviceMapper.markMissingOffline(robotId, reportedIds, eventTime); deviceMapper.markMissingOffline(robotId, reportedIds, eventTime);
} }
private InspectionRobotDevice createDevice(String robotId, String deviceId, Date now) { private ResourceDevice createDevice(String robotId, String deviceId, Date now) {
InspectionRobotDevice device = new InspectionRobotDevice(); ResourceDevice device = new ResourceDevice();
device.setId(UUID.randomUUID().toString().replace("-", ""));
device.setRobotId(robotId); device.setRobotId(robotId);
device.setDeviceId(deviceId); device.setDeviceId(deviceId);
device.setDeviceName(deviceId); device.setDeviceName(deviceId);
@ -194,6 +170,60 @@ public class RobotQuicStateService {
return device; return device;
} }
private void synchronizeTerminal(String robotId, NodeSnapshot node, Date eventTime, boolean offline) {
String terminalCode = StringUtils.trimToNull(node.getNodeId());
if (terminalCode == null) {
return;
}
ResourceTerminal terminal = terminalMapper.selectByCode(terminalCode);
boolean newTerminal = terminal == null;
if (newTerminal) {
terminal = new ResourceTerminal();
terminal.setId(UUID.randomUUID().toString().replace("-", ""));
terminal.setTerminalCode(terminalCode);
terminal.setTerminalName(terminalCode);
terminal.setCreateBy("quic");
terminal.setCreateTime(eventTime);
}
terminal.setIpAddress(StringUtils.trimToNull(node.getObservedSourceIp()));
terminal.setPort(node.getGrpcEndpoint().getPort() == 0
? null : (long) node.getGrpcEndpoint().getPort());
terminal.setSoftwareVersion(node.getSoftwareVersion());
terminal.setConnectStatus(offline ? "0" : "1");
terminal.setLastHeartbeatTime(eventTime);
terminal.setStatus(offline ? "OFFLINE" : "ONLINE");
terminal.setUpdateBy("quic");
terminal.setUpdateTime(eventTime);
if (newTerminal) {
terminalMapper.insert(terminal);
} else {
terminalMapper.updateById(terminal);
}
ResourceRobotTerminal binding = robotTerminalMapper.selectOne(
new LambdaQueryWrapper<ResourceRobotTerminal>()
.eq(ResourceRobotTerminal::getRobotId, robotId)
.eq(ResourceRobotTerminal::getTerminalId, terminal.getId()));
if (binding == null) {
binding = new ResourceRobotTerminal();
binding.setId(UUID.randomUUID().toString().replace("-", ""));
binding.setRobotId(robotId);
binding.setTerminalId(terminal.getId());
binding.setBindTime(eventTime);
binding.setCreateTime(eventTime);
binding.setActive(offline ? "0" : "1");
robotTerminalMapper.insert(binding);
} else {
binding.setActive(offline ? "0" : "1");
binding.setUnbindTime(offline ? eventTime : null);
binding.setUpdateTime(eventTime);
robotTerminalMapper.updateById(binding);
}
if (!offline) {
robotTerminalMapper.deactivateOtherBindings(robotId, terminal.getId());
}
}
private boolean hasEnabledAgv(NodeSnapshot node) { private boolean hasEnabledAgv(NodeSnapshot node) {
return node.getDeviceManager().getDevicesList().stream() return node.getDeviceManager().getDevicesList().stream()
.anyMatch(device -> device.getKind() == AGV_DEVICE_KIND && device.getEnabled()); .anyMatch(device -> device.getKind() == AGV_DEVICE_KIND && device.getEnabled());

View File

@ -0,0 +1,174 @@
package com.cmvr.resource.service;
import cmvr.msgs.AgvUtils;
import cmvr.quic_edge.v1.QuicEdge;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeAgvService;
import com.cmvr.resource.domain.ResourceDevice;
import com.cmvr.resource.domain.ResourceMap;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.mapper.ResourceDeviceMapper;
import com.cmvr.resource.mapper.ResourceMapMapper;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ResourceRobotControlService {
private final ResourceRobotMapper robotMapper;
private final ResourceDeviceMapper deviceMapper;
private final ResourceMapMapper mapMapper;
private final ResourceRobotMapService robotMapService;
private final EdgeAgvService edgeAgvService;
public List<String> listRemoteMaps(String robotRef) {
return edgeAgvService.listMaps(buildAgvRequest(requireRobot(robotRef)));
}
public int bindMap(String robotRef, String mapId) {
ResourceRobot robot = requireRobot(robotRef);
if (mapMapper.selectById(mapId) == null) {
throw new GlobalException("地图不存在:" + mapId);
}
return robotMapService.bind(robot.getRobotId(), mapId, true);
}
public String downloadMap(String robotRef, String mapName) {
return edgeAgvService.downloadMap(buildAgvRequest(requireRobot(robotRef)), mapName);
}
public String uploadActiveMap(String robotRef) {
ResourceRobot target = requireRobot(robotRef);
ResourceMap map = mapMapper.selectById(target.getCurrentMapId());
if (map == null) {
throw new GlobalException("机器人未绑定有效地图");
}
String sourceRobotId = StringUtils.trimToNull(map.getSourceRobotId());
String sourceMapName = StringUtils.trimToNull(map.getSourceMapName());
if (sourceRobotId == null || sourceMapName == null) {
throw new GlobalException("地图未配置来源机器人或来源地图名称");
}
ResourceRobot source = requireRobot(sourceRobotId);
String content = edgeAgvService.downloadMap(buildAgvRequest(source), sourceMapName);
if (StringUtils.isBlank(content)) {
throw new GlobalException("下载的地图内容为空");
}
edgeAgvService.uploadMap(buildAgvRequest(target), sourceMapName, content);
return "上传成功";
}
public ResourceRobot syncRuntimeState(String robotRef) {
ResourceRobot robot = requireRobot(robotRef);
try {
AgvUtils.AgvRuntimeState runtime = edgeAgvService.getRuntimeState(buildAgvRequest(robot));
if (runtime == null) {
throw new GlobalException("AGV未返回运行状态");
}
if (runtime.hasBattery()) {
int percentage = (int) Math.round(runtime.getBattery().getPercentage() * 100D);
robot.setBatteryLevel(Math.max(0, Math.min(100, percentage)));
}
if (runtime.hasPose()) {
AgvUtils.AgvPose2d pose = runtime.getPose();
robot.setCurrentPosition(String.format(Locale.ROOT, "%.3f,%.3f,%.3f",
pose.getX(), pose.getY(), pose.getTheta()));
}
robot.setStatus(String.valueOf(runtime.getMode()));
robotMapper.updateAgvRuntimeStatus(robot);
return robot;
} catch (GlobalException error) {
throw error;
} catch (Exception error) {
robot.setStatus("0");
robotMapper.updateAgvRuntimeStatus(robot);
String detail = StringUtils.isBlank(error.getMessage())
? error.getClass().getSimpleName() : error.getMessage();
throw new GlobalException("获取机器人运行状态失败:" + detail, error);
}
}
public AgvUtils.AgvPose2d getRealtimeLocation(String robotRef) {
AgvUtils.AgvRuntimeState runtime = edgeAgvService.getRuntimeState(
buildAgvRequest(requireRobot(robotRef)));
return runtime.hasPose() ? runtime.getPose() : null;
}
public void navigateToPosition(String robotRef, Double x, Double y, Double theta) {
if (x == null || y == null) {
throw new GlobalException("X和Y坐标不能为空");
}
AgvUtils.AgvPose2d pose = AgvUtils.AgvPose2d.newBuilder()
.setX(x)
.setY(y)
.setTheta(theta == null ? 0D : theta)
.build();
edgeAgvService.navigateToPose(buildAgvRequest(requireRobot(robotRef)), pose);
}
public String startMapping(String robotRef, int dimension, String mapName, boolean realTime) {
AgvUtils.AgvMapDimension mapDimension = AgvUtils.AgvMapDimension.forNumber(dimension);
if (mapDimension == null) {
throw new GlobalException("无效的建图维度:" + dimension);
}
return edgeAgvService.startMapping(
buildAgvRequest(requireRobot(robotRef)), mapDimension, mapName, realTime);
}
public void stopMapping(String robotRef) {
edgeAgvService.stopMapping(buildAgvRequest(requireRobot(robotRef)));
}
private ResourceRobot requireRobot(String robotRef) {
String normalized = StringUtils.trimToNull(robotRef);
if (normalized == null) {
throw new GlobalException("机器人标识不能为空");
}
ResourceRobot robot = robotMapper.selectById(normalized);
if (robot == null) {
robot = robotMapper.selectByRobotIdentity(normalized);
}
if (robot == null || "1".equals(robot.getArchivedStatus())) {
throw new GlobalException("机器人不存在:" + normalized);
}
return robot;
}
private EdgeCommonVO buildAgvRequest(ResourceRobot robot) {
List<ResourceDevice> agvDevices = deviceMapper.selectByRobotId(
robot.getRobotId(), QuicEdge.DeviceKind.DEVICE_KIND_AGV_VALUE, true);
if (agvDevices.isEmpty()) {
List<ResourceDevice> onlineDevices = deviceMapper.selectByRobotId(robot.getRobotId(), null, true);
if (onlineDevices.isEmpty()) {
throw new GlobalException("机器人当前没有在线设备:" + robot.getRobotId());
}
String summary = onlineDevices.stream()
.map(device -> device.getDeviceId() + "(" + deviceKindName(device.getDeviceKind()) + ")")
.collect(Collectors.joining("、"));
throw new GlobalException("机器人在线设备中没有AGV,当前设备:" + summary);
}
if (agvDevices.size() > 1) {
String ids = agvDevices.stream().map(ResourceDevice::getDeviceId)
.collect(Collectors.joining("、"));
throw new GlobalException("机器人存在多个在线AGV,无法确定默认设备:" + ids);
}
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_", "");
}
}

View File

@ -1,35 +1,43 @@
package com.cmvr.inspection.service; package com.cmvr.resource.service;
import com.cmvr.common.exception.GlobalException; import com.cmvr.common.exception.GlobalException;
import com.cmvr.inspection.mapper.InspectionRobotDeviceMapper; import com.cmvr.resource.config.ResourceRobotStateProperties;
import com.cmvr.inspection.config.RobotStateProperties; import com.cmvr.resource.domain.ResourceDevice;
import com.cmvr.resource.mapper.ResourceDeviceMapper;
import com.cmvr.test.flow.runtime.engine.RobotDeviceAssignmentValidator; import com.cmvr.test.flow.runtime.engine.RobotDeviceAssignmentValidator;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Locale; import java.util.Locale;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class InspectionRobotDeviceAssignmentValidator implements RobotDeviceAssignmentValidator { public class ResourceRobotDeviceAssignmentValidator implements RobotDeviceAssignmentValidator {
private static final int MICROPHONE_DEVICE_KIND = 9; private static final int MICROPHONE_DEVICE_KIND = 9;
private static final String RECORDING_STREAM_CONFLICT = private static final String RECORDING_STREAM_CONFLICT =
"cannot start streaming while microphone recording is active"; "cannot start streaming while microphone recording is active";
private final InspectionRobotDeviceMapper deviceMapper; private final ResourceDeviceMapper deviceMapper;
private final RobotStateProperties robotStateProperties; private final ResourceRobotStateProperties stateProperties;
@Override @Override
public void validate(String robotId, String deviceId, Integer expectedDeviceKind) { public void validate(String robotId, String deviceId, Integer expectedDeviceKind) {
if (robotStateProperties.isVirtualRobot(robotId)) { validateAndGet(robotId, deviceId, expectedDeviceKind);
}
private ResourceDevice validateAndGet(String robotId, String deviceId, Integer expectedDeviceKind) {
if (stateProperties.isVirtualRobot(robotId)) {
throw new GlobalException("虚拟机器人不能绑定边缘设备:" + robotId); throw new GlobalException("虚拟机器人不能绑定边缘设备:" + robotId);
} }
var devices = deviceMapper.selectByIdentity(robotId, deviceId); List<ResourceDevice> devices = deviceMapper.selectByIdentity(robotId, deviceId);
if (devices.isEmpty()) { if (devices.isEmpty()) {
throw new GlobalException("设备[" + deviceId + "]不属于机器人[" + robotId + "]"); throw new GlobalException("设备[" + deviceId + "]不属于机器人[" + robotId + "]");
} }
var device = devices.get(0); if (devices.size() > 1) {
throw new GlobalException("设备身份存在重复记录:" + robotId + "/" + deviceId);
}
ResourceDevice device = devices.get(0);
Integer actualKind = device.getDeviceKind(); Integer actualKind = device.getDeviceKind();
if (expectedDeviceKind != null && !expectedDeviceKind.equals(actualKind)) { if (expectedDeviceKind != null && !expectedDeviceKind.equals(actualKind)) {
throw new GlobalException("设备[" + deviceId + "]的类型与流程设备用途不匹配"); throw new GlobalException("设备[" + deviceId + "]的类型与流程设备用途不匹配");
@ -45,12 +53,12 @@ public class InspectionRobotDeviceAssignmentValidator implements RobotDeviceAssi
&& device.getErrorMessage().toLowerCase(Locale.ROOT).contains(RECORDING_STREAM_CONFLICT); && device.getErrorMessage().toLowerCase(Locale.ROOT).contains(RECORDING_STREAM_CONFLICT);
if ("1".equals(device.getHasError()) && !recoverableMicrophoneConflict) { if ("1".equals(device.getHasError()) && !recoverableMicrophoneConflict) {
String detail = device.getErrorMessage() == null || device.getErrorMessage().isBlank() String detail = device.getErrorMessage() == null || device.getErrorMessage().isBlank()
? "设备上报异常" ? "设备上报异常" : device.getErrorMessage();
: device.getErrorMessage();
throw new GlobalException("设备[" + deviceId + "]当前异常: " + detail); throw new GlobalException("设备[" + deviceId + "]当前异常: " + detail);
} }
if (Integer.valueOf(3).equals(device.getHealthStatus()) && !recoverableMicrophoneConflict) { if (Integer.valueOf(3).equals(device.getHealthStatus()) && !recoverableMicrophoneConflict) {
throw new GlobalException("设备[" + deviceId + "]当前处于故障状态"); throw new GlobalException("设备[" + deviceId + "]当前处于故障状态");
} }
return device;
} }
} }

View File

@ -1,24 +1,22 @@
package com.cmvr.inspection.service; package com.cmvr.resource.service;
import com.cmvr.common.exception.GlobalException; import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.robot.RobotEndpoint; import com.cmvr.common.robot.RobotEndpoint;
import com.cmvr.common.robot.RobotEndpointResolver; import com.cmvr.common.robot.RobotEndpointResolver;
import com.cmvr.inspection.domain.InspectionRobot; import com.cmvr.resource.config.ResourceRobotStateProperties;
import com.cmvr.inspection.config.RobotStateProperties; import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.inspection.mapper.InspectionRobotMapper; import com.cmvr.resource.mapper.ResourceRobotMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Date; import java.util.Date;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class InspectionRobotEndpointResolver implements RobotEndpointResolver { public class ResourceRobotEndpointResolver implements RobotEndpointResolver {
private final ResourceRobotMapper robotMapper;
private final InspectionRobotMapper robotMapper; private final ResourceRobotStateProperties stateProperties;
private final RobotStateProperties robotStateProperties;
@Override @Override
public RobotEndpoint resolve(String robotId) { public RobotEndpoint resolve(String robotId) {
@ -26,25 +24,21 @@ public class InspectionRobotEndpointResolver implements RobotEndpointResolver {
if (normalized == null) { if (normalized == null) {
throw new GlobalException("机器人ID不能为空"); throw new GlobalException("机器人ID不能为空");
} }
if (robotStateProperties.isVirtualRobot(normalized)) { if (stateProperties.isVirtualRobot(normalized)) {
throw new GlobalException("虚拟机器人不能调用边缘设备服务:" + normalized); throw new GlobalException("虚拟机器人不能调用边缘设备服务:" + normalized);
} }
List<InspectionRobot> robots = robotMapper.selectByRobotId(normalized); ResourceRobot robot = robotMapper.selectByRobotIdentity(normalized);
if (robots.isEmpty()) { if (robot == null) {
throw new GlobalException("机器人不存在:" + normalized); throw new GlobalException("机器人不存在:" + normalized);
} }
if (robots.size() > 1) {
throw new GlobalException("机器人ID存在重复数据:" + normalized);
}
InspectionRobot robot = robots.get(0);
if ("1".equals(robot.getArchivedStatus())) { if ("1".equals(robot.getArchivedStatus())) {
throw new GlobalException("机器人已归档:" + normalized); throw new GlobalException("机器人已归档:" + normalized);
} }
if (!"1".equals(robot.getConnectStatus())) { if (!"1".equals(robot.getConnectStatus())) {
throw new GlobalException("机器人当前离线:" + normalized); throw new GlobalException("机器人当前离线:" + normalized);
} }
Date heartbeatCutoff = robotStateProperties.heartbeatCutoff(new Date()); Date cutoff = stateProperties.heartbeatCutoff(new Date());
if (robot.getLastHeartbeatTime() == null || robot.getLastHeartbeatTime().before(heartbeatCutoff)) { if (robot.getLastHeartbeatTime() == null || robot.getLastHeartbeatTime().before(cutoff)) {
throw new GlobalException("机器人QUIC心跳已超时:" + normalized); throw new GlobalException("机器人QUIC心跳已超时:" + normalized);
} }
if (StringUtils.isBlank(robot.getIpAddress()) || robot.getPort() == null) { if (StringUtils.isBlank(robot.getIpAddress()) || robot.getPort() == null) {

View File

@ -1,23 +1,22 @@
package com.cmvr.inspection.service; package com.cmvr.resource.service;
import com.cmvr.resource.config.ResourceRobotStateProperties;
import com.cmvr.test.flow.runtime.engine.RobotExecutionTarget; import com.cmvr.test.flow.runtime.engine.RobotExecutionTarget;
import com.cmvr.test.flow.runtime.engine.RobotExecutionTargetResolver; import com.cmvr.test.flow.runtime.engine.RobotExecutionTargetResolver;
import com.cmvr.inspection.config.RobotStateProperties;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class InspectionRobotExecutionTargetResolver implements RobotExecutionTargetResolver { public class ResourceRobotExecutionTargetResolver implements RobotExecutionTargetResolver {
private final ResourceRobotEndpointResolver endpointResolver;
private final InspectionRobotEndpointResolver endpointResolver; private final ResourceRobotStateProperties stateProperties;
private final RobotStateProperties robotStateProperties;
@Override @Override
public RobotExecutionTarget resolve(String robotId) { public RobotExecutionTarget resolve(String robotId) {
String normalized = StringUtils.trimToNull(robotId); String normalized = StringUtils.trimToNull(robotId);
if (!robotStateProperties.isVirtualRobot(normalized)) { if (!stateProperties.isVirtualRobot(normalized)) {
endpointResolver.resolve(normalized); endpointResolver.resolve(normalized);
} }
return new RobotExecutionTarget(normalized); return new RobotExecutionTarget(normalized);

View File

@ -0,0 +1,86 @@
package com.cmvr.resource.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.common.utils.SecurityUtils;
import com.cmvr.resource.domain.ResourceMap;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.domain.ResourceRobotMap;
import com.cmvr.resource.mapper.ResourceMapMapper;
import com.cmvr.resource.mapper.ResourceRobotMapMapper;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.UUID;
@Service
public class ResourceRobotMapService {
private final ResourceRobotMapper robotMapper;
private final ResourceMapMapper mapMapper;
private final ResourceRobotMapMapper bindingMapper;
public ResourceRobotMapService(ResourceRobotMapper robotMapper, ResourceMapMapper mapMapper,
ResourceRobotMapMapper bindingMapper) {
this.robotMapper = robotMapper;
this.mapMapper = mapMapper;
this.bindingMapper = bindingMapper;
}
public List<ResourceMap> listMaps(String robotId) {
return mapMapper.selectByRobotId(robotId);
}
@Transactional(rollbackFor = Exception.class)
public int bind(String robotId, String mapId, boolean makeDefault) {
ResourceRobot robot = robotMapper.selectByRobotIdentity(robotId);
if (robot == null) throw new GlobalException("机器人不存在:" + robotId);
if (mapMapper.selectById(mapId) == null) throw new GlobalException("地图不存在:" + mapId);
ResourceRobotMap binding = bindingMapper.selectOne(new LambdaQueryWrapper<ResourceRobotMap>()
.eq(ResourceRobotMap::getRobotId, robotId)
.eq(ResourceRobotMap::getMapId, mapId));
if (binding == null) {
binding = new ResourceRobotMap();
binding.setId(UUID.randomUUID().toString().replace("-", ""));
binding.setRobotId(robotId);
binding.setMapId(mapId);
binding.setEnabled("1");
binding.setIsDefault(makeDefault ? "1" : "0");
binding.setLoadedStatus("NOT_LOADED");
binding.setCompatibilityStatus("UNKNOWN");
binding.setCreateBy(SecurityUtils.getUsername());
binding.setCreateTime(DateUtils.getNowDate());
bindingMapper.insert(binding);
} else {
binding.setEnabled("1");
if (makeDefault) binding.setIsDefault("1");
binding.setUpdateBy(SecurityUtils.getUsername());
binding.setUpdateTime(DateUtils.getNowDate());
bindingMapper.updateById(binding);
}
if (makeDefault) {
bindingMapper.clearDefault(robotId, mapId);
ResourceRobot update = new ResourceRobot();
update.setId(robot.getId());
update.setCurrentMapId(mapId);
update.setUpdateBy(SecurityUtils.getUsername());
update.setUpdateTime(DateUtils.getNowDate());
robotMapper.updateById(update);
}
return 1;
}
@Transactional(rollbackFor = Exception.class)
public int unbind(String robotId, String mapId) {
ResourceRobot robot = robotMapper.selectByRobotIdentity(robotId);
if (robot == null) throw new GlobalException("机器人不存在:" + robotId);
int rows = bindingMapper.deleteBinding(robotId, mapId);
if (mapId.equals(robot.getCurrentMapId())) {
robotMapper.clearActiveMap(robot.getId(), mapId, SecurityUtils.getUsername());
}
return rows;
}
}

View File

@ -0,0 +1,111 @@
package com.cmvr.resource.service;
import com.cmvr.resource.domain.ResourceDevice;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.mapper.ResourceDeviceMapper;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.common.utils.SecurityUtils;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.springframework.transaction.annotation.Transactional;
/** Shared resource lookup used by Aima, inspection and workflow modules. */
@Service
public class ResourceRobotService {
private final ResourceRobotMapper robotMapper;
private final ResourceDeviceMapper deviceMapper;
public ResourceRobotService(ResourceRobotMapper robotMapper, ResourceDeviceMapper deviceMapper) {
this.robotMapper = robotMapper;
this.deviceMapper = deviceMapper;
}
public List<ResourceRobot> list(ResourceRobot query) {
return robotMapper.selectResourceRobots(query);
}
public ResourceRobot getByRobotId(String robotId) {
if (!StringUtils.hasText(robotId)) {
return null;
}
return robotMapper.selectByRobotIdentity(robotId.trim());
}
public ResourceRobot getById(String id) {
return StringUtils.hasText(id) ? robotMapper.selectById(id.trim()) : null;
}
public List<ResourceDevice> listDevices(String robotId, Integer deviceKind, boolean onlineOnly) {
if (!StringUtils.hasText(robotId)) {
return Collections.emptyList();
}
return deviceMapper.selectByRobotId(robotId.trim(), deviceKind, onlineOnly);
}
public List<ResourceDevice> listDevicesByRobotRecord(String id, Integer deviceKind, boolean onlineOnly) {
ResourceRobot robot = getById(id);
if (robot == null) {
throw new GlobalException("机器人不存在");
}
return listDevices(robot.getRobotId(), deviceKind, onlineOnly);
}
@Transactional(rollbackFor = Exception.class)
public synchronized int create(ResourceRobot robot) {
String robotId = normalize(robot.getRobotId());
if (!StringUtils.hasText(robotId)) {
throw new GlobalException("机器人ID不能为空");
}
if (robotMapper.selectByRobotIdentity(robotId) != null) {
throw new GlobalException("机器人ID已存在:" + robotId);
}
robot.setId(UUID.randomUUID().toString().replace("-", ""));
robot.setRobotId(robotId);
robot.setIpAddress(null);
robot.setPort(null);
robot.setConnectStatus("0");
robot.setArchivedStatus("0");
robot.setCreateBy(SecurityUtils.getUsername());
robot.setCreateTime(DateUtils.getNowDate());
return robotMapper.insert(robot);
}
@Transactional(rollbackFor = Exception.class)
public int update(ResourceRobot robot) {
ResourceRobot existing = getById(robot.getId());
if (existing == null) {
throw new GlobalException("机器人不存在");
}
if (!Objects.equals(existing.getRobotId(), normalize(robot.getRobotId()))) {
throw new GlobalException("机器人ID是永久身份标识,不允许修改");
}
ResourceRobot update = new ResourceRobot();
update.setId(existing.getId());
update.setRobotName(robot.getRobotName());
update.setRobotCode(robot.getRobotCode());
update.setRobotModel(robot.getRobotModel());
update.setRobotType(robot.getRobotType());
update.setCurrentMapId(robot.getCurrentMapId());
update.setRemark(robot.getRemark());
update.setUpdateBy(SecurityUtils.getUsername());
update.setUpdateTime(DateUtils.getNowDate());
return robotMapper.updateById(update);
}
@Transactional(rollbackFor = Exception.class)
public int archive(String[] ids) {
String username = SecurityUtils.getUsername();
return robotMapper.archiveByIds(ids, username);
}
private String normalize(String value) {
return value == null ? null : value.trim();
}
}

View File

@ -0,0 +1,108 @@
package com.cmvr.resource.service;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.DateUtils;
import com.cmvr.common.utils.SecurityUtils;
import com.cmvr.resource.domain.ResourceTerminal;
import com.cmvr.resource.mapper.ResourceTerminalMapper;
import com.cmvr.resource.mapper.ResourceRobotTerminalMapper;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.UUID;
import java.util.LinkedHashSet;
@Service
public class ResourceTerminalService {
private final ResourceTerminalMapper terminalMapper;
private final ResourceRobotTerminalMapper bindingMapper;
private final ResourceRobotMapper robotMapper;
public ResourceTerminalService(ResourceTerminalMapper terminalMapper,
ResourceRobotTerminalMapper bindingMapper,
ResourceRobotMapper robotMapper) {
this.terminalMapper = terminalMapper;
this.bindingMapper = bindingMapper;
this.robotMapper = robotMapper;
}
public List<ResourceTerminal> list(ResourceTerminal query) {
return terminalMapper.selectResourceTerminals(query);
}
public ResourceTerminal get(String id) {
ResourceTerminal terminal = StringUtils.hasText(id) ? terminalMapper.selectById(id) : null;
if (terminal == null) throw new GlobalException("终端不存在");
List<String> robotIds = bindingMapper.selectActiveRobotIds(id);
terminal.setRobotIdsCsv(String.join(",", robotIds));
terminal.setRobotCount(robotIds.size());
return terminal;
}
public List<String> listRobotIds(String id) {
get(id);
return bindingMapper.selectActiveRobotIds(id);
}
public int create(ResourceTerminal terminal) {
if (!StringUtils.hasText(terminal.getTerminalCode())) throw new GlobalException("终端编码不能为空");
if (terminalMapper.selectByCode(terminal.getTerminalCode().trim()) != null) {
throw new GlobalException("终端编码已存在:" + terminal.getTerminalCode());
}
terminal.setId(UUID.randomUUID().toString().replace("-", ""));
terminal.setTerminalCode(terminal.getTerminalCode().trim());
terminal.setConnectStatus("0");
terminal.setCreateBy(SecurityUtils.getUsername());
terminal.setCreateTime(DateUtils.getNowDate());
return terminalMapper.insert(terminal);
}
public int update(ResourceTerminal terminal) {
ResourceTerminal existing = terminalMapper.selectById(terminal.getId());
if (existing == null) throw new GlobalException("终端不存在");
terminal.setTerminalCode(null);
terminal.setIpAddress(null);
terminal.setPort(null);
terminal.setConnectStatus(null);
terminal.setLastHeartbeatTime(null);
terminal.setUpdateBy(SecurityUtils.getUsername());
terminal.setUpdateTime(DateUtils.getNowDate());
return terminalMapper.updateById(terminal);
}
@Transactional(rollbackFor = Exception.class)
public int replaceRobots(String terminalId, List<String> robotIds) {
get(terminalId);
LinkedHashSet<String> normalized = new LinkedHashSet<>();
if (robotIds != null) {
robotIds.stream().filter(StringUtils::hasText).map(String::trim).forEach(normalized::add);
}
for (String robotId : normalized) {
if (robotMapper.selectByRobotIdentity(robotId) == null) {
throw new GlobalException("机器人不存在:" + robotId);
}
}
if (!normalized.isEmpty()) robotMapper.lockByRobotIds(new java.util.ArrayList<>(normalized));
int changed = bindingMapper.deactivateTerminalBindings(terminalId);
for (String robotId : normalized) {
bindingMapper.deactivateOtherBindings(robotId, terminalId);
changed += bindingMapper.activateBinding(UUID.randomUUID().toString().replace("-", ""), robotId, terminalId);
}
return changed;
}
@Transactional(rollbackFor = Exception.class)
public int remove(String[] ids) {
int changed = 0;
for (String id : ids == null ? new String[0] : ids) {
ResourceTerminal terminal = get(id);
if ("1".equals(terminal.getConnectStatus())) throw new GlobalException("在线终端不能删除:" + terminal.getTerminalCode());
bindingMapper.deleteTerminalBindings(id);
changed += terminalMapper.deleteById(id);
}
return changed;
}
}

View File

@ -0,0 +1,20 @@
package com.cmvr.resource.task;
import com.cmvr.resource.service.ResourceQuicStateSynchronizer;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "cmvr.quic", name = "enabled", havingValue = "true")
public class ResourceRobotHeartbeatExpiryTask {
private final ResourceQuicStateSynchronizer synchronizer;
@Scheduled(initialDelayString = "${cmvr.resource.robot-state.initial-delay-ms:3000}",
fixedDelayString = "${cmvr.resource.robot-state.heartbeat-check-ms:5000}")
public void expireStaleRobots() {
synchronizer.expireStaleRobots();
}
}

View File

@ -0,0 +1,64 @@
package com.cmvr.resource.task;
import com.cmvr.resource.config.ResourceRobotStateProperties;
import com.cmvr.resource.domain.ResourceRobot;
import com.cmvr.resource.mapper.ResourceRobotMapper;
import com.cmvr.resource.service.ResourceRobotControlService;
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.Date;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Component
@ConditionalOnProperty(prefix = "cmvr.resource.robot-state", name = "enabled",
havingValue = "true", matchIfMissing = true)
public class ResourceRobotRuntimeRefreshTask {
private static final Logger log = LoggerFactory.getLogger(ResourceRobotRuntimeRefreshTask.class);
private final ResourceRobotMapper robotMapper;
private final ResourceRobotControlService controlService;
private final TaskExecutor taskExecutor;
private final ResourceRobotStateProperties stateProperties;
private final Set<String> syncingRobotIds = ConcurrentHashMap.newKeySet();
public ResourceRobotRuntimeRefreshTask(ResourceRobotMapper robotMapper,
ResourceRobotControlService controlService,
@Qualifier("threadPoolTaskExecutor") TaskExecutor taskExecutor,
ResourceRobotStateProperties stateProperties) {
this.robotMapper = robotMapper;
this.controlService = controlService;
this.taskExecutor = taskExecutor;
this.stateProperties = stateProperties;
}
@Scheduled(initialDelayString = "${cmvr.resource.robot-state.initial-delay-ms:3000}",
fixedDelayString = "${cmvr.resource.robot-state.refresh-ms:5000}")
public void refreshConnectedRobots() {
Date cutoff = stateProperties.heartbeatCutoff(new Date());
for (ResourceRobot robot : robotMapper.selectRuntimeSyncCandidates(
cutoff, stateProperties.normalizedVirtualRobotIds())) {
if (robot.getId() == null || !syncingRobotIds.add(robot.getId())) {
continue;
}
taskExecutor.execute(() -> refreshRobot(robot));
}
}
private void refreshRobot(ResourceRobot robot) {
try {
controlService.syncRuntimeState(robot.getRobotId());
} catch (Exception error) {
log.debug("Failed to refresh AGV runtime state, robotId={}, reason={}",
robot.getRobotId(), error.getMessage());
} finally {
syncingRobotIds.remove(robot.getId());
}
}
}

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.resource.mapper.ResourceDeviceMapper">
<resultMap id="ResourceDeviceResult" type="com.cmvr.resource.domain.ResourceDevice">
<id property="id" column="id"/>
<result property="robotId" column="robot_id"/>
<result property="deviceId" column="device_id"/>
<result property="deviceName" column="device_name"/>
<result property="deviceKind" column="device_kind"/>
<result property="typeName" column="device_type"/>
<result property="deviceRole" column="device_role"/>
<result property="deviceIndex" column="device_index"/>
<result property="terminalId" column="terminal_id"/>
<result property="enabled" column="enabled"/>
<result property="managerState" column="manager_state"/>
<result property="healthStatus" column="health_status"/>
<result property="onlineStatus" column="online_status"/>
<result property="hasError" column="has_error"/>
<result property="errorMessage" column="error_message"/>
<result property="statusUpdatedTime" column="status_updated_time"/>
<result property="lastSeenTime" column="last_seen_time"/>
<result property="offlineTime" column="offline_time"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="columns">id, robot_id, device_id, device_name, device_kind, device_type, device_role,
device_index, terminal_id, enabled, manager_state, health_status, online_status,
has_error, error_message, status_updated_time, last_seen_time,
offline_time, create_by, create_time, update_by, update_time, remark</sql>
<select id="selectResourceDevices" resultMap="ResourceDeviceResult">
select d.* from iot_device d
<where>
<if test="query.robotId != null and query.robotId != ''">d.robot_id = #{query.robotId}</if>
<if test="query.deviceId != null and query.deviceId != ''">and d.device_id = #{query.deviceId}</if>
<if test="query.deviceKind != null">and d.device_kind = #{query.deviceKind}</if>
<if test="query.deviceRole != null and query.deviceRole != ''">and d.device_role = #{query.deviceRole}</if>
<if test="onlineOnly">and d.online_status = '1' and d.enabled = '1'</if>
</where>
order by d.robot_id, d.online_status desc, d.device_kind, d.device_name, d.device_id
</select>
<select id="selectByRobotId" resultMap="ResourceDeviceResult">
select d.* from iot_device d where d.robot_id = #{robotId}
<if test="deviceKind != null">and d.device_kind = #{deviceKind}</if>
<if test="onlineOnly">and d.online_status = '1' and d.enabled = '1'</if>
order by d.online_status desc, d.device_kind, d.device_name, d.device_id
</select>
<select id="selectByIdentity" resultMap="ResourceDeviceResult">
select d.* from iot_device d where d.robot_id = #{robotId} and d.device_id = #{deviceId}
</select>
<update id="updateRuntimeState" parameterType="com.cmvr.resource.domain.ResourceDevice">
update iot_device
set device_kind = #{deviceKind}, device_type = #{typeName}, enabled = #{enabled},
manager_state = #{managerState}, health_status = #{healthStatus},
online_status = #{onlineStatus}, has_error = #{hasError}, error_message = #{errorMessage},
status_updated_time = #{statusUpdatedTime}, last_seen_time = #{lastSeenTime},
offline_time = #{offlineTime}, update_by = #{updateBy}, update_time = #{updateTime}
where id = #{id}
</update>
<update id="markMissingOffline">
update iot_device set online_status = '0', offline_time = #{offlineTime},
update_by = 'quic', update_time = #{offlineTime}
where robot_id = #{robotId} and online_status != '0'
<if test="deviceIds != null and deviceIds.size() > 0">
and device_id not in
<foreach collection="deviceIds" item="deviceId" open="(" separator="," close=")">#{deviceId}</foreach>
</if>
</update>
<update id="markAllOffline">
update iot_device set online_status = '0', offline_time = #{offlineTime},
update_by = 'quic', update_time = #{offlineTime}
where robot_id = #{robotId} and online_status != '0'
</update>
</mapper>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.resource.mapper.ResourceMapMapper">
<resultMap id="ResourceMapResult" type="com.cmvr.resource.domain.ResourceMap">
<id property="id" column="id"/>
<result property="mapName" column="map_name"/>
<result property="mapVersion" column="map_version"/>
<result property="mapType" column="map_type"/>
<result property="coordinateSystem" column="coordinate_system"/>
<result property="sourceType" column="source_type"/>
<result property="sourceRobotId" column="source_robot_id"/>
<result property="sourceMapName" column="source_map_name"/>
<result property="mapSourceRobotId" column="map_source_robot_id"/>
<result property="mapSourceName" column="source_map_name"/>
<result property="mapFilePath" column="map_file_path"/>
<result property="status" column="status"/>
<result property="mapDesc" column="map_desc"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="columns">id, map_name, map_version, map_type, coordinate_system, source_type,
source_robot_id, source_map_name, map_file_path, status, map_desc, create_by, create_time,
update_by, update_time, remark</sql>
<select id="selectResourceMaps" resultMap="ResourceMapResult">
select m.*, r.id as map_source_robot_id from iot_map m
left join iot_robot r on r.robot_id = m.source_robot_id
<where>
<if test="mapName != null and mapName != ''">m.map_name like concat('%', #{mapName}, '%')</if>
<if test="mapType != null and mapType != ''">and m.map_type = #{mapType}</if>
<if test="status != null and status != ''">and m.status = #{status}</if>
</where>
order by m.map_name, m.map_version
</select>
<select id="selectByRobotId" resultMap="ResourceMapResult">
select m.* from iot_map m
join iot_robot_map rm on rm.map_id = m.id
where rm.robot_id = #{robotId} and rm.enabled = '1' and m.status = '0'
order by rm.is_default desc, m.map_name, m.map_version
</select>
</mapper>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.resource.mapper.ResourceRobotMapMapper">
<update id="clearDefault">
update iot_robot_map set is_default = '0', update_time = now()
where robot_id = #{robotId} and map_id != #{exceptMapId}
</update>
<delete id="deleteBinding">
delete from iot_robot_map where robot_id = #{robotId} and map_id = #{mapId}
</delete>
<select id="countByMapIds" resultType="int">
select count(*) from iot_robot_map where enabled='1' and map_id in
<foreach collection="mapIds" item="mapId" open="(" separator="," close=")">#{mapId}</foreach>
</select>
</mapper>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.resource.mapper.ResourceRobotMapper">
<resultMap id="ResourceRobotResult" type="com.cmvr.resource.domain.ResourceRobot">
<id property="id" column="id"/>
<result property="robotId" column="robot_id"/>
<result property="robotCode" column="robot_code"/>
<result property="robotName" column="robot_name"/>
<result property="robotModel" column="robot_model"/>
<result property="robotType" column="robot_type"/>
<result property="ipAddress" column="ip_address"/>
<result property="port" column="port"/>
<result property="currentMapId" column="active_map_id"/>
<result property="status" column="runtime_status"/>
<result property="batteryLevel" column="battery_level"/>
<result property="currentPosition" column="current_position"/>
<result property="connectStatus" column="connect_status"/>
<result property="quicNodeId" column="quic_node_id"/>
<result property="quicSessionId" column="quic_session_id"/>
<result property="quicBootId" column="quic_boot_id"/>
<result property="observedIp" column="observed_ip"/>
<result property="softwareVersion" column="software_version"/>
<result property="lastHeartbeatTime" column="last_heartbeat_time"/>
<result property="archivedStatus" column="archived_status"/>
<result property="currentMapName" column="current_map_name"/>
<result property="deviceTotal" column="device_total"/>
<result property="onlineDeviceCount" column="online_device_count"/>
<result property="faultDeviceCount" column="fault_device_count"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="columns">id, robot_id, robot_code, robot_name, robot_model, robot_type, ip_address, port,
active_map_id, runtime_status, battery_level, current_position, connect_status,
quic_node_id, quic_session_id, quic_boot_id, observed_ip, software_version,
last_heartbeat_time, archived_status, create_by, create_time, update_by, update_time, remark</sql>
<select id="selectResourceRobots" resultMap="ResourceRobotResult">
select r.*, m.map_name as current_map_name,
(select count(*) from iot_device d where d.robot_id = r.robot_id) as device_total,
(select count(*) from iot_device d where d.robot_id = r.robot_id and d.online_status = '1') as online_device_count,
(select count(*) from iot_device d where d.robot_id = r.robot_id and (d.health_status = 3 or d.has_error = '1')) as fault_device_count
from iot_robot r
left join iot_map m on m.id = r.active_map_id
<where>
coalesce(r.archived_status, '0') = '0'
<if test="robotId != null and robotId != ''">and r.robot_id = #{robotId}</if>
<if test="robotName != null and robotName != ''">and r.robot_name like concat('%', #{robotName}, '%')</if>
<if test="robotType != null and robotType != ''">and r.robot_type = #{robotType}</if>
<if test="connectStatus != null and connectStatus != ''">and r.connect_status = #{connectStatus}</if>
</where>
order by r.robot_name, r.robot_id
</select>
<select id="selectByRobotIdentity" resultMap="ResourceRobotResult">
select <include refid="columns"/> from iot_robot where robot_id = #{robotId}
</select>
<select id="selectById" resultMap="ResourceRobotResult">
select <include refid="columns"/> from iot_robot where id = #{id}
</select>
<update id="archiveByIds">
update iot_robot set archived_status = '1', connect_status = '0', update_by = #{updateBy}, update_time = now()
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
</update>
<update id="clearActiveMap">
update iot_robot set active_map_id = null, update_by = #{updateBy}, update_time = now()
where id = #{id} and active_map_id = #{mapId}
</update>
<update id="updateQuicRuntimeState" parameterType="com.cmvr.resource.domain.ResourceRobot">
update iot_robot
set ip_address = #{ipAddress}, port = #{port}, connect_status = #{connectStatus},
runtime_status = coalesce(#{status}, runtime_status),
quic_node_id = #{quicNodeId}, quic_session_id = #{quicSessionId},
quic_boot_id = #{quicBootId}, observed_ip = #{observedIp},
software_version = #{softwareVersion}, last_heartbeat_time = #{lastHeartbeatTime},
update_by = 'quic', update_time = #{updateTime}
where id = #{id}
</update>
<select id="selectStaleConnected" resultMap="ResourceRobotResult">
select <include refid="columns"/> from iot_robot
where connect_status = '1' and coalesce(archived_status, '0') = '0'
and (last_heartbeat_time is null or last_heartbeat_time &lt; #{heartbeatCutoff})
<if test="excludedRobotIds != null and excludedRobotIds.size() > 0">
and lower(robot_id) not in
<foreach collection="excludedRobotIds" item="robotId" open="(" separator="," close=")">#{robotId}</foreach>
</if>
</select>
<select id="selectRuntimeSyncCandidates" resultMap="ResourceRobotResult">
select <include refid="columns"/> from iot_robot r
where r.connect_status='1' and coalesce(r.archived_status,'0')='0'
and r.last_heartbeat_time is not null and r.last_heartbeat_time &gt;= #{heartbeatCutoff}
<if test="excludedRobotIds != null and excludedRobotIds.size() > 0">
and lower(r.robot_id) not in
<foreach collection="excludedRobotIds" item="robotId" open="(" separator="," close=")">#{robotId}</foreach>
</if>
and (select count(*) from iot_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="updateAgvRuntimeStatus" parameterType="com.cmvr.resource.domain.ResourceRobot">
update iot_robot set runtime_status=#{status}, battery_level=#{batteryLevel},
current_position=#{currentPosition}, update_by='agv-status', update_time=now()
where id=#{id}
</update>
<select id="lockByRobotIds" resultType="java.lang.String">
select robot_id from iot_robot where robot_id in
<foreach collection="robotIds" item="robotId" open="(" separator="," close=")">#{robotId}</foreach>
for update
</select>
<update id="markHeartbeatExpiredOffline">
update iot_robot set connect_status = '0', runtime_status = '1',
update_by = 'heartbeat-timeout', update_time = #{offlineTime}
where id = #{id} and connect_status = '1'
and (last_heartbeat_time is null or last_heartbeat_time &lt; #{heartbeatCutoff})
</update>
</mapper>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.resource.mapper.ResourceRobotTerminalMapper">
<update id="deactivateOtherBindings">
update iot_robot_terminal set active = '0', unbind_time = now(), update_time = now()
where robot_id = #{robotId} and terminal_id != #{terminalId} and active = '1'
</update>
<select id="selectActiveRobotIds" resultType="java.lang.String">
select robot_id from iot_robot_terminal where terminal_id = #{terminalId} and active = '1' order by robot_id
</select>
<update id="deactivateTerminalBindings">
update iot_robot_terminal set active = '0', unbind_time = now(), update_time = now()
where terminal_id = #{terminalId} and active = '1'
</update>
<delete id="deleteTerminalBindings">
delete from iot_robot_terminal where terminal_id = #{terminalId}
</delete>
<insert id="activateBinding">
insert into iot_robot_terminal(id, robot_id, terminal_id, active, bind_time, create_time, update_time)
values(#{id}, #{robotId}, #{terminalId}, '1', now(), now(), now())
on duplicate key update active = '1', bind_time = now(), unbind_time = null, update_time = now()
</insert>
</mapper>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cmvr.resource.mapper.ResourceTerminalMapper">
<resultMap id="TerminalResult" type="com.cmvr.resource.domain.ResourceTerminal">
<id property="id" column="id"/>
<result property="terminalCode" column="terminal_code"/>
<result property="terminalName" column="terminal_name"/>
<result property="ipAddress" column="ip_address"/>
<result property="port" column="port"/>
<result property="softwareVersion" column="software_version"/>
<result property="connectStatus" column="connect_status"/>
<result property="lastHeartbeatTime" column="last_heartbeat_time"/>
<result property="status" column="status"/>
<result property="robotIdsCsv" column="robot_ids_csv"/>
<result property="robotCount" column="robot_count"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="columns">id, terminal_code, terminal_name, ip_address, port, software_version,
connect_status, last_heartbeat_time, status, create_by, create_time, update_by, update_time, remark</sql>
<select id="selectResourceTerminals" resultMap="TerminalResult">
select t.*,
(select group_concat(rt.robot_id order by rt.robot_id separator ',') from iot_robot_terminal rt
where rt.terminal_id = t.id and rt.active = '1') robot_ids_csv,
(select count(*) from iot_robot_terminal rt where rt.terminal_id = t.id and rt.active = '1') robot_count
from iot_terminal t
<where>
<if test="terminalCode != null and terminalCode != ''">t.terminal_code = #{terminalCode}</if>
<if test="terminalName != null and terminalName != ''">and t.terminal_name like concat('%', #{terminalName}, '%')</if>
<if test="connectStatus != null and connectStatus != ''">and t.connect_status = #{connectStatus}</if>
</where>
order by t.connect_status desc, t.terminal_name, t.terminal_code
</select>
<select id="selectByCode" resultMap="TerminalResult">
select <include refid="columns"/> from iot_terminal where terminal_code = #{terminalCode}
</select>
</mapper>

Some files were not shown because too many files have changed in this diff Show More