package com.ard.work.sdk.zlxd.service;
|
|
import com.ard.common.core.constant.CacheConstants;
|
import com.ard.common.core.constant.SecurityConstants;
|
import com.ard.common.core.domain.R;
|
import com.ard.common.core.exception.ServiceException;
|
import com.ard.common.core.exception.device.CameraSDKException;
|
import com.ard.common.core.utils.StringUtils;
|
import com.ard.common.core.utils.file.FileMultipartFile;
|
import com.ard.common.core.utils.file.MimeTypeUtils;
|
import com.ard.common.redis.service.RedisService;
|
import com.ard.system.api.RemoteFileService;
|
import com.ard.system.api.domain.SysFile;
|
import com.ard.vtdu.api.RemoteArdVtduService;
|
import com.ard.vtdu.api.domian.ArdVtdu;
|
import com.ard.work.api.domian.*;
|
import com.ard.work.device.camera.domain.PtzParamDTO;
|
import com.ard.work.device.camera.service.IArdCameraPtzScopeService;
|
import com.ard.work.event.LoginEvent;
|
import com.ard.work.sdk.dh.common.Base64;
|
import com.ard.work.sdk.model.DeviceCFG;
|
import com.ard.work.sdk.model.PtzScope;
|
import com.ard.work.sdk.service.CameraSDK;
|
import com.ard.work.sdk.zlxd.cache.RecordSessionCache;
|
import com.ard.work.sdk.zlxd.netty.message.parameters.CameraParameters;
|
import com.ard.work.sdk.zlxd.netty.message.request.*;
|
import com.ard.work.sdk.zlxd.netty.message.response.*;
|
import com.ard.work.sdk.zlxd.netty.tcp.NettyTcpClient;
|
import com.ard.work.sdk.zlxd.utils.PTZ3DLocationConverter;
|
import com.ard.work.sdk.zlxd.utils.XmlUtils;
|
import com.ard.work.utils.gis.GisUtil;
|
import com.galaxy.sdk.RecordSdk;
|
import com.galaxy.sdk.SDKConfig;
|
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.http.MediaType;
|
import org.springframework.stereotype.Service;
|
import org.springframework.web.multipart.MultipartFile;
|
import java.io.File;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.nio.charset.Charset;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
/**
|
* 中林信达sdk策略
|
*
|
* @author 刘苏义
|
* @since 2023-11-07
|
*/
|
@Service("zlxdSDK")
|
@Slf4j
|
public class ZlxdSDK implements CameraSDK {
|
|
@Value("${sdk.tempDir}")
|
private String tempDir;
|
|
private RecordSdk recordSdk;
|
@Resource
|
private RemoteFileService remoteFileService;
|
@Resource
|
private RemoteArdVtduService remoteArdVtduService;
|
@Resource
|
private NettyTcpClient nettyTcpClient;
|
@Resource
|
private ApplicationEventPublisher eventPublisher;
|
@Resource
|
private RedisService redisService;
|
@Resource
|
private IArdCameraPtzScopeService ardCameraPtzScopeService;
|
@PostConstruct
|
private void initSdk() {
|
recordSdk = RecordSdk.getInstance();
|
SDKConfig config = new SDKConfig();
|
String saveDir = tempDir + File.separator + "record";
|
config.setStoragePath(saveDir);
|
config.setEnableAutoRetry(true);
|
config.setMaxRetryCount(3);
|
config.setMaxStorageSize(5L * 1024 * 1024 * 1024); //最大存储5G
|
config.setPerFileMaxSize(1L * 1024 * 1024 * 1024);
|
int ret = recordSdk.init(config);
|
if (ret < 0) {
|
System.out.println("SDK初始化失败");
|
}
|
System.out.println("SDK初始化成功(预热已完成)");
|
System.out.println("SDK实例: " + recordSdk + ", 类加载器: " + recordSdk.getClass().getClassLoader());
|
}
|
|
private final AtomicInteger SEQ = new AtomicInteger(1);
|
|
// 安全的序列号生成
|
private synchronized int getSafeSeq() {
|
int seq = SEQ.getAndIncrement();
|
// 检测并重置回绕
|
if (seq >= Integer.MAX_VALUE - 1000) {
|
SEQ.set(1);
|
log.warn("序列号即将达到上限,已重置");
|
}
|
return seq;
|
}
|
|
/**
|
* TCP+XML设备登录
|
*
|
* @param camera 设备信息
|
* @return 登录ID,失败返回 -1
|
* @throws Exception 网络或解析异常由上层处理
|
*/
|
@Override
|
public void login(ArdCamera camera) {
|
String ip = camera.getIp();
|
Integer port = camera.getPort();
|
|
// 建立TCP连接
|
boolean connected = nettyTcpClient.connectCamera(camera);
|
if (!connected) {
|
log.error("TCP连接建立失败: {}:{}", ip, port);
|
camera.setState("0");
|
camera.setChanNum(0);
|
camera.setLoginId(-1L);
|
eventPublisher.publishEvent(new LoginEvent(camera));
|
return;
|
}
|
log.debug("TCP连接建立成功,准备发送登录请求");
|
|
// 1️⃣ 生成序列号
|
int preLoginSeq = getSafeSeq();
|
|
// 2️⃣ 预登录获取盐
|
String preLoginResp = nettyTcpClient.sendXmlAndWait(camera.getId(), new PreLoginRequest(preLoginSeq));
|
PreLoginResponse preRes = XmlUtils.fromXml(preLoginResp, PreLoginResponse.class);
|
|
if (preRes.getResult().getCode() != 0) {
|
camera.setLoginId(-1L);
|
camera.setState("0");
|
Integer errorCode = preRes.getResult().getCode();
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设备[{}:{}]预登录失败,错误码:{},原因:{}", ip, port, errorCode, errorDesc);
|
return;
|
}
|
|
// 3️⃣ 登录
|
int loginSeq = getSafeSeq();
|
String loginResp = nettyTcpClient.sendXmlAndWait(camera.getId(),
|
new LoginRequest(camera, preRes.getParameters().getSessionID(),
|
preRes.getParameters().getChallenge(), loginSeq));
|
LoginResponse res = XmlUtils.fromXml(loginResp, LoginResponse.class);
|
if (res.getResult().getCode() != 0) {
|
// 登录失败处理
|
handleLoginFail(camera, res);
|
} else {
|
// 登录成功处理
|
handleLoginSuccess(camera, res);
|
}
|
// 登录成功
|
log.debug("TCP+XML设备登录成功 [{}:{}]", ip, port);
|
// 发布登录事件
|
eventPublisher.publishEvent(new LoginEvent(camera));
|
}
|
|
/**
|
* 登录失败处理
|
*/
|
private void handleLoginFail(ArdCamera camera, LoginResponse res) {
|
camera.setChanNum(0);
|
camera.setLoginId(-1L);
|
camera.setState("0");
|
camera.setChannelList(null);
|
Integer errorCode = res.getResult().getCode();
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设备[{}:{}]登录失败,错误码:{},原因:{}", camera.getIp(), camera.getPort(), errorCode, errorDesc);
|
}
|
|
/**
|
* 登录成功处理
|
*/
|
private void handleLoginSuccess(ArdCamera camera, LoginResponse res) {
|
camera.setState("1");
|
camera.setChanNum(2);
|
Long loginId = System.currentTimeMillis(); // 可以用时间戳或其他唯一ID
|
camera.setLoginId(loginId);
|
String sessionId = res.getHeader().getSessionId();
|
SessionCache.put(camera.getId(), sessionId);
|
// 获取最新通道
|
List<ArdChannel> channels = getChannels(camera);
|
if (!channels.isEmpty()) {
|
camera.setChannelList(channels);
|
}
|
log.info("设备登录成功 [{}:{}]", camera.getIp(), camera.getPort());
|
}
|
|
@Override
|
public void logout(String cameraId) {
|
nettyTcpClient.disconnectCamera(cameraId);
|
}
|
|
// 建议按 cameraId 维度保存失败次数
|
private final ConcurrentHashMap<String, AtomicInteger> offlineFailCountMap = new ConcurrentHashMap<>();
|
private static final int MAX_FAIL_COUNT = 2;
|
|
@Override
|
public Boolean isOnLine(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
AtomicInteger failCount = offlineFailCountMap.computeIfAbsent(cameraId, k -> new AtomicInteger(0));
|
try {
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
GetDeviceStateRequest request = new GetDeviceStateRequest("", seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetDeviceStateResponse res = XmlUtils.fromXml(response, GetDeviceStateResponse.class);
|
if (res != null && res.getResult() != null && res.getResult().getCode() == 0) {
|
// ✅ 成功:清零失败计数
|
failCount.set(0);
|
return true;
|
}
|
} catch (Exception e) {
|
log.error("设备在线状态查询异常:{}", e.getMessage());
|
int count = failCount.incrementAndGet();
|
log.warn("设备 [{}] 在线检测异常,第 {} 次", cameraId, count);
|
// ⭐ 关键:3 次以内,仍认为在线
|
if (count <= MAX_FAIL_COUNT) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
@Override
|
public Boolean pTZControl(CameraCmd cameraCmd) {
|
String cameraId = cameraCmd.getCameraId();
|
Integer chanNo = cameraCmd.getChanNo();
|
boolean enable = cameraCmd.isEnable();
|
Integer speed = cameraCmd.getSpeed();
|
Integer code = cameraCmd.getCode();
|
|
try {
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
if (sessionId == null) {
|
log.warn("【{}】未找到有效会话,PTZ控制失败", cameraId);
|
return false;
|
}
|
|
String cmd = "";
|
String param = "";
|
|
// 1️⃣ 指令映射
|
switch (code) {
|
case 1:
|
cmd = "TUPL";
|
break;
|
case 2:
|
cmd = "TU";
|
break;
|
case 3:
|
cmd = "TUPR";
|
break;
|
case 4:
|
cmd = "PL";
|
break;
|
case 5:
|
break;
|
case 6:
|
cmd = "PR";
|
break;
|
case 7:
|
cmd = "TDPL";
|
break;
|
case 8:
|
cmd = "TD";
|
break;
|
case 9:
|
cmd = "TDPR";
|
break;
|
case 10:
|
cmd = "ZIN";
|
break;
|
case 11:
|
cmd = "ZOUT";
|
break;
|
case 12:
|
cmd = "FN";
|
break;
|
case 13:
|
cmd = "FR";
|
break;
|
case 14:
|
cmd = "IO";
|
break;
|
case 15:
|
cmd = "IC";
|
break;
|
case 16:
|
cmd = "WP_ON";
|
break;
|
default:
|
cmd = "STOP";
|
break;
|
}
|
|
// 2️⃣ 关闭动作处理
|
if (!enable) {
|
param = cmd;
|
cmd = "STOP";
|
}
|
|
// 3️⃣ 速度映射
|
int bizSpeed = ((speed - 1) * 31) + 1;
|
|
log.debug("【{}】发送PTZ指令: {} 参数:{} 速度:{}", cameraId, cmd, param, bizSpeed);
|
|
ControlPTZRequest request = new ControlPTZRequest(chanNo, cmd, param, bizSpeed, seq, sessionId);
|
|
// 4️⃣ 发送请求并解析响应
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
ControlPTZResponse res = XmlUtils.fromXml(response, ControlPTZResponse.class);
|
|
if (res != null && res.getResult() != null && res.getResult().getCode() == 0) {
|
log.debug("【{}】PTZ指令发送成功: {} 参数:{} 速度:{}", cameraId, cmd, param, bizSpeed);
|
return true;
|
} else {
|
Integer errorCode = (res != null && res.getResult() != null) ? res.getResult().getCode() : null;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】PTZ控制失败,错误码:{} 原因:{}", cameraId, errorCode, errorDesc);
|
return false;
|
}
|
} catch (Exception e) {
|
log.error("【{}】PTZ控制异常:{}", cameraId, e.getMessage());
|
return false;
|
}
|
}
|
|
|
/**
|
* 设置聚焦值
|
*
|
* @param cmd 相机命令
|
* @return true 成功,false 失败
|
*/
|
@Override
|
public Boolean setFocusPos(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
int chanNo = cmd.getChanNo();
|
Integer focus = cmd.getDwFocusPos();
|
|
try {
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 1️⃣ 获取当前云台信息
|
GetPtzInfoRequest getPtzInfoRequest = new GetPtzInfoRequest(chanNo, seq, sessionId);
|
String getPtzInfoXml = nettyTcpClient.sendXmlAndWait(cameraId, getPtzInfoRequest);
|
GetPtzInfoResponse getPtzInfoRes = XmlUtils.fromXml(getPtzInfoXml, GetPtzInfoResponse.class);
|
|
if (getPtzInfoRes == null || getPtzInfoRes.getResult() == null || getPtzInfoRes.getResult().getCode() != 0) {
|
log.warn("【{}】获取当前云台信息失败", cameraId);
|
return false;
|
}
|
|
Float pan = getPtzInfoRes.getParameters().getPanAngle();
|
Float tilt = getPtzInfoRes.getParameters().getTiltAngle();
|
Float zoom = getPtzInfoRes.getParameters().getZoom();
|
|
// 2️⃣ 设置绝对位置,包括聚焦值
|
SetAbsLocationZoomRequest request = new SetAbsLocationZoomRequest(chanNo, pan, tilt, zoom, focus, seq,
|
sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPtzInfoResponse res = XmlUtils.fromXml(response, GetPtzInfoResponse.class);
|
|
if (res != null && res.getResult() != null && res.getResult().getCode() == 0) {
|
return true;
|
} else {
|
Integer errorCode = res != null && res.getResult() != null ? res.getResult().getCode() : null;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】设置聚焦值失败,错误码:{},原因:{}", cameraId, errorCode, errorDesc);
|
return false;
|
}
|
|
} catch (Exception e) {
|
log.error("【{}】设置聚焦值异常: {}", cameraId, e.getMessage(), e);
|
return false;
|
}
|
}
|
|
|
@Override
|
public Integer getFocusPos(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
int chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
GetPtzInfoRequest request = new GetPtzInfoRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPtzInfoResponse res = XmlUtils.fromXml(response, GetPtzInfoResponse.class);
|
|
if (res != null && res.getResult() != null && res.getResult().getCode() == 0) {
|
Float focus = res.getParameters().getFocus();
|
return focus != null ? focus.intValue() : null;
|
}
|
|
} catch (Exception e) {
|
log.error("获取聚焦值异常: {}", e.getMessage());
|
}
|
return null;
|
}
|
|
|
@Override
|
public Boolean setPreset(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"设置预置点功能暂不支持"
|
);
|
}
|
|
@Override
|
public Boolean gotoPreset(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"调用预置位功能暂不支持"
|
);
|
}
|
|
@Override
|
public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
GetPuImageEncodeParaInfoRequest request = new GetPuImageEncodeParaInfoRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPuImageEncodeParaInfoResponse res = XmlUtils.fromXml(response, GetPuImageEncodeParaInfoResponse.class);
|
if (res.getParameters() != null && !res.getParameters().getImageEncodeParas().isEmpty()) {
|
String videoBitrate = res.getParameters().getImageEncodeParas().get(0).getBitRate().toString();
|
String resolution = res.getParameters().getImageEncodeParas().get(0).getImageSize();
|
String videoEncType = res.getParameters().getImageEncodeParas().get(0).getEncodeMode();
|
String nFrameRate = res.getParameters().getImageEncodeParas().get(0).getFrameRate().toString();
|
VideoCompressionCfg videoCompressionCfg = new VideoCompressionCfg();
|
videoCompressionCfg.setVideoBitrate(videoBitrate);//比特率
|
videoCompressionCfg.setResolution(resolution);//分辨率
|
videoCompressionCfg.setVideoEncType(videoEncType);//编码
|
videoCompressionCfg.setFrameRate(nFrameRate);//帧率
|
return videoCompressionCfg;
|
}
|
return null;
|
}
|
|
@Override
|
public Boolean controlDefog(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 光学除雾0: 关闭; 1: 黑白; 2: 彩色; 3: 自适应; 默认值:0.
|
Integer enable = cmd.isEnable() ? 1 : 0;
|
|
// 获取前端设备图像参数
|
GetGeneralCameraParaRequest getRequest = new GetGeneralCameraParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, getRequest);
|
GetGeneralCameraParaResponse getRes = XmlUtils.fromXml(response, GetGeneralCameraParaResponse.class);
|
|
if (getRes == null || getRes.getResult() == null || getRes.getResult().getCode() != 0) {
|
int errorCode = getRes != null && getRes.getResult() != null ? getRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取前端设备图像参数失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
// 修改聚焦模式
|
CameraParameters parameters = getRes.getParameters();
|
parameters.setOpticalDefog(enable);
|
|
// 发送设置请求
|
seq = getSafeSeq();
|
SetGeneralCameraParaRequest setRequest = new SetGeneralCameraParaRequest(parameters, seq, sessionId);
|
response = nettyTcpClient.sendXmlAndWait(cameraId, setRequest);
|
SetGeneralCameraParaResponse setRes = XmlUtils.fromXml(response, SetGeneralCameraParaResponse.class);
|
|
if (setRes == null || setRes.getResult() == null || setRes.getResult().getCode() != 0) {
|
int errorCode = setRes != null && setRes.getResult() != null ? setRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置透雾失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
log.debug("设置透雾成功");
|
return true;
|
}
|
|
@Override
|
public Boolean isEnableDefog(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 获取前端设备参数
|
GetGeneralCameraParaRequest request = new GetGeneralCameraParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetGeneralCameraParaResponse res = XmlUtils.fromXml(response, GetGeneralCameraParaResponse.class);
|
|
if (res == null || res.getResult() == null || res.getResult().getCode() != 0) {
|
int errorCode = res != null && res.getResult() != null ? res.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取聚焦模式失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null; // 或 false,根据你的业务逻辑
|
}
|
|
// 0-自动;1-单次;2-手动
|
int mode = res.getParameters().getOpticalDefog();
|
|
//光学除雾0: 关闭; 1: 黑白; 2: 彩色; 3: 自适应; 默认值:0.
|
return mode != 0;
|
}
|
|
@Override
|
public Boolean controlInfrared(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 1: 自动; 2: 黑白; 3: 彩色; 4: 报警触发; 5: 定时;
|
Integer enable = cmd.isEnable() ? 2 : 3;
|
|
// 获取前端设备图像参数
|
GetGeneralCameraParaRequest getRequest = new GetGeneralCameraParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, getRequest);
|
GetGeneralCameraParaResponse getRes = XmlUtils.fromXml(response, GetGeneralCameraParaResponse.class);
|
|
if (getRes == null || getRes.getResult() == null || getRes.getResult().getCode() != 0) {
|
int errorCode = getRes != null && getRes.getResult() != null ? getRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取前端设备图像参数失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
// 修改日夜模式
|
CameraParameters parameters = getRes.getParameters();
|
parameters.setDayNightMode(enable);
|
|
// 发送设置请求
|
seq = getSafeSeq();
|
SetGeneralCameraParaRequest setRequest = new SetGeneralCameraParaRequest(parameters, seq, sessionId);
|
response = nettyTcpClient.sendXmlAndWait(cameraId, setRequest);
|
SetGeneralCameraParaResponse setRes = XmlUtils.fromXml(response, SetGeneralCameraParaResponse.class);
|
|
if (setRes == null || setRes.getResult() == null || setRes.getResult().getCode() != 0) {
|
int errorCode = setRes != null && setRes.getResult() != null ? setRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置透雾失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
log.debug("设置透雾成功");
|
return true;
|
}
|
|
|
@Override
|
public Boolean isEnableInfrared(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 获取前端设备参数
|
GetGeneralCameraParaRequest request = new GetGeneralCameraParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetGeneralCameraParaResponse res = XmlUtils.fromXml(response, GetGeneralCameraParaResponse.class);
|
|
if (res == null || res.getResult() == null || res.getResult().getCode() != 0) {
|
int errorCode = res != null && res.getResult() != null ? res.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取聚焦模式失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null; // 或 false,根据你的业务逻辑
|
}
|
|
// 日夜切换, 2: 黑白; 3: 彩色;
|
int mode = res.getParameters().getDayNightMode();
|
|
//日夜切换, 1: 自动; 2: 黑白; 3: 彩色; 4: 报警触发; 5: 定时;
|
return mode != 3;
|
}
|
|
|
@Override
|
public Boolean controlFocusMode(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 聚焦模式:0-自动;1-单次;2-手动
|
Integer enable = cmd.isEnable() ? 2 : 0;
|
|
// 获取前端设备图像参数
|
GetGeneralCameraParaRequest getRequest = new GetGeneralCameraParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, getRequest);
|
GetGeneralCameraParaResponse getRes = XmlUtils.fromXml(response, GetGeneralCameraParaResponse.class);
|
|
if (getRes == null || getRes.getResult() == null || getRes.getResult().getCode() != 0) {
|
int errorCode = getRes != null && getRes.getResult() != null ? getRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取前端设备图像参数失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
// 修改聚焦模式
|
CameraParameters parameters = getRes.getParameters();
|
parameters.setFocusMode(enable);
|
|
// 发送设置请求
|
seq = getSafeSeq();
|
SetGeneralCameraParaRequest setRequest = new SetGeneralCameraParaRequest(parameters, seq, sessionId);
|
response = nettyTcpClient.sendXmlAndWait(cameraId, setRequest);
|
SetGeneralCameraParaResponse setRes = XmlUtils.fromXml(response, SetGeneralCameraParaResponse.class);
|
|
if (setRes == null || setRes.getResult() == null || setRes.getResult().getCode() != 0) {
|
int errorCode = setRes != null && setRes.getResult() != null ? setRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置聚焦模式失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
log.debug("设置聚焦模式成功");
|
return true;
|
}
|
|
|
@Override
|
public Boolean getFocusMode(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 获取前端设备参数
|
GetGeneralCameraParaRequest request = new GetGeneralCameraParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetGeneralCameraParaResponse res = XmlUtils.fromXml(response, GetGeneralCameraParaResponse.class);
|
|
if (res == null || res.getResult() == null || res.getResult().getCode() != 0) {
|
int errorCode = res != null && res.getResult() != null ? res.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取聚焦模式失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null; // 或 false,根据你的业务逻辑
|
}
|
|
// 0-自动;1-单次;2-手动
|
int mode = res.getParameters().getFocusMode();
|
|
// 这里用 Boolean 返回:true=手动,false=自动
|
return mode == 2;
|
}
|
|
|
@Override
|
public Boolean controlPTHeater(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public Boolean controlCameraDeicing(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public Boolean getPTZLockInfo(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 获取前端设备参数
|
GetPuLockRequest request = new GetPuLockRequest(seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPuLockResponse res = XmlUtils.fromXml(response, GetPuLockResponse.class);
|
|
if (res == null || res.getResult() == null || res.getResult().getCode() != 0) {
|
int errorCode = res != null && res.getResult() != null ? res.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取聚焦模式失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null; // 或 false,根据你的业务逻辑
|
}
|
|
//0:未锁定;1:锁定
|
int mode = res.getParameters().getPuLock();
|
|
// 这里用 Boolean 返回:true=锁定,false=未锁定
|
return mode == 1;
|
}
|
|
@Override
|
public Boolean setPTZLockInfo(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 0: 解锁; 1: 锁定;
|
Integer enable = cmd.isEnable() ? 1 : 0;
|
|
// 设置云台锁定
|
SetPuLockRequest request = new SetPuLockRequest(enable, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
SetPuLockResponse getRes = XmlUtils.fromXml(response, SetPuLockResponse.class);
|
// 没返回 → 失败
|
if (getRes == null || getRes.getResult() == null) {
|
log.warn("云台锁定:设备未返回结果");
|
return false;
|
}
|
int code = getRes.getResult().getCode();
|
if (code == 0) {
|
return true;
|
} else {
|
int errorCode = getRes != null && getRes.getResult() != null ? getRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置云台锁定参数失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
}
|
|
@Override
|
public byte[] localCapture(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public String picCutCate(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String type = cmd.getType() == null ? "other" : cmd.getType();
|
String bucketName = cmd.getBucketName() == null ? "pic" : cmd.getBucketName();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
|
// 获取前端设备图像参数
|
GetSnapPicSyncRequest getRequest = new GetSnapPicSyncRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, getRequest);
|
GetSnapPicSyncResponse getRes = XmlUtils.fromXml(response, GetSnapPicSyncResponse.class);
|
|
if (getRes == null || getRes.getResult() == null || getRes.getResult().getCode() != 0) {
|
int errorCode = getRes != null && getRes.getResult() != null ? getRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("抓图失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return "";
|
}
|
|
String base64Str = getRes.getParameters().getPicBuf();
|
if (base64Str == null || base64Str.isEmpty()) {
|
log.warn("Base64图片数据为空");
|
return "";
|
}
|
|
// 处理 Base64 前缀(如果有 data:image/jpeg;base64, 这样的前缀)
|
String pureBase64 = base64Str;
|
if (base64Str.contains(",")) {
|
pureBase64 = base64Str.split(",")[1];
|
}
|
|
// 存储到minio
|
String objectName = cameraId + "_" + chanNo + ".jpeg";
|
String url = "";
|
File tempFile = null;
|
|
try {
|
// Base64 解码
|
byte[] imageBytes = Base64.getDecoder().decode(pureBase64);
|
|
// 创建临时文件
|
tempFile = File.createTempFile("snap_", ".jpeg");
|
java.nio.file.Files.write(tempFile.toPath(), imageBytes);
|
|
// 使用 FileMultipartFile 转换
|
MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.IMAGE_JPEG);
|
|
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
|
if (sysFileR.getCode() == R.SUCCESS) {
|
url = sysFileR.getData().getUrl();
|
log.debug("上传文件成功:{}", url);
|
}
|
|
} catch (Exception e) {
|
log.error("上传失败", e);
|
} finally {
|
// 删除临时文件
|
if (tempFile != null && tempFile.exists()) {
|
tempFile.delete();
|
}
|
}
|
|
return url;
|
}
|
|
@Override
|
public String picCutCate(CameraCmd cmd, String bucketName, String objectName) {
|
return "";
|
}
|
|
@Override
|
public PtzDto getPtz(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
GetPtzInfoRequest request = new GetPtzInfoRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPtzInfoResponse res = XmlUtils.fromXml(response, GetPtzInfoResponse.class);
|
// 合并空值判断
|
if (res == null || res.getResult() == null) {
|
String reason = res == null ? "接口返回结果为空" : "接口返回无错误信息";
|
return null;
|
}
|
Integer errorCode = res.getResult().getCode();
|
if (errorCode != 0) {
|
// 2. 通过ErrorCodeEnum获取错误描述
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
// 3. 拼接错误信息,返回包含错误码和描述的提示
|
String errorMsg = String.format("获取ptz失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
return null;
|
}
|
Float panAngle = res.getParameters().getPanAngle();
|
Float P = new BigDecimal(panAngle.toString()).setScale(2, RoundingMode.HALF_UP).floatValue();
|
Float tiltAngle = res.getParameters().getTiltAngle();
|
Float T = tiltAngle < 0 ? tiltAngle + 360 : tiltAngle;
|
T = new BigDecimal(T.toString()).setScale(2, RoundingMode.HALF_UP).floatValue();
|
float zoomRatio = res.getParameters().getZoomRatio();
|
return new PtzDto(P, T, zoomRatio);
|
} catch (Exception ex) {
|
log.error("获取ptz异常:{}", ex.getMessage());
|
return null;
|
}
|
}
|
|
@Override
|
public PtzDto getPtzHigh(CameraCmd cmd) {
|
return getPtz(cmd);
|
}
|
|
@Override
|
public PtzScope getPtzScope(CameraCmd cmd) {
|
PtzScope ptzScope = new PtzScope();
|
ptzScope.setPMax(360f);
|
ptzScope.setPMin(0f);
|
ptzScope.setTMax(90f);
|
ptzScope.setTMin(270f);
|
ptzScope.setZMax(40f);
|
ptzScope.setZMin(0f);
|
return ptzScope;
|
}
|
|
@Override
|
public Boolean setPtz(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
PtzDto ptzDto = cmd.getPtzDto();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
//保留小数点2位
|
Float P = new BigDecimal(ptzDto.getP().toString()).setScale(2, RoundingMode.HALF_UP).floatValue();
|
Float T = ptzDto.getT() > 300 ? ptzDto.getT() - 360 : ptzDto.getT();
|
T = new BigDecimal(T.toString()).setScale(2, RoundingMode.HALF_UP).floatValue();
|
Float Z = ptzDto.getZ();
|
SetAbsLocationZoomRequest request = new SetAbsLocationZoomRequest(chanNo, P, T, Z, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPtzInfoResponse res = XmlUtils.fromXml(response, GetPtzInfoResponse.class);
|
// 合并空值判断
|
if (res == null || res.getResult() == null) {
|
String reason = res == null ? "接口返回结果为空" : "接口返回无错误信息";
|
log.warn(reason);
|
return false;
|
}
|
Integer errorCode = res.getResult().getCode();
|
if (errorCode != 0) {
|
// 2. 通过ErrorCodeEnum获取错误描述
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
// 3. 拼接错误信息,返回包含错误码和描述的提示
|
String errorMsg = String.format("设置ptz失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
} catch (Exception ex) {
|
log.error("设置ptz异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
@Override
|
public Boolean setPtzHigh(CameraCmd cmd) {
|
return setPtz(cmd);
|
}
|
|
@Override
|
public Boolean setPtzHighT(CameraCmd cmd) {
|
return setPtz(cmd);
|
}
|
|
@Override
|
public Boolean setZeroPtz(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
// 设置零方位角
|
SetLocationParaPtRequest request = new SetLocationParaPtRequest(seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
SetLocationParaPtResponse getRes = XmlUtils.fromXml(response, SetLocationParaPtResponse.class);
|
// 没返回 → 失败
|
if (getRes == null || getRes.getResult() == null) {
|
return false;
|
}
|
int code = getRes.getResult().getCode();
|
if (code == 0) {
|
return true;
|
} else {
|
int errorCode = getRes != null && getRes.getResult() != null ? getRes.getResult().getCode() : -1;
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置零方位角失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
}
|
|
@Override
|
public String record(CameraCmd cmd) {
|
return "";
|
}
|
|
@Override
|
public Boolean guideTargetPosition(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
ArdCamera ardCamera = redisService.getCacheMapValue(CacheConstants.CAMERA_LIST, cameraId);
|
if (StringUtils.isNull(ardCamera)) {
|
log.warn("未找到相机信息,cameraId:{}", cameraId);
|
return false;
|
}
|
Point cameraPosition = new Point(ardCamera.getLongitude(), ardCamera.getLatitude(), ardCamera.getAltitude());
|
cameraPosition.validate();
|
Point targetPositions = cmd.getTargetPosition();
|
targetPositions.validate();
|
PtzDto cameraPTZ = GisUtil.getCameraPTZ(cameraPosition, targetPositions, 20, 150);
|
cmd.setPtzDto(cameraPTZ);
|
validatePtzRange(cmd);
|
setPtz(cmd);
|
return true;
|
}
|
|
/**
|
* 校验 PTZ 参数是否在设备支持的范围内
|
*
|
* @param cmd 相机命令,需包含 PTZ 参数
|
* @throws CameraSDKException 校验失败时抛出异常
|
*/
|
protected void validatePtzRange(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
|
// 从数据库获取 PTZ 范围
|
PtzScope ptzScope = ardCameraPtzScopeService.getPtzScopeByCameraId(cameraId);
|
if (ptzScope == null) {
|
log.warn("获取 PTZ 范围失败: " + cameraId);
|
throw new CameraSDKException(CameraSDKException.ErrorCode.OVER_GUIDANCE_RANGE.getCode(),
|
cameraId, "获取 PTZ 范围失败,请检查设备配置");
|
}
|
|
PtzDto ptzDto = cmd.getPtzDto();
|
if (ptzDto == null) {
|
log.warn("PTZ 控制参数为空: " + cameraId);
|
throw new CameraSDKException(CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cameraId,
|
"PTZ 控制参数为空");
|
}
|
|
float p = ptzDto.getP();
|
float t = ptzDto.getT();
|
float z = ptzDto.getZ() < 1 ? 1 : ptzDto.getZ(); // 修正 z 小于 1 的情况
|
|
// 水平角度校验
|
if (p < ptzScope.getPMin() || p > ptzScope.getPMax()) {
|
log.warn("水平角度超出范围:" + p + ",允许范围:" + ptzScope.getPMin() + " ~ " + ptzScope.getPMax());
|
throw new CameraSDKException(CameraSDKException.ErrorCode.OVER_GUIDANCE_RANGE.getCode(),
|
cameraId,
|
"水平角度超出范围:" + ptzScope.getPMin() + " ~ " + ptzScope.getPMax());
|
}
|
|
// 俯仰角度校验
|
if (t <= 90) {
|
if (t > ptzScope.getTMax()) {
|
log.warn("俯仰角度超出向上范围:" + t + ",最大允许:" + ptzScope.getTMax());
|
throw new CameraSDKException(CameraSDKException.ErrorCode.OVER_GUIDANCE_RANGE.getCode(),
|
cameraId,
|
"俯仰角度超出向上范围:" + ptzScope.getTMax());
|
}
|
} else if (t >= 270 && t <= 360) {
|
if (t < ptzScope.getTMin()) {
|
log.warn("俯仰角度超出向下范围:" + t + ",最小允许:" + ptzScope.getTMin());
|
throw new CameraSDKException(CameraSDKException.ErrorCode.OVER_GUIDANCE_RANGE.getCode(),
|
cameraId,
|
"俯仰角度超出向下范围:" + ptzScope.getTMin());
|
}
|
} else {
|
log.warn("俯仰角度非法:" + t);
|
throw new CameraSDKException(CameraSDKException.ErrorCode.BEYOND_VISIBILITY_RANGE.getCode(),
|
cameraId,
|
"俯仰角度非法:" + t);
|
}
|
|
// 变倍校验
|
if (z > ptzScope.getZMax()) {
|
log.warn("变倍超出范围:" + z + ",最大允许:" + ptzScope.getZMax());
|
throw new CameraSDKException(CameraSDKException.ErrorCode.OVER_GUIDANCE_RANGE.getCode(),
|
cameraId,
|
"变倍超出范围:" + ptzScope.getZMax());
|
}
|
}
|
@Override
|
public Boolean recordStart(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
|
try {
|
// 1. 获取流媒体信息
|
R<ArdVtdu> info = remoteArdVtduService.getInfo(name, SecurityConstants.INNER);
|
if (info.getCode() != R.SUCCESS || info.getData() == null) {
|
log.warn("获取摄像头信息失败: {}, code: {}", name, info.getCode());
|
return false;
|
}
|
|
ArdVtdu ardVtdu = info.getData();
|
String rtspUrl = ardVtdu.getRtspUrl();
|
if (rtspUrl == null || rtspUrl.isEmpty()) {
|
log.error("RTSP地址为空: {}", name);
|
return false;
|
}
|
|
// 2. 开始录像
|
String sessionId = recordSdk.startRecording(cameraId, rtspUrl, null);
|
|
if (sessionId == null || sessionId.isEmpty()) {
|
log.error("录像SDK返回SessionId为空,开始录像失败: {}", name);
|
return false;
|
}
|
|
// 3. 缓存 SessionId
|
RecordSessionCache.put(name, sessionId);
|
log.info("录像开始成功: {}, sessionId: {}", name, sessionId);
|
return true;
|
|
} catch (Exception ex) {
|
log.error("开始录像发生未知异常: {}", name, ex);
|
return false;
|
}
|
}
|
|
@Override
|
public String recordStop(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
String localFilePath = null;
|
|
try {
|
// 1. 获取并移除 SessionId (原子操作,防止并发问题)
|
String sessionId = RecordSessionCache.remove(name);
|
if (sessionId == null) {
|
log.warn("未找到正在进行的录像会话,可能已停止或未开始: {}", name);
|
return "";
|
}
|
|
// 2. 停止录像并获取本地文件路径
|
List<String> filePaths = recordSdk.stopRecording(sessionId);
|
if (filePaths == null || filePaths.isEmpty()) {
|
log.error("录像停止失败,未获取到文件路径: {}, sessionId: {}", name, sessionId);
|
return "";
|
}
|
|
localFilePath = filePaths.get(0);
|
log.info("录像停止成功,本地文件: {}", localFilePath);
|
|
// 3. 校验文件是否存在
|
File file = new File(localFilePath);
|
if (!file.exists()) {
|
log.error("录像文件不存在,无法上传: {}", localFilePath);
|
return "";
|
}
|
|
// 4. 使用 FileMultipartFile 转换(不用 MultipartFileUtil)
|
String objectName = cameraId + "_" + chanNo + ".mp4";
|
String type = cmd.getType() == null ? "record" : cmd.getType();
|
|
MultipartFile multipartFile = new FileMultipartFile(file, objectName, MimeTypeUtils.VIDEO_MP4);
|
|
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, "record", type);
|
|
if (sysFileR.getCode() == R.SUCCESS && sysFileR.getData() != null) {
|
String url = sysFileR.getData().getUrl();
|
log.info("录像上传MinIO成功: {}", url);
|
return url;
|
} else {
|
log.error("录像上传MinIO失败: {}, msg: {}", name, sysFileR.getMsg());
|
return "";
|
}
|
|
} catch (Exception ex) {
|
log.error("停止录像过程发生异常: {}", name, ex);
|
return "";
|
} finally {
|
// 5. 【重要】清理本地临时文件
|
if (localFilePath != null) {
|
try {
|
java.nio.file.Files.deleteIfExists(java.nio.file.Paths.get(localFilePath));
|
log.debug("本地临时录像文件已删除: {}", localFilePath);
|
} catch (Exception e) {
|
log.warn("删除本地临时文件失败: {}", localFilePath, e);
|
}
|
}
|
}
|
}
|
|
@Override
|
public PtzParamDTO getGisInfo(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
try {
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
GetPtzInfoRequest request = new GetPtzInfoRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPtzInfoResponse res = XmlUtils.fromXml(response, GetPtzInfoResponse.class);
|
// 合并空值判断
|
if (res == null || res.getResult() == null) {
|
String reason = res == null ? "接口返回结果为空" : "接口返回无错误信息";
|
return new PtzParamDTO();
|
}
|
Integer errorCode = res.getResult().getCode();
|
if (errorCode != 0) {
|
// 2. 通过ErrorCodeEnum获取错误描述
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
// 3. 拼接错误信息,返回包含错误码和描述的提示
|
String errorMsg = String.format("获取ptz失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
return new PtzParamDTO();
|
}
|
Float panAngle = res.getParameters().getPanAngle();
|
Float tiltAngle = res.getParameters().getTiltAngle();
|
Float zoomRatio = res.getParameters().getZoomRatio();
|
Float zoomAngle = res.getParameters().getZoom();//水平视场角
|
Float tZoomAngle = res.getParameters().getTZoom();//垂直视场角
|
|
PtzParamDTO ptzDTO = new PtzParamDTO();
|
ptzDTO.setP(panAngle);
|
ptzDTO.setT(tiltAngle);
|
ptzDTO.setZ(zoomRatio);
|
ptzDTO.setFHorFieldAngle(zoomAngle);
|
ptzDTO.setFVerFieldAngle(tZoomAngle);
|
return ptzDTO;
|
} catch (Exception ex) {
|
log.error("设备【{}】获取ptz异常:{}", cameraId, ex.getMessage());
|
return new PtzParamDTO();
|
}
|
}
|
|
@Override
|
public Double correctPitch(CameraCmd cmd) {
|
return 0.0;
|
}
|
|
@Override
|
public List<ArdChannel> getChannels(ArdCamera camera) {
|
if (camera == null || camera.getChanNum() <= 0) {
|
return new ArrayList<>();
|
}
|
String cameraId = camera.getId();
|
List<ArdChannel> channels = new ArrayList<>();
|
for (int i = 1; i <= 2; i++) {
|
ArdChannel channel = new ArdChannel();
|
CameraCmd cameraCmd = new CameraCmd(cameraId, i);
|
// 设置通道基本属性
|
channel.setDeviceId(cameraId);
|
String channelName = getChannelName(cameraCmd);
|
channel.setName(channelName);
|
channel.setChanNo(i); // 通道号从1开始
|
VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(cameraCmd);
|
if (videoCompressionCfg != null) {
|
channel.setVideoCode(videoCompressionCfg.getVideoEncType());
|
}
|
// 获取rtsp地址
|
String rtspUrl = String.format("rtsp://%s:%s@%s:%d/00010%d",
|
camera.getUsername(),
|
camera.getPassword(),
|
camera.getIp(),
|
camera.getRtspPort(),
|
channel.getChanNo() - 1
|
);
|
channel.setLiveAddress(rtspUrl);
|
channels.add(channel);
|
}
|
|
return channels;
|
}
|
|
private String getChannelName(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String sessionId = SessionCache.get(cameraId);
|
int seq = getSafeSeq();
|
GetPuImageTextParaRequest request = new GetPuImageTextParaRequest(chanNo, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetPuImageTextParaResponse res = XmlUtils.fromXml(response, GetPuImageTextParaResponse.class);
|
if (res.getParameters() != null && !res.getParameters().getBitmapText().isEmpty()) {
|
String bitmapText = res.getParameters().getBitmapText();
|
return new String(Base64.getDecoder().decode(bitmapText), Charset.forName("GBK"));
|
}
|
return "通道" + chanNo;
|
}
|
|
|
@Override
|
public Boolean set3DPosition(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
PointFrame pointFrame = cmd.getPointFrame();
|
PTZ3DLocationConverter.LocationParam locationParam = PTZ3DLocationConverter.convertPrecise(pointFrame);
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
SetPTZ3DLocationRequest request = new SetPTZ3DLocationRequest(chanNo, locationParam, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
SetPTZ3DLocationResponse res = XmlUtils.fromXml(response, SetPTZ3DLocationResponse.class);
|
// 合并空值判断
|
if (res == null || res.getResult() == null) {
|
String reason = res == null ? "接口返回结果为空" : "接口返回无错误信息";
|
log.warn(reason);
|
return false;
|
}
|
Integer errorCode = res.getResult().getCode();
|
if (errorCode != 0) {
|
// 2. 通过ErrorCodeEnum获取错误描述
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
// 3. 拼接错误信息,返回包含错误码和描述的提示
|
String errorMsg = String.format("获取ptz失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
} catch (Exception e) {
|
log.error("3D定位异常:{}", e.getMessage());
|
return false;
|
}
|
}
|
|
@Override
|
public DeviceCFG getSystemInfo(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
int seq = getSafeSeq();
|
String sessionId = SessionCache.get(cameraId);
|
GetDeviceInfoRequest request = new GetDeviceInfoRequest(cameraId, seq, sessionId);
|
String response = nettyTcpClient.sendXmlAndWait(cameraId, request);
|
GetDeviceInfoResponse res = XmlUtils.fromXml(response, GetDeviceInfoResponse.class);
|
// 合并空值判断
|
if (res == null || res.getResult() == null) {
|
String reason = res == null ? "接口返回结果为空" : "接口返回无错误信息";
|
return null;
|
}
|
Integer errorCode = res.getResult().getCode();
|
if (errorCode != 0) {
|
// 2. 通过ErrorCodeEnum获取错误描述
|
String errorDesc = ErrorCodeEnum.getDescByCode(errorCode);
|
// 3. 拼接错误信息,返回包含错误码和描述的提示
|
String errorMsg = String.format("获取设备信息失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
return null;
|
}
|
DeviceCFG deviceCFG = new DeviceCFG();
|
deviceCFG.setSDVRName(res.getParameters().getDeviceName());
|
deviceCFG.setDwSoftwareVersion(res.getParameters().getFirmwareVersion());
|
return deviceCFG;
|
} catch (Exception ex) {
|
log.error("获取设备信息异常:{}", ex.getMessage());
|
return null;
|
}
|
}
|
@Override
|
public List<Map<String, Object>> getCameraVideoLable(Long userId, CameraCmd cmd)throws ServiceException {
|
return null;
|
}
|
|
}
|