Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f478e96d97 | |||
| 780650f0cc |
@@ -74,6 +74,10 @@ public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
|
|||||||
"非机动车违停检测", "bicycle,motorcycle", "检测禁停区域内是否有非机动车(自行车、电动车)违规停放。确认非机动车停留10秒后开始3分钟倒计时,超时触发告警。非机动车离开60秒后自动结束告警。",
|
"非机动车违停检测", "bicycle,motorcycle", "检测禁停区域内是否有非机动车(自行车、电动车)违规停放。确认非机动车停留10秒后开始3分钟倒计时,超时触发告警。非机动车离开60秒后自动结束告警。",
|
||||||
"{\"confirm_vehicle_sec\":{\"type\":\"int\",\"default\":10,\"min\":5},\"parking_countdown_sec\":{\"type\":\"int\",\"default\":180,\"min\":60},\"confirm_clear_sec\":{\"type\":\"int\",\"default\":60,\"min\":10},\"cooldown_sec\":{\"type\":\"int\",\"default\":900,\"min\":0}}"
|
"{\"confirm_vehicle_sec\":{\"type\":\"int\",\"default\":10,\"min\":5},\"parking_countdown_sec\":{\"type\":\"int\",\"default\":180,\"min\":60},\"confirm_clear_sec\":{\"type\":\"int\",\"default\":60,\"min\":10},\"cooldown_sec\":{\"type\":\"int\",\"default\":900,\"min\":0}}"
|
||||||
});
|
});
|
||||||
|
PRESET_ALGORITHMS.put("garbage", new String[]{
|
||||||
|
"垃圾检测", "garbage", "检测监控区域内散落垃圾的持续存在,确认60秒后触发告警,垃圾清理后30秒确认自动结束告警。",
|
||||||
|
"{\"confirm_garbage_sec\":{\"type\":\"int\",\"default\":60,\"min\":10,\"max\":600},\"confirm_clear_sec\":{\"type\":\"int\",\"default\":30,\"min\":10,\"max\":300},\"cooldown_sec\":{\"type\":\"int\",\"default\":600,\"min\":0}}"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
|||||||
@@ -438,11 +438,29 @@ public class AiRedisConfigServiceImpl implements IAiRedisConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 查询该设备下所有摄像头
|
// 1. 查询该设备下所有摄像头(优先从 stream_proxy 表查询,确保包含无 ROI 的摄像头)
|
||||||
List<String> cameraIds = roiMapper.queryDistinctCameraIdsByDeviceId(deviceId);
|
// 合并两个来源:stream_proxy 表的 edge_device_id 关联 + ROI 表的 device_id 关联
|
||||||
if (cameraIds == null || cameraIds.isEmpty()) {
|
Set<String> cameraIdSet = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
// 来源1:stream_proxy 表中 edge_device_id 匹配的摄像头
|
||||||
|
List<String> proxyCameraIds = streamProxyMapper.selectCameraCodesByEdgeDeviceId(deviceId);
|
||||||
|
if (proxyCameraIds != null && !proxyCameraIds.isEmpty()) {
|
||||||
|
cameraIdSet.addAll(proxyCameraIds);
|
||||||
|
log.debug("[AiRedis] 从 stream_proxy 表查询到 {} 个摄像头: deviceId={}", proxyCameraIds.size(), deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 来源2:ROI 表中 device_id 关联的摄像头(兼容旧数据或边缘情况)
|
||||||
|
List<String> roiCameraIds = roiMapper.queryDistinctCameraIdsByDeviceId(deviceId);
|
||||||
|
if (roiCameraIds != null && !roiCameraIds.isEmpty()) {
|
||||||
|
cameraIdSet.addAll(roiCameraIds);
|
||||||
|
log.debug("[AiRedis] 从 ROI 表查询到 {} 个摄像头: deviceId={}", roiCameraIds.size(), deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> cameraIds = new ArrayList<>(cameraIdSet);
|
||||||
|
if (cameraIds.isEmpty()) {
|
||||||
log.info("[AiRedis] 设备 {} 下无关联摄像头,写入空配置", deviceId);
|
log.info("[AiRedis] 设备 {} 下无关联摄像头,写入空配置", deviceId);
|
||||||
cameraIds = Collections.emptyList();
|
} else {
|
||||||
|
log.info("[AiRedis] 设备 {} 下共 {} 个摄像头(去重后)", deviceId, cameraIds.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 构建扁平格式 JSON
|
// 2. 构建扁平格式 JSON
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
|
|||||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
|
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
|
||||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiMapper;
|
import com.genersoft.iot.vmp.aiot.dao.AiRoiMapper;
|
||||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
||||||
|
import com.genersoft.iot.vmp.aiot.service.IAiRedisConfigService;
|
||||||
import com.genersoft.iot.vmp.aiot.service.IAiRoiService;
|
import com.genersoft.iot.vmp.aiot.service.IAiRoiService;
|
||||||
import com.genersoft.iot.vmp.streamProxy.dao.StreamProxyMapper;
|
import com.genersoft.iot.vmp.streamProxy.dao.StreamProxyMapper;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
@@ -40,6 +41,9 @@ public class AiRoiServiceImpl implements IAiRoiService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IAiConfigLogService configLogService;
|
private IAiConfigLogService configLogService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiRedisConfigService redisConfigService;
|
||||||
|
|
||||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -157,6 +161,11 @@ public class AiRoiServiceImpl implements IAiRoiService {
|
|||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
throw new IllegalArgumentException("该ROI已绑定此算法");
|
throw new IllegalArgumentException("该ROI已绑定此算法");
|
||||||
}
|
}
|
||||||
|
// 校验算法是否存在
|
||||||
|
AiAlgorithm algo = algorithmMapper.queryByCode(bind.getAlgoCode());
|
||||||
|
if (algo == null) {
|
||||||
|
throw new IllegalArgumentException("算法不存在: " + bind.getAlgoCode());
|
||||||
|
}
|
||||||
if (ObjectUtils.isEmpty(bind.getBindId())) {
|
if (ObjectUtils.isEmpty(bind.getBindId())) {
|
||||||
bind.setBindId(UUID.randomUUID().toString());
|
bind.setBindId(UUID.randomUUID().toString());
|
||||||
}
|
}
|
||||||
@@ -170,17 +179,25 @@ public class AiRoiServiceImpl implements IAiRoiService {
|
|||||||
bind.setUpdateTime(now);
|
bind.setUpdateTime(now);
|
||||||
bindMapper.add(bind);
|
bindMapper.add(bind);
|
||||||
configLogService.addLog("BIND", bind.getBindId(), null, toJson(bind), null);
|
configLogService.addLog("BIND", bind.getBindId(), null, toJson(bind), null);
|
||||||
|
|
||||||
|
// 推送配置到 Edge(绑定算法后)
|
||||||
|
pushRoiConfig(bind.getRoiId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void unbindAlgo(String bindId) {
|
public void unbindAlgo(String bindId) {
|
||||||
AiRoiAlgoBind old = bindMapper.queryByBindId(bindId);
|
AiRoiAlgoBind old = bindMapper.queryByBindId(bindId);
|
||||||
|
String roiId = null;
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
String roiId = old.getRoiId();
|
roiId = old.getRoiId();
|
||||||
bindMapper.deleteByBindId(bindId);
|
bindMapper.deleteByBindId(bindId);
|
||||||
configLogService.addLog("BIND", bindId, toJson(old), null, null);
|
configLogService.addLog("BIND", bindId, toJson(old), null, null);
|
||||||
}
|
}
|
||||||
|
// 推送配置到 Edge(解绑算法后)
|
||||||
|
if (roiId != null) {
|
||||||
|
pushRoiConfig(roiId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -200,6 +217,9 @@ public class AiRoiServiceImpl implements IAiRoiService {
|
|||||||
bind.setUpdateTime(now);
|
bind.setUpdateTime(now);
|
||||||
bindMapper.updateByBindId(bind);
|
bindMapper.updateByBindId(bind);
|
||||||
configLogService.addLog("BIND", bind.getBindId(), toJson(old), toJson(bind), null);
|
configLogService.addLog("BIND", bind.getBindId(), toJson(old), toJson(bind), null);
|
||||||
|
|
||||||
|
// 推送配置到 Edge(参数变更后)
|
||||||
|
pushRoiConfig(old.getRoiId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toJson(Object obj) {
|
private String toJson(Object obj) {
|
||||||
@@ -212,4 +232,21 @@ public class AiRoiServiceImpl implements IAiRoiService {
|
|||||||
return obj.toString();
|
return obj.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送指定 ROI 所属设备的配置到边缘端
|
||||||
|
*/
|
||||||
|
private void pushRoiConfig(String roiId) {
|
||||||
|
try {
|
||||||
|
AiRoi roi = roiMapper.queryByRoiId(roiId);
|
||||||
|
if (roi != null && roi.getDeviceId() != null && !roi.getDeviceId().isEmpty()) {
|
||||||
|
redisConfigService.writeDeviceAggregatedConfig(roi.getDeviceId(), "UPDATE");
|
||||||
|
log.info("[AiRoi] ROI配置已推送到Edge,roiId={}, deviceId={}", roiId, roi.getDeviceId());
|
||||||
|
} else {
|
||||||
|
log.warn("[AiRoi] 无法确定ROI所属设备,跳过推送,roiId={}", roiId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[AiRoi] 推送ROI配置失败,roiId={}", roiId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,4 +133,10 @@ public interface StreamProxyMapper {
|
|||||||
*/
|
*/
|
||||||
@Select("SELECT edge_device_id FROM wvp_stream_proxy WHERE camera_code = #{cameraCode}")
|
@Select("SELECT edge_device_id FROM wvp_stream_proxy WHERE camera_code = #{cameraCode}")
|
||||||
String selectEdgeDeviceIdByCameraCode(@Param("cameraCode") String cameraCode);
|
String selectEdgeDeviceIdByCameraCode(@Param("cameraCode") String cameraCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 edge_device_id 查询关联的所有摄像头编码
|
||||||
|
*/
|
||||||
|
@Select("SELECT camera_code FROM wvp_stream_proxy WHERE edge_device_id = #{edgeDeviceId} AND camera_code IS NOT NULL AND camera_code != '' ORDER BY camera_name")
|
||||||
|
List<String> selectCameraCodesByEdgeDeviceId(@Param("edgeDeviceId") String edgeDeviceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,21 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
streamProxyMapper.add(streamProxy);
|
streamProxyMapper.add(streamProxy);
|
||||||
streamProxy.setDataType(ChannelDataType.STREAM_PROXY);
|
streamProxy.setDataType(ChannelDataType.STREAM_PROXY);
|
||||||
streamProxy.setDataDeviceId(streamProxy.getId());
|
streamProxy.setDataDeviceId(streamProxy.getId());
|
||||||
|
|
||||||
|
// 推送配置到 Edge(新增摄像头)
|
||||||
|
String edgeDeviceId = streamProxy.getEdgeDeviceId();
|
||||||
|
if (edgeDeviceId != null && !edgeDeviceId.isEmpty()) {
|
||||||
|
try {
|
||||||
|
redisConfigService.writeDeviceAggregatedConfig(edgeDeviceId, "UPDATE");
|
||||||
|
log.info("[StreamProxy] 新增摄像头后推送配置到 Edge,edgeDeviceId={}, cameraCode={}",
|
||||||
|
edgeDeviceId, cameraCode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[StreamProxy] 新增摄像头后推送配置失败,edgeDeviceId={}, cameraCode={}",
|
||||||
|
edgeDeviceId, cameraCode, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("[StreamProxy] 新增摄像头但 edgeDeviceId 为空,跳过推送配置,cameraCode={}", cameraCode);
|
||||||
|
}
|
||||||
} catch (DuplicateKeyException e) {
|
} catch (DuplicateKeyException e) {
|
||||||
throw new RuntimeException("生成 camera_code 失败,请重试");
|
throw new RuntimeException("生成 camera_code 失败,请重试");
|
||||||
}
|
}
|
||||||
@@ -185,7 +200,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
|
|
||||||
private void delete(StreamProxy streamProxy) {
|
private void delete(StreamProxy streamProxy) {
|
||||||
Assert.notNull(streamProxy, "代理不可为NULL");
|
Assert.notNull(streamProxy, "代理不可为NULL");
|
||||||
String cameraCode = streamProxy.getCameraCode();
|
String edgeDeviceId = streamProxy.getEdgeDeviceId();
|
||||||
if (streamProxy.getPulling() != null && streamProxy.getPulling()) {
|
if (streamProxy.getPulling() != null && streamProxy.getPulling()) {
|
||||||
playService.stopProxy(streamProxy);
|
playService.stopProxy(streamProxy);
|
||||||
}
|
}
|
||||||
@@ -194,13 +209,15 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
}
|
}
|
||||||
streamProxyMapper.delete(streamProxy.getId());
|
streamProxyMapper.delete(streamProxy.getId());
|
||||||
// 推送配置到 Edge(删除摄像头)
|
// 推送配置到 Edge(删除摄像头)
|
||||||
if (cameraCode != null) {
|
if (edgeDeviceId != null && !edgeDeviceId.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
redisConfigService.writeDeviceAggregatedConfig(cameraCode, "UPDATE");
|
redisConfigService.writeDeviceAggregatedConfig(edgeDeviceId, "UPDATE");
|
||||||
log.info("[StreamProxy] 删除摄像头后推送配置到 Edge,camera_code={}", cameraCode);
|
log.info("[StreamProxy] 删除摄像头后推送配置到 Edge,edgeDeviceId={}", edgeDeviceId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[StreamProxy] 删除摄像头后推送配置失败,camera_code={}", cameraCode, e);
|
log.error("[StreamProxy] 删除摄像头后推送配置失败,edgeDeviceId={}", edgeDeviceId, e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("[StreamProxy] 删除摄像头但 edgeDeviceId 为空,跳过推送配置");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +261,38 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
gbChannelService.add(streamProxy.buildCommonGBChannel());
|
gbChannelService.add(streamProxy.buildCommonGBChannel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 推送配置到 Edge(更新摄像头)
|
||||||
|
String oldEdgeDeviceId = streamProxyInDb.getEdgeDeviceId();
|
||||||
|
String newEdgeDeviceId = streamProxy.getEdgeDeviceId();
|
||||||
|
|
||||||
|
if (oldEdgeDeviceId != null && !oldEdgeDeviceId.isEmpty() &&
|
||||||
|
newEdgeDeviceId != null && !newEdgeDeviceId.isEmpty() &&
|
||||||
|
!oldEdgeDeviceId.equals(newEdgeDeviceId)) {
|
||||||
|
// edgeDeviceId 变更:先推送给老设备(移除),再推送给新设备(添加)
|
||||||
|
try {
|
||||||
|
redisConfigService.writeDeviceAggregatedConfig(oldEdgeDeviceId, "UPDATE");
|
||||||
|
log.info("[StreamProxy] 更新摄像头后推送配置到旧 Edge,oldEdgeDeviceId={}", oldEdgeDeviceId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[StreamProxy] 更新摄像头后推送配置到旧 Edge 失败,oldEdgeDeviceId={}", oldEdgeDeviceId, e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
redisConfigService.writeDeviceAggregatedConfig(newEdgeDeviceId, "UPDATE");
|
||||||
|
log.info("[StreamProxy] 更新摄像头后推送配置到新 Edge,newEdgeDeviceId={}", newEdgeDeviceId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[StreamProxy] 更新摄像头后推送配置到新 Edge 失败,newEdgeDeviceId={}", newEdgeDeviceId, e);
|
||||||
|
}
|
||||||
|
} else if (newEdgeDeviceId != null && !newEdgeDeviceId.isEmpty()) {
|
||||||
|
// edgeDeviceId 未变更或原为空:推送给当前设备
|
||||||
|
try {
|
||||||
|
redisConfigService.writeDeviceAggregatedConfig(newEdgeDeviceId, "UPDATE");
|
||||||
|
log.info("[StreamProxy] 更新摄像头后推送配置到 Edge,edgeDeviceId={}", newEdgeDeviceId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[StreamProxy] 更新摄像头后推送配置失败,edgeDeviceId={}", newEdgeDeviceId, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("[StreamProxy] 更新摄像头但 edgeDeviceId 为空,跳过推送配置");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user