package com.inspect.nvr.controller;
|
|
|
|
import com.inspect.nvr.aop.TimeTrace;
|
|
import com.inspect.nvr.domain.device.*;
|
|
import com.inspect.nvr.domain.ivs.IvsPosition3dRequest;
|
|
import com.inspect.nvr.domain.ivs.IvsPtzControlRequest;
|
|
import com.inspect.nvr.domain.ivs.IvsPtzControlResponse;
|
|
import com.inspect.nvr.ivs.service.IvsOpenApiService;
|
|
import com.inspect.nvr.security.PtzLockService;
|
|
import com.inspect.nvr.service.IvsCameraService;
|
|
import com.inspect.nvr.utils.StringUtils;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.core.io.InputStreamResource;
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import javax.annotation.Resource;
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
@Slf4j
|
|
@RestController
|
|
public class IvsCameraController {
|
|
|
|
@Resource
|
|
private IvsCameraService ivsCameraService;
|
|
@Resource
|
|
private IvsOpenApiService ivsOpenApiService;
|
|
/** 云台控制权锁:body 携带角色字段的调用按 cameraCode 抢锁,北向机器调用跳过。 */
|
|
@Resource
|
|
private PtzLockService ptzLockService;
|
|
private static final Object captureLock = new Object();
|
|
private static final Object captureLock_pic = new Object();
|
|
/**
|
|
* 查询指定摄像机在对应 NVR 上的预置位列表。
|
|
*/
|
|
@CrossOrigin
|
|
@GetMapping("/device/ptzpresetlist/{cameraCode}/{nvrCode}")
|
|
public ResponseEntity<IvsPresetListView> ptzPresetList(
|
|
@PathVariable("cameraCode") String cameraCode,
|
|
@PathVariable("nvrCode") String nvrCode) {
|
|
log.info("PTZ_PRESET_LIST cameraCode : {}, nvrCode: {}", cameraCode, nvrCode);
|
|
IvsPresetListView ivsPresetListView = ivsOpenApiService.presetList(cameraCode, nvrCode);
|
|
return ResponseEntity
|
|
.ok()
|
|
.body(ivsPresetListView);
|
|
}
|
|
|
|
/**
|
|
* 接收 IVS 云台控制指令,并转发给厂商适配器执行。
|
|
*/
|
|
@CrossOrigin
|
|
@PostMapping({"/device/ptzcontrol"})
|
|
public ResponseEntity<IvsPtzControlResponse> ptzControl( @RequestBody IvsPtzControlRequest param) {
|
|
log.info("Ptz control request param: {}", param);
|
|
// 请求体携带 username 时按 cameraCode 校验控制权;锁被更高优先持有时抛 HTTP 423。
|
|
ptzLockService.checkLock(
|
|
param == null ? null : param.getCameraCode(),
|
|
param == null ? null : param.getUsername(),
|
|
param == null ? null : param.getJoinbrightToken());
|
|
return ResponseEntity.ok().body(ivsOpenApiService.ptzControl(param));
|
|
}
|
|
|
|
@PostMapping({"/v1/device/ptzcontrol"})
|
|
public ResponseEntity<PtzControlResult> ptzControl(@RequestBody PtzControlParam param) {
|
|
log.info("Ptz control request param: {}", param);
|
|
return ResponseEntity
|
|
.ok()
|
|
.body(ivsCameraService.ptzControl(param));
|
|
}
|
|
/**
|
|
* 接收前端视频框选坐标,并按框选方向执行 3D 定位放大或缩小。
|
|
*
|
|
* @param param cameraCode 和 0~255 归一化起止坐标
|
|
* @return 厂商设备的执行结果
|
|
*/
|
|
@CrossOrigin
|
|
@PostMapping("/device/position3d")
|
|
public ResponseEntity<IvsPtzControlResponse> position3d(
|
|
@RequestBody IvsPosition3dRequest param) {
|
|
// 3D 定位与普通云台控制共用同一把 cameraCode 控制权锁。
|
|
ptzLockService.checkLock(
|
|
param == null ? null : param.getCameraCode(),
|
|
param == null ? null : param.getUsername(),
|
|
param == null ? null : param.getJoinbrightToken());
|
|
// 由 IVS 门面按 cameraCode 选择厂商,并在厂商层完成坐标校验和 SDK 调用。
|
|
return ResponseEntity.ok().body(ivsOpenApiService.position3d(param));
|
|
}
|
|
|
|
/**
|
|
* 创建指定摄像机的平台抓图任务。
|
|
*/
|
|
@GetMapping({"/platform/platformSnapshot2/{cameraCode}/{domainCode}"})
|
|
public ResponseEntity<SnapshotCommandResult> platformSnapshot2(
|
|
@PathVariable("cameraCode") String cameraCode,
|
|
@PathVariable("domainCode") String domainCode) {
|
|
log.info("Platform Snapshot cameraCode: {}, domainCode: {}", cameraCode, domainCode);
|
|
return ResponseEntity
|
|
.ok()
|
|
.body(ivsCameraService.platformSnapshot(cameraCode, domainCode, null));
|
|
}
|
|
|
|
/**
|
|
* 按任务或时间范围查询平台抓图结果。
|
|
*/
|
|
@PostMapping({"/platform/snapshotList2"})
|
|
public ResponseEntity<SnapshotInfoListResult> retrieveSnapshotList2( @RequestBody SnapshotInfoListParam param) {
|
|
log.info("Snapshot list request param: {}", param);
|
|
return ResponseEntity
|
|
.ok()
|
|
.body(ivsCameraService.getSnapshotList(param));
|
|
}
|
|
|
|
/**
|
|
* 兼容旧版路径参数格式查询平台抓图结果。
|
|
*/
|
|
@GetMapping("/platform/snapshotlist/{cameraCode}/{domainCode}/{startTime}/{endTime}/{fromIndex}/{toIndex}/{snapType}")
|
|
public ResponseEntity<SnapshotInfoListResult> retrieveLegacySnapshotList(
|
|
@PathVariable("cameraCode") String cameraCode,
|
|
@PathVariable("domainCode") String domainCode,
|
|
@PathVariable("startTime") String startTime,
|
|
@PathVariable("endTime") String endTime,
|
|
@PathVariable("fromIndex") Integer fromIndex,
|
|
@PathVariable("toIndex") Integer toIndex,
|
|
@PathVariable("snapType") Integer snapType) {
|
|
SnapshotInfoListParam.SnapshotDetailInfo detail =
|
|
SnapshotInfoListParam.SnapshotDetailInfo.builder()
|
|
.startTime(startTime)
|
|
.endTime(endTime)
|
|
.fromIndex(fromIndex)
|
|
.toIndex(toIndex)
|
|
.snapType(snapType)
|
|
.build();
|
|
SnapshotInfoListParam request = SnapshotInfoListParam.builder()
|
|
.cameraCode(cameraCode)
|
|
.domainCode(domainCode)
|
|
.snapshotDetailInfo(detail)
|
|
.build();
|
|
return ResponseEntity.ok(ivsCameraService.getSnapshotList(request));
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
* use {@link #capture} instead.
|
|
*/
|
|
@Deprecated
|
|
/**
|
|
* 使用旧版会话标识下载抓图文件,并保留失败重试机制。
|
|
*/
|
|
@GetMapping("/downloadfile_old")
|
|
public ResponseEntity<InputStreamResource> downloadFile( @RequestParam("filesessionid") String fileSessionId) {
|
|
// synchronized (captureLock_pic) {
|
|
log.info("Download File Get Stream, fileSessionId: {}", fileSessionId);
|
|
ByteArrayInputStream byteArrayInputStream = null;
|
|
//添加重试机制
|
|
final int MAX_RETRIES = 20; // 最大重试次数
|
|
boolean success = false;
|
|
int retryCount = 0;
|
|
while (!success && retryCount < MAX_RETRIES) {
|
|
try {
|
|
retryCount++;
|
|
log.info("第{}次尝试抓图", retryCount);
|
|
byteArrayInputStream = ivsCameraService.downloadFile(fileSessionId);
|
|
if (StringUtils.isNotNull(byteArrayInputStream)) {
|
|
success = true;
|
|
} else {
|
|
Thread.sleep(500); // 等待一秒后重试
|
|
log.error("第{}次抓图失败", retryCount);
|
|
}
|
|
} catch (Exception e) {
|
|
log.error("第{}次抓图失败: {}", retryCount, e.getMessage());
|
|
}
|
|
}
|
|
return ResponseEntity
|
|
.ok()
|
|
.body(new InputStreamResource(byteArrayInputStream));
|
|
// }
|
|
}
|
|
|
|
@TimeTrace
|
|
/**
|
|
* 根据临时图片会话标识下载抓图文件。
|
|
*/
|
|
@GetMapping("/downloadfile")
|
|
public ResponseEntity<InputStreamResource> capture( @RequestParam("filesessionid") String fileSessionId) {
|
|
ByteArrayInputStream byteArrayInputStream = ivsCameraService.capture(fileSessionId);
|
|
return ResponseEntity.ok().body(new InputStreamResource(byteArrayInputStream));
|
|
}
|
|
|
|
@TimeTrace
|
|
/**
|
|
* 使用 Digest 认证直接抓取并返回 JPEG 图片。
|
|
*/
|
|
@GetMapping("/digest")
|
|
public ResponseEntity<byte[]> digest( @RequestParam("filesessionid") String fileSessionId) {
|
|
byte[] bytes = ivsCameraService.captureDigest(fileSessionId);
|
|
if (bytes == null || bytes.length == 0) {
|
|
log.error("Digest 抓图失败,filesessionid={}", fileSessionId);
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
|
}
|
|
return ResponseEntity.ok()
|
|
.contentType(MediaType.IMAGE_JPEG)
|
|
.contentLength(bytes.length)
|
|
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"digest.jpg\"")
|
|
.body(bytes);
|
|
}
|
|
}
|