package com.ard.work.inspect.task; import com.ard.common.core.constant.SecurityConstants; import com.ard.common.core.domain.R; import com.ard.common.core.utils.SpringUtils; import com.ard.common.core.web.domain.AjaxResult; import com.ard.system.api.RemoteConfigService; import com.ard.work.api.domian.ArdWell; import com.ard.work.api.domian.CameraCmd; import com.ard.work.api.domian.PtzDto; import com.ard.work.device.camera.service.IArdCameraService; import com.ard.work.inspect.domain.ArdVideoInspectRecord; import com.ard.work.inspect.domain.ArdVideoInspectTask; import com.ard.work.inspect.domain.ArdVideoInspectTaskStep; import com.ard.work.inspect.service.IArdVideoInspectRecordService; import com.ard.work.api.domian.ArdWellGuideCamera; import com.ard.work.point.well.service.IArdWellGuideCameraService; import com.ard.work.point.well.service.IArdWellService; import com.ard.work.sdk.service.CameraSDKService; import com.ard.work.api.domian.Point; import lombok.extern.slf4j.Slf4j; import java.time.LocalTime; import java.util.Date; import java.util.List; @Slf4j public class TaskWrapper implements Runnable { public volatile TaskStatus status = TaskStatus.RUNNING; private final ArdVideoInspectTask task; private ArdVideoInspectTaskStep currentStep; private Date stepStartTime; private final CameraSDKService cameraSDKService = SpringUtils.getBean(CameraSDKService.class); private final IArdCameraService ardCameraService = SpringUtils.getBean(IArdCameraService.class); private final IArdVideoInspectRecordService ardVideoInspectRecordService = SpringUtils.getBean(IArdVideoInspectRecordService.class); private final IArdWellService ardWellService = SpringUtils.getBean(IArdWellService.class); private final IArdWellGuideCameraService ardWellGuideCameraService = SpringUtils.getBean(IArdWellGuideCameraService.class); private final RemoteConfigService remoteConfigService = SpringUtils.getBean(RemoteConfigService.class); public TaskWrapper(ArdVideoInspectTask task) { this.task = task; } @Override public void run() { List steps = task.getArdVideoInspectTaskSteps(); if (steps.isEmpty()) return; while (status != TaskStatus.CANCELED) { // 循环直到任务被取消 for (ArdVideoInspectTaskStep step : steps) { if (status == TaskStatus.CANCELED) break; currentStep = step; stepStartTime = new Date(); // 记录当前步骤的开始时间 log.info("开始执行任务【{}】 的巡检步骤【{}】,时间:{}", step.getTaskId(), step.getId(), stepStartTime); // 执行步骤 executeStep(step); // 等待步骤完成 waitForStepCompletion(step); } } log.info("任务被取消或完成"); } //等待步骤完成 private void waitForStepCompletion(ArdVideoInspectTaskStep step) { while (!isStepCompleted()) { if (status == TaskStatus.CANCELED) { handleCancellation(step); return; } if (status == TaskStatus.PAUSED) { handlePause(step); } try { Thread.sleep(100); // 每 100 毫秒检查一次 } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("任务线程被中断"); return; } } log.info("完成任务【{}】的巡检步骤【{}】", task.getId(), step.getId()); finalizeStep(step); } //处理暂停 private void handlePause(ArdVideoInspectTaskStep step) { log.info("任务【{}】暂停,等待恢复...", task.getId()); synchronized (this) { while (status == TaskStatus.PAUSED) { try { stopRecording(step); wait(); // 暂停任务 } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("任务线程在暂停时被中断"); break; } } } } //处理取消 private void handleCancellation(ArdVideoInspectTaskStep step) { log.info("任务【{}】被取消,停止执行", task.getId()); stopRecording(step); } //处理完成 private void finalizeStep(ArdVideoInspectTaskStep step) { stopRecording(step); //recordStep(step, ""); // 提供空 URL 或根据需要调整 } //执行任务 private void executeStep(ArdVideoInspectTaskStep step) { log.info("执行任务 {} 的步骤 {}", step.getTaskId(), step.getId()); startRecording(step); } //步骤是否完成 private boolean isStepCompleted() { long elapsedMillis = new Date().getTime() - stepStartTime.getTime(); long elapsedSeconds = elapsedMillis / 1000; return elapsedSeconds >= currentStep.getRecordingTime(); } //暂停 public void pause() { this.status = TaskStatus.PAUSED; } //恢复 public void resume() { this.status = TaskStatus.RUNNING; synchronized (this) { stepStartTime = new Date(); log.info("恢复执行任务【{}】 的巡检步骤【{}】,时间:{}", currentStep.getTaskId(), currentStep.getId(), stepStartTime); // 执行步骤 executeStep(currentStep); notifyAll(); // 唤醒所有在等待中的线程 } } //取消 public void cancel() { this.status = TaskStatus.CANCELED; synchronized (this) { notifyAll(); // 唤醒所有在等待中的线程以便退出 } } //是否暂停 public boolean isPaused() { return this.status == TaskStatus.PAUSED; } //是否取消 public boolean isCanceled() { return this.status == TaskStatus.CANCELED; } //开始录像 private void startRecording(ArdVideoInspectTaskStep step) { log.info("步骤 {} 开始 {} 录像", step.getId(), task.getCameraId() + "_" + task.getChanNo()); cameraSDKService.recordStart(new CameraCmd(task.getCameraId(), task.getChanNo()));//录像 try { guideWell(step);//引导 } catch (Exception e) { log.error("引导井异常,停止录像", e); cameraSDKService.recordStop(new CameraCmd(task.getCameraId(), task.getChanNo(), "inspect"));//录像 } } //停止录像 private void stopRecording(ArdVideoInspectTaskStep step) { log.info("步骤 {} 停止 {} 录像", step.getId(), task.getCameraId() + "_" + task.getChanNo()); String url = cameraSDKService.recordStop(new CameraCmd(task.getCameraId(), task.getChanNo(), "inspect")); ArdVideoInspectRecord record = new ArdVideoInspectRecord(); record.setTaskId(task.getId()); record.setStepId(step.getId()); record.setStartTime(stepStartTime); record.setEndTime(new Date()); ArdWell ardWell = ardWellService.selectArdWellById(step.getWellId()); record.setWellName(ardWell.getWellId()); record.setRecordUrl(url); record.setTaskName(task.getTaskName()); ardVideoInspectRecordService.insertArdVideoInspectRecord(record); } //引导井 private void guideWell(ArdVideoInspectTaskStep step) { log.info("控制相机{}通道{}引导井{}", task.getCameraId(), task.getChanNo(), step.getWellId()); //根据井id获取井的坐标 ArdWell ardWell = ardWellService.selectArdWellById(step.getWellId()); if (ardWell == null) { return; } CameraCmd cmd = new CameraCmd(); cmd.setCameraId(task.getCameraId()); //根据日夜配置切换通道 Integer chanNo = switchChanByTime(); task.setChanNo(chanNo); cmd.setChanNo(task.getChanNo()); cmd.setOperator("sys_patrol_inspect"); cmd.setExpired(step.getRecordingTime()); ardCameraService.controlLock(cmd);//锁定 Boolean res; ArdWellGuideCamera guidePTZInfo = isPtzGuide(task.getCameraId(), task.getChanNo(), ardWell.getId()); if (guidePTZInfo != null) { //使用ptz引导 cmd.setPtzDto(new PtzDto(guidePTZInfo.getP(), guidePTZInfo.getT(), guidePTZInfo.getZ())); res = cameraSDKService.setPtz(cmd); log.info("引导方式:ptz引导"); } else { //使用坐标引导 cmd.setTargetPosition(new Point(ardWell.getLongitude(), ardWell.getLatitude(), ardWell.getAltitude())); res = cameraSDKService.guideTargetPosition(cmd); log.info("引导方式:坐标引导"); } log.info("控制相机{}通道{}引导井{}结果{}", task.getCameraId(), task.getChanNo(), step.getWellId(), res); } //判断是否使用ptz引导 private ArdWellGuideCamera isPtzGuide(String cameraId, Integer chanNo, String wellId) { ArdWellGuideCamera ardWellGuideCamera = new ArdWellGuideCamera(); ardWellGuideCamera.setWellId(wellId); ardWellGuideCamera.setCameraId(cameraId); ardWellGuideCamera.setChanNo(chanNo); List ardWellGuideCameras = ardWellGuideCameraService.selectArdWellGuideCameraList(ardWellGuideCamera); if (ardWellGuideCameras.isEmpty()) { return null; } return ardWellGuideCameras.get(0); } // 通道日夜切换 public Integer switchChanByTime() { R result = remoteConfigService.getConfigKey("dayNight"); // 解析时间区间(示例:18:00-06:00) if (result.getCode()==R.SUCCESS) { String timeRange = result.getData(); return checkTimeRange(timeRange); } return 1; } /** * 判断当前时间是否在时间区间内(支持跨天区间,如 18:00-06:00) * * @param timeRange 时间区间字符串,格式 "HH:mm-HH:mm" * @return 1(在区间内) 或 2(不在区间内) */ public static int checkTimeRange(String timeRange) { if (timeRange == null || !timeRange.matches("\\d{2}:\\d{2}-\\d{2}:\\d{2}")) { throw new IllegalArgumentException("时间区间格式无效,应为 HH:mm-HH:mm"); } String[] parts = timeRange.split("-"); LocalTime start = LocalTime.parse(parts[0]); LocalTime end = LocalTime.parse(parts[1]); LocalTime now = LocalTime.now(); if (start.isBefore(end)) { // 普通区间(同一天内,如 09:00-18:00) return (now.isAfter(start) && now.isBefore(end)) ? 1 : 2; } else { // 跨天区间(如 18:00-06:00) return (now.isAfter(start) || now.isBefore(end)) ? 1 : 2; } } }