|
@@ -0,0 +1,264 @@
|
|
|
+package com.sooka.sponest.monitor.dahua.utils;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.alibaba.nacos.shaded.com.google.gson.Gson;
|
|
|
+import com.ruoyi.common.core.domain.R;
|
|
|
+import com.sooka.sponest.monitor.camera.domain.CentermonitorTRecorderAlarm;
|
|
|
+import com.sooka.sponest.monitor.camera.mapper.CentermonitorTCamerachannelMapper;
|
|
|
+import com.sooka.sponest.monitor.camera.mapper.CentermonitorTRecorderAlarmMapper;
|
|
|
+import com.sooka.sponest.monitor.camera.mapper.CentermonitorTRecorderMapper;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.annotation.PreDestroy;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.time.Instant;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.*;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class DeviceMonitorManager {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private CentermonitorTRecorderMapper centermonitorTRecorderMapper;
|
|
|
+ @Resource
|
|
|
+ private CentermonitorTRecorderAlarmMapper centermonitorTRecorderAlarmMapper;
|
|
|
+ @Resource
|
|
|
+ private CentermonitorTCamerachannelMapper centermonitorTCamerachannelMapper;
|
|
|
+ // @Value("${sooka.dahua_interface_server.loginIp}")
|
|
|
+// private String loginIp;
|
|
|
+// @Value("${sooka.dahua_interface_server.loginPort}")
|
|
|
+// private String loginPort;
|
|
|
+// @Value("${sooka.dahua_interface_server.userName}")
|
|
|
+// private String userName;
|
|
|
+// @Value("${sooka.dahua_interface_server.userPwd}")
|
|
|
+// private String userPwd;
|
|
|
+ private String loginIp = "10.53.0.35";
|
|
|
+ private String loginPort = "7901";
|
|
|
+ private String userName = "system";
|
|
|
+ private String userPwd = "Admin123";
|
|
|
+ //按组织获取设备详细信息
|
|
|
+ public static final String ACTION = "/videoService/devicesManager/devicesInfo";
|
|
|
+ // 使用线程安全的Map存储所有设备的监控任务
|
|
|
+ private final ConcurrentMap<String, ScheduledFuture<?>> deviceTasks = new ConcurrentHashMap<>();
|
|
|
+ // 定时任务线程池
|
|
|
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(20);
|
|
|
+ // 存储设备初始状态
|
|
|
+ private final ConcurrentMap<String, String> deviceInitialStatus = new ConcurrentHashMap<>();
|
|
|
+ // 存储设备状态检查开始时间
|
|
|
+ private final ConcurrentMap<String, Long> monitoringStartTimes = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动设备开机监控任务(如果不存在)
|
|
|
+ *
|
|
|
+ * @param deviceCode 设备编码
|
|
|
+ * @param onlineTime 上线时间
|
|
|
+ * @return true表示成功启动任务,false表示任务已存在
|
|
|
+ */
|
|
|
+ public boolean startDeviceMonitoringIfAbsent(String deviceCode, Date onlineTime) {
|
|
|
+ // 如果已有监控任务,直接返回false
|
|
|
+ if (deviceTasks.containsKey(deviceCode)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 创建监控任务
|
|
|
+ Runnable monitorTask = () -> {
|
|
|
+ checkDeviceStatus(deviceCode, onlineTime);
|
|
|
+ };
|
|
|
+ // 每分钟检查一次
|
|
|
+ ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(
|
|
|
+ monitorTask, 0, 1, TimeUnit.MINUTES);
|
|
|
+ // 原子性操作,防止并发问题
|
|
|
+ ScheduledFuture<?> existing = deviceTasks.putIfAbsent(deviceCode, future);
|
|
|
+ if (existing != null) {
|
|
|
+ // 如果已经有其他线程创建了任务,取消当前创建的任务
|
|
|
+ future.cancel(false);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查设备是否有监控任务
|
|
|
+ *
|
|
|
+ * @param deviceCode 设备编码
|
|
|
+ * @return 是否已有监控任务
|
|
|
+ */
|
|
|
+ public boolean hasMonitoringTask(String deviceCode) {
|
|
|
+ return deviceTasks.containsKey(deviceCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停止设备的监控任务
|
|
|
+ *
|
|
|
+ * @param deviceCode 设备编码
|
|
|
+ */
|
|
|
+ public void stopDeviceMonitoring(String deviceCode) {
|
|
|
+ ScheduledFuture<?> future = deviceTasks.remove(deviceCode);
|
|
|
+ if (future != null) {
|
|
|
+ future.cancel(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查设备状态
|
|
|
+ */
|
|
|
+ private void checkDeviceStatus(String deviceCode, Date onlineTime) {
|
|
|
+ // 这里实现你的状态检查逻辑
|
|
|
+ Date now = new Date();
|
|
|
+ long offlineMinutes = (now.getTime() - onlineTime.getTime()) / (60 * 1000);
|
|
|
+ if (offlineMinutes >= 5) {
|
|
|
+ System.out.println("设备 " + deviceCode + " 已在线超过5分钟,触发报警!");
|
|
|
+ triggerAlarm(deviceCode, onlineTime,"设备开机,未执法告警");
|
|
|
+ stopDeviceMonitoring(deviceCode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 触发报警
|
|
|
+ */
|
|
|
+ private void triggerAlarm(String deviceCode, Date onlineTime,String alarmName) {
|
|
|
+ String cameraCode = centermonitorTCamerachannelMapper.selectChannelCodeByDeviceCode(deviceCode);
|
|
|
+ CentermonitorTRecorderAlarm saveVo = new CentermonitorTRecorderAlarm();
|
|
|
+ saveVo.setCameraCode(cameraCode);
|
|
|
+ saveVo.setAlarmName(alarmName);
|
|
|
+ saveVo.setAlarmTime(onlineTime);
|
|
|
+ centermonitorTRecorderAlarmMapper.insertCentermonitorTRecorderAlarm(saveVo);
|
|
|
+ System.out.println("设备编码:" + deviceCode + "摄像头编码:" + cameraCode + "执行报警操作.");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动设备关机监控任务
|
|
|
+ *
|
|
|
+ * @param deviceCode 摄像头编码
|
|
|
+ * @param currentStatus 当前设备状态
|
|
|
+ * @return true表示成功启动任务,false表示任务已存在
|
|
|
+ */
|
|
|
+ public boolean startShutdownMonitoring(String deviceCode, String currentStatus) {
|
|
|
+ if (deviceTasks.containsKey(deviceCode)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 记录初始状态
|
|
|
+ deviceInitialStatus.put(deviceCode, currentStatus);
|
|
|
+ // 记录开始关机时间
|
|
|
+ monitoringStartTimes.put(deviceCode, System.currentTimeMillis());
|
|
|
+
|
|
|
+ Runnable shutdownMonitorTask = () -> {
|
|
|
+ checkDeviceStatusChange(deviceCode);
|
|
|
+ };
|
|
|
+ ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(
|
|
|
+ shutdownMonitorTask, 0, 1, TimeUnit.MINUTES);
|
|
|
+
|
|
|
+ ScheduledFuture<?> existing = deviceTasks.putIfAbsent(deviceCode, future);
|
|
|
+ if (existing != null) {
|
|
|
+ future.cancel(false);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查设备状态变化
|
|
|
+ */
|
|
|
+ private void checkDeviceStatusChange(String deviceCode) {
|
|
|
+ // 1. 状态变化检查
|
|
|
+ JSONObject jsonObject = queryDeviceStatus(deviceCode);
|
|
|
+ String initialStatus = deviceInitialStatus.get(deviceCode);
|
|
|
+ if (jsonObject.get("status").equals(initialStatus)) {
|
|
|
+ System.out.println("设备已经关机,停止当前监控任务");
|
|
|
+ stopDeviceMonitoring(deviceCode);
|
|
|
+ }
|
|
|
+ // 2. 强制检查时间阈值(即使状态已变化也记录时长)
|
|
|
+ Long startTime = monitoringStartTimes.get(deviceCode);
|
|
|
+ if (startTime == null) return;
|
|
|
+
|
|
|
+ long elapsedMinutes = (System.currentTimeMillis() - startTime) / (60 * 1000);
|
|
|
+ if (elapsedMinutes >= 5) {
|
|
|
+ triggerAlarm(deviceCode, new Date(),"设备执法完成未关机,告警");
|
|
|
+ System.out.println("设备 " + deviceCode + " 触发关机按钮,超过5分钟内状态未变化,触发报警");
|
|
|
+ stopDeviceMonitoring(deviceCode);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询设备状态(需要根据实际情况实现)
|
|
|
+ */
|
|
|
+ private JSONObject queryDeviceStatus(String deviceCode) {
|
|
|
+ JSONObject jsonObject = new JSONObject();
|
|
|
+ try {
|
|
|
+ jsonObject = getJsonObject(deviceCode, "2");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("根据通道编码(摄像头编码)获取大华设备info信息失败:", e);
|
|
|
+ }
|
|
|
+ return jsonObject; // 实际应该调用设备服务查询状态
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据设备编码或者摄像头编码获取大华设备info信息
|
|
|
+ *
|
|
|
+ * @param code (摄像头编码或者设备编码)
|
|
|
+ * @param type (1:摄像头编码,2:设备编码)
|
|
|
+ * @return
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ public JSONObject getJsonObject(String code, String type) {
|
|
|
+ R<?> result = null;
|
|
|
+ try {
|
|
|
+ result = HttpTestUtils.getToken(loginIp, Integer.parseInt(loginPort), userName, userPwd);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("获取token失败,请检查配置信息:", JSONObject.parseObject(result.getMsg()));
|
|
|
+ }
|
|
|
+ String token = result.getData().toString();
|
|
|
+ Map<String, String> content = new HashMap<>();
|
|
|
+ content.put("orgCode", "11033445593778368");
|
|
|
+ String response = HttpTestUtils.httpRequest(HttpEnum.POST, loginIp, Integer.parseInt(loginPort), ACTION, token, new Gson().toJson(content));
|
|
|
+ Map<String, Object> rsp = new Gson().fromJson(response, Map.class);
|
|
|
+ List<Map<String, Object>> arr = (List<Map<String, Object>>) rsp.get("devices");
|
|
|
+ JSONObject json = new JSONObject();
|
|
|
+ final String deviceCode = type.equals("1")
|
|
|
+ ? centermonitorTRecorderMapper.selectCameraCodeByChannelCode(code)
|
|
|
+ : code;
|
|
|
+ arr.stream().filter(item -> item.get("code").equals(deviceCode)).findFirst().ifPresent(item -> {
|
|
|
+ json.put("code", item.get("code"));
|
|
|
+ json.put("status", item.get("status"));
|
|
|
+ json.put("statusTime", item.get("statusTime"));
|
|
|
+ });
|
|
|
+ return json;
|
|
|
+ }
|
|
|
+
|
|
|
+ //时间戳格式化为年月日时分秒
|
|
|
+ public String formatStatusTime(Map<String, Object> var) {
|
|
|
+ // 定义输出格式
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ // 从Map中获取double类型时间戳
|
|
|
+ double timestamp = (Double) var.get("statusTime");
|
|
|
+ // 将double转换为long(毫秒时间戳)
|
|
|
+ long milliseconds = (long) timestamp;
|
|
|
+ // 转换为Instant
|
|
|
+ Instant instant = Instant.ofEpochMilli(milliseconds);
|
|
|
+ // 转换为本地时区的日期时间(可根据需要修改时区)
|
|
|
+ LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
|
|
+ return dateTime.format(formatter);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 应用关闭时清理资源
|
|
|
+ */
|
|
|
+ @PreDestroy
|
|
|
+ public void shutdown() {
|
|
|
+ scheduler.shutdownNow();
|
|
|
+ deviceTasks.clear();
|
|
|
+ deviceInitialStatus.clear();
|
|
|
+ monitoringStartTimes.clear();
|
|
|
+ }
|
|
|
+}
|