You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

186 lines
8.1 KiB

package com.inspect.nvr.hik.controller;
import com.inspect.nvr.dahua.DahuaIvsVendorService;
import com.inspect.nvr.domain.Infrared.NvrInfo;
import com.inspect.nvr.hik.domain.HikNvrLiveStreamResponse;
import com.inspect.nvr.hik.domain.HikNvrPlaybackErrorResponse;
import com.inspect.nvr.hik.domain.HikNvrPresetListResponse;
import com.inspect.nvr.hik.domain.HikNvrUiActionRequest;
import com.inspect.nvr.hik.domain.HikNvrUiActionResponse;
import com.inspect.nvr.hik.exception.HikNvrPlaybackException;
import com.inspect.nvr.hik.service.HikNvrCredentialService;
import com.inspect.nvr.hik.service.HikNvrLiveStreamService;
import com.inspect.nvr.hik.service.HikNvrUiActionService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@CrossOrigin
@RequestMapping("/hik/nvr/playback/hls/ui/actions")
public class HikNvrUiActionController {
/** 海康云台、预置位和抓图操作服务。 */
@Resource
private HikNvrUiActionService actionService;
/** 海康实时拉流服务。 */
@Resource
private HikNvrLiveStreamService liveStreamService;
/** 按页面选择的 NVR IP 从数据库读取服务端凭据。 */
@Resource
private HikNvrCredentialService credentialService;
/** 大华工作台预置位保存能力。 */
@Resource
private DahuaIvsVendorService dahuaVendorService;
/**
* 接收海康 NVR 云台控制指令。
*/
@PostMapping("/ptz")
/** 代理海康云台动作请求。 */
public ResponseEntity<HikNvrUiActionResponse> ptz(@RequestBody HikNvrUiActionRequest request) {
// 绑定页面所选 NVR 后执行云台动作,避免相同通道号被发送到默认设备。
return ResponseEntity.ok(actionServiceFor(request).ptz(request));
}
/**
* 调用指定通道的海康预置位。
*/
@PostMapping("/preset")
/** 代理海康预置位调用请求。 */
public ResponseEntity<HikNvrUiActionResponse> preset(@RequestBody HikNvrUiActionRequest request) {
// 绑定页面所选 NVR 后执行预置位。
return ResponseEntity.ok(actionServiceFor(request).preset(request));
}
/**
* 保存当前云台位置为指定的海康预置位。
*/
@PostMapping("/preset/create")
/** 代理海康预置位保存请求。 */
public ResponseEntity<HikNvrUiActionResponse> createPreset(@RequestBody HikNvrUiActionRequest request) {
// 先按页面 NVR IP 读取数据库厂商,浏览器不能自行选择 SDK。
NvrInfo nvr = nvrInfoFor(request);
// 大华工作台复用同一路由,由 NetSDK 保存页面指定编号的预置位。
if (isDahua(nvr.getNvrCompany())) {
return ResponseEntity.ok(dahuaVendorService.createPreset(request));
}
// 海康设备继续沿用原有 SDK/ISAPI 保存实现。
return ResponseEntity.ok(actionService.forNvr(nvr).createPreset(request));
}
/**
* 读取指定通道的海康预置位列表。
*/
@GetMapping("/presets")
public ResponseEntity<HikNvrPresetListResponse> listPresets(
@RequestParam("channel") Integer channel,
@RequestParam("nvrIp") String nvrIp) {
// 创建内部请求对象并读取 NVR 当前预置位列表。
HikNvrUiActionRequest request = new HikNvrUiActionRequest();
request.setNvrIp(nvrIp);
request.setChannel(channel);
// 绑定页面所选 NVR 后委托海康 ISAPI 列表读取能力。
return ResponseEntity.ok(actionServiceFor(request).listPresets(request));
}
/**
* 抓取海康 NVR 当前通道的 JPEG 图片。
*/
@PostMapping(value = "/snapshot", produces = MediaType.IMAGE_JPEG_VALUE)
/** 代理海康 JPEG 抓图请求。 */
public ResponseEntity<byte[]> snapshot(@RequestBody HikNvrUiActionRequest request) {
// 绑定页面所选 NVR 后调用海康抓图服务获取 JPEG 字节。
byte[] image = actionServiceFor(request).snapshot(request);
// 从请求中提取通道号用于响应文件名和响应头。
int channel = request == null || request.getChannel() == null ? 0 : request.getChannel();
String filename = "nvr-ch" + channel + "-" + System.currentTimeMillis() + ".jpg";
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_JPEG)
.contentLength(image.length)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
.header("X-Capture-Channel", String.valueOf(channel))
.body(image);
}
/**
* 启动指定通道的实时拉流任务。
*/
@PostMapping("/live/start")
/** 代理实时拉流启动请求。 */
public ResponseEntity<HikNvrLiveStreamResponse> startLive(@RequestBody HikNvrUiActionRequest request) {
// 从页面请求提取设备树完整 cameraCode 和可选码流类型。
String cameraCode = request == null ? null : request.getCameraCode();
Integer streamType = request == null ? null : request.getStreamType();
// 实时服务通过现有 IVS 媒体接口解析 cameraCode 并取得 RTSP 源地址。
return ResponseEntity.ok(liveStreamService.start(cameraCode, streamType));
}
/**
* 停止指定通道的实时拉流任务。
*/
@PostMapping("/live/stop")
/** 代理实时拉流停止请求。 */
public ResponseEntity<HikNvrLiveStreamResponse> stopLive(@RequestBody HikNvrUiActionRequest request) {
// 从页面请求提取要停止的完整 cameraCode。
String cameraCode = request == null ? null : request.getCameraCode();
// 只停止该 cameraCode 对应的流,不影响设备树中的其他摄像机。
return ResponseEntity.ok(liveStreamService.stop(cameraCode));
}
/**
* 查询指定通道的实时拉流状态。
*/
@GetMapping("/live/status")
public ResponseEntity<HikNvrLiveStreamResponse> liveStatus(
@RequestParam("cameraCode") String cameraCode) {
// 按设备树完整 cameraCode 查询唯一实时拉流状态。
return ResponseEntity.ok(liveStreamService.status(cameraCode));
}
/**
* 为页面请求绑定数据库中已启用的目标 NVR。
*
* @param request 页面操作请求,必须包含所选 NVR IP
* @return 绑定数据库目标 NVR 的海康操作服务
*/
private HikNvrUiActionService actionServiceFor(HikNvrUiActionRequest request) {
return actionService.forNvr(nvrInfoFor(request));
}
/** 按页面请求读取并校验数据库中的目标 NVR。 */
private NvrInfo nvrInfoFor(HikNvrUiActionRequest request) {
// 多 NVR 环境必须明确目标 IP,禁止按固定配置或隐式默认设备执行控制。
if (request == null || !hasText(request.getNvrIp())) {
throw HikNvrPlaybackException.badRequest("NVR IP不能为空");
}
// 只允许绑定数据库中已启用的 NVR,页面无法直接提供账号密码。
return credentialService.resolveByIp(request.getNvrIp().trim());
}
/** 判断数据库厂商字段是否表示大华。 */
private boolean isDahua(String vendor) {
return vendor != null
&& (vendor.trim().toUpperCase().contains("DAHUA")
|| vendor.contains("大华"));
}
/**
* 判断页面提交的 NVR IP 是否包含有效文本。
*
* @param value 待检查文本
* @return 非空且不全为空白时返回 true
*/
private boolean hasText(String value) {
// 空文本不能定位数据库 NVR。
return value != null && !value.trim().isEmpty();
}
}