新增: 设备级算法参数(device_algo_params 表 + API + 配置推送按设备合并)
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.genersoft.iot.vmp.aiot.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "设备级算法参数")
|
||||
public class AiAlgorithmDeviceParams {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "边缘设备ID")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "算法编码")
|
||||
private String algoCode;
|
||||
|
||||
@Schema(description = "设备级参数JSON")
|
||||
private String params;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private String updateTime;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.aiot.controller;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithmDeviceParams;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -22,8 +23,23 @@ public class AiAlgorithmController {
|
||||
|
||||
@Operation(summary = "查询算法列表")
|
||||
@GetMapping("/list")
|
||||
public List<AiAlgorithm> queryList() {
|
||||
return algorithmService.queryAll();
|
||||
public List<AiAlgorithm> queryList(@RequestParam(required = false) String deviceId) {
|
||||
List<AiAlgorithm> algorithms = algorithmService.queryAll();
|
||||
if (deviceId != null && !deviceId.isEmpty()) {
|
||||
// 查设备级参数,合并到每个算法的 globalParams 字段返回
|
||||
List<AiAlgorithmDeviceParams> deviceParams = algorithmService.queryDeviceParams(deviceId);
|
||||
Map<String, String> deviceParamsMap = new java.util.HashMap<>();
|
||||
for (AiAlgorithmDeviceParams dp : deviceParams) {
|
||||
deviceParamsMap.put(dp.getAlgoCode(), dp.getParams());
|
||||
}
|
||||
for (AiAlgorithm algo : algorithms) {
|
||||
String dp = deviceParamsMap.get(algo.getAlgoCode());
|
||||
if (dp != null && !dp.isEmpty()) {
|
||||
algo.setGlobalParams(dp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return algorithms;
|
||||
}
|
||||
|
||||
@Operation(summary = "启用/禁用算法")
|
||||
@@ -44,4 +60,18 @@ public class AiAlgorithmController {
|
||||
String globalParams = body.get("globalParams");
|
||||
algorithmService.saveGlobalParams(algoCode, globalParams);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询设备级算法参数")
|
||||
@GetMapping("/device-params/{deviceId}")
|
||||
public List<AiAlgorithmDeviceParams> queryDeviceParams(@PathVariable String deviceId) {
|
||||
return algorithmService.queryDeviceParams(deviceId);
|
||||
}
|
||||
|
||||
@Operation(summary = "保存设备级算法参数")
|
||||
@PostMapping("/device-params/{deviceId}/{algoCode}")
|
||||
public void saveDeviceParams(@PathVariable String deviceId, @PathVariable String algoCode,
|
||||
@RequestBody Map<String, String> body) {
|
||||
String params = body.get("params");
|
||||
algorithmService.saveDeviceParams(deviceId, algoCode, params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.genersoft.iot.vmp.aiot.dao;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithmDeviceParams;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AiAlgorithmDeviceParamsMapper {
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_algorithm_device_params WHERE device_id=#{deviceId} ORDER BY id")
|
||||
List<AiAlgorithmDeviceParams> queryByDeviceId(@Param("deviceId") String deviceId);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_algorithm_device_params WHERE device_id=#{deviceId} AND algo_code=#{algoCode}")
|
||||
AiAlgorithmDeviceParams queryByDeviceAndAlgo(@Param("deviceId") String deviceId, @Param("algoCode") String algoCode);
|
||||
|
||||
@Insert("INSERT INTO wvp_ai_algorithm_device_params (device_id, algo_code, params, create_time, update_time) " +
|
||||
"VALUES (#{deviceId}, #{algoCode}, #{params}, #{createTime}, #{updateTime}) " +
|
||||
"ON DUPLICATE KEY UPDATE params=#{params}, update_time=#{updateTime}")
|
||||
int upsert(AiAlgorithmDeviceParams record);
|
||||
|
||||
@Delete("DELETE FROM wvp_ai_algorithm_device_params WHERE device_id=#{deviceId} AND algo_code=#{algoCode}")
|
||||
int deleteByDeviceAndAlgo(@Param("deviceId") String deviceId, @Param("algoCode") String algoCode);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.genersoft.iot.vmp.aiot.service;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithmDeviceParams;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -15,4 +16,8 @@ public interface IAiAlgorithmService {
|
||||
void syncFromEdge();
|
||||
|
||||
void saveGlobalParams(String algoCode, String globalParams);
|
||||
|
||||
List<AiAlgorithmDeviceParams> queryDeviceParams(String deviceId);
|
||||
|
||||
void saveDeviceParams(String deviceId, String algoCode, String params);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.genersoft.iot.vmp.aiot.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithmDeviceParams;
|
||||
import com.genersoft.iot.vmp.aiot.config.AiServiceConfig;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmDeviceParamsMapper;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigService;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiRedisConfigService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -25,6 +28,9 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
|
||||
@Autowired
|
||||
private AiAlgorithmMapper algorithmMapper;
|
||||
|
||||
@Autowired
|
||||
private AiAlgorithmDeviceParamsMapper deviceParamsMapper;
|
||||
|
||||
@Autowired
|
||||
private AiServiceConfig aiServiceConfig;
|
||||
|
||||
@@ -34,6 +40,9 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
|
||||
@Autowired
|
||||
private IAiConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private IAiRedisConfigService redisConfigService;
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
@@ -176,4 +185,30 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
|
||||
log.warn("[AI算法] 全局参数推送失败(参数已保存): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiAlgorithmDeviceParams> queryDeviceParams(String deviceId) {
|
||||
return deviceParamsMapper.queryByDeviceId(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveDeviceParams(String deviceId, String algoCode, String params) {
|
||||
String now = LocalDateTime.now().format(FORMATTER);
|
||||
AiAlgorithmDeviceParams record = new AiAlgorithmDeviceParams();
|
||||
record.setDeviceId(deviceId);
|
||||
record.setAlgoCode(algoCode);
|
||||
record.setParams(params);
|
||||
record.setCreateTime(now);
|
||||
record.setUpdateTime(now);
|
||||
deviceParamsMapper.upsert(record);
|
||||
log.info("[AI算法] 保存设备级参数: deviceId={}, algoCode={}, params={}", deviceId, algoCode, params);
|
||||
|
||||
// 只推送该设备的配置
|
||||
try {
|
||||
redisConfigService.writeDeviceAggregatedConfig(deviceId, "UPDATE");
|
||||
log.info("[AI算法] 设备级参数变更已推送到设备: {}", deviceId);
|
||||
} catch (Exception e) {
|
||||
log.warn("[AI算法] 设备级参数推送失败(参数已保存): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgoTemplate;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithmDeviceParams;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoi;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmDeviceParamsMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgoTemplateMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiMapper;
|
||||
@@ -46,6 +48,9 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
||||
@Autowired
|
||||
private AiAlgorithmMapper algorithmMapper;
|
||||
|
||||
@Autowired
|
||||
private AiAlgorithmDeviceParamsMapper deviceParamsMapper;
|
||||
|
||||
@Autowired
|
||||
private AiAlgoTemplateMapper templateMapper;
|
||||
|
||||
@@ -492,6 +497,13 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
||||
algoMap.put(algo.getAlgoCode(), algo);
|
||||
}
|
||||
|
||||
// 查询该设备的设备级参数,构建 algo_code -> params 索引
|
||||
List<AiAlgorithmDeviceParams> deviceParamsList = deviceParamsMapper.queryByDeviceId(deviceId);
|
||||
Map<String, String> deviceParamsMap = new LinkedHashMap<>();
|
||||
for (AiAlgorithmDeviceParams dp : deviceParamsList) {
|
||||
deviceParamsMap.put(dp.getAlgoCode(), dp.getParams());
|
||||
}
|
||||
|
||||
for (String cameraId : cameraIds) {
|
||||
// 摄像头信息
|
||||
Map<String, Object> cameraMap = new LinkedHashMap<>();
|
||||
@@ -607,12 +619,13 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
||||
bindMap.put("enabled", bind.getEnabled() != null && bind.getEnabled() == 1);
|
||||
bindMap.put("priority", bind.getPriority() != null ? bind.getPriority() : 0);
|
||||
|
||||
// params: 三级合并 param_schema.default < global_params < bind.params
|
||||
// params: 四级合并 param_schema.default < global_params < device_params < bind.params
|
||||
AiAlgorithm algo = algoMap.get(bind.getAlgoCode());
|
||||
String algoParamSchema = algo != null ? algo.getParamSchema() : null;
|
||||
String algoGlobalParams = algo != null ? algo.getGlobalParams() : null;
|
||||
String algoDeviceParams = deviceParamsMap.get(bind.getAlgoCode());
|
||||
String effectiveParams = resolveEffectiveParams(bind);
|
||||
String mergedParams = mergeParams(algoParamSchema, algoGlobalParams, effectiveParams);
|
||||
String mergedParams = mergeParams(algoParamSchema, algoGlobalParams, algoDeviceParams, effectiveParams);
|
||||
try {
|
||||
bindMap.put("params", objectMapper.readValue(mergedParams, Object.class));
|
||||
} catch (Exception e) {
|
||||
@@ -629,13 +642,16 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
||||
flatConfig.put("binds", binds);
|
||||
|
||||
// 顶层新增 global_params: {algo_code: {param_key: value, ...}, ...}
|
||||
// 优先使用设备级参数,没有则使用全局参数
|
||||
Map<String, Object> globalParamsMap = new LinkedHashMap<>();
|
||||
for (AiAlgorithm algo : allAlgorithms) {
|
||||
if (algo.getGlobalParams() != null && !algo.getGlobalParams().isEmpty()) {
|
||||
String deviceParams = deviceParamsMap.get(algo.getAlgoCode());
|
||||
String sourceParams = deviceParams != null && !deviceParams.isEmpty() ? deviceParams : algo.getGlobalParams();
|
||||
if (sourceParams != null && !sourceParams.isEmpty()) {
|
||||
try {
|
||||
globalParamsMap.put(algo.getAlgoCode(), objectMapper.readValue(algo.getGlobalParams(), Object.class));
|
||||
globalParamsMap.put(algo.getAlgoCode(), objectMapper.readValue(sourceParams, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.warn("[AiRedis] 解析算法 {} 的 globalParams 失败: {}", algo.getAlgoCode(), e.getMessage());
|
||||
log.warn("[AiRedis] 解析算法 {} 的参数失败: {}", algo.getAlgoCode(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -647,14 +663,15 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 三级参数合并:param_schema 的 default 值 < global_params < bind 的 params
|
||||
* 四级参数合并:param_schema 的 default 值 < global_params < device_params < bind 的 params
|
||||
* @param paramSchema 算法参数模板JSON(含 default 字段)
|
||||
* @param globalParams 用户自定义的全局默认参数JSON
|
||||
* @param deviceParams 设备级别的参数JSON
|
||||
* @param bindParams 绑定级别的参数JSON(最高优先级)
|
||||
* @return 合并后的参数JSON字符串
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private String mergeParams(String paramSchema, String globalParams, String bindParams) {
|
||||
private String mergeParams(String paramSchema, String globalParams, String deviceParams, String bindParams) {
|
||||
Map<String, Object> merged = new LinkedHashMap<>();
|
||||
|
||||
// 1. 从 paramSchema 提取 default 值(最低优先级)
|
||||
@@ -684,7 +701,17 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 用 bindParams 覆盖(最高优先级)
|
||||
// 3. 用 deviceParams 覆盖
|
||||
if (deviceParams != null && !deviceParams.isEmpty()) {
|
||||
try {
|
||||
Map<String, Object> device = JSON.parseObject(deviceParams, LinkedHashMap.class);
|
||||
merged.putAll(device);
|
||||
} catch (Exception e) {
|
||||
log.debug("[AiRedis] 解析 deviceParams 失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 用 bindParams 覆盖(最高优先级)
|
||||
if (bindParams != null && !bindParams.isEmpty()) {
|
||||
try {
|
||||
Map<String, Object> bind = JSON.parseObject(bindParams, LinkedHashMap.class);
|
||||
|
||||
12
数据库/aiot/迁移-添加device_algo_params表.sql
Normal file
12
数据库/aiot/迁移-添加device_algo_params表.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
-- 设备级算法参数表
|
||||
-- 允许为每个边缘设备单独配置算法参数,覆盖全局默认值
|
||||
CREATE TABLE IF NOT EXISTS wvp_ai_algorithm_device_params (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
device_id VARCHAR(50) NOT NULL COMMENT '边缘设备ID',
|
||||
algo_code VARCHAR(100) NOT NULL COMMENT '算法编码',
|
||||
params TEXT NULL COMMENT '设备级参数JSON',
|
||||
create_time VARCHAR(50) NULL,
|
||||
update_time VARCHAR(50) NULL,
|
||||
UNIQUE KEY uk_device_algo (device_id, algo_code),
|
||||
INDEX idx_device_id (device_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备级算法参数';
|
||||
Reference in New Issue
Block a user