package com.ard.work.sdk.hik.service;
|
|
import com.ard.common.core.constant.CacheConstants;
|
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.FileUtils;
|
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.work.api.domian.*;
|
import com.ard.work.device.camera.domain.PtzParamDTO;
|
import com.ard.work.device.camera.service.IArdCameraPtzScopeService;
|
import com.ard.work.device.terrain.domain.ArdTerrainMark;
|
import com.ard.work.device.terrain.service.IArdTerrainMarkService;
|
import com.ard.work.event.LoginEvent;
|
import com.ard.work.point.tower.domain.ArdTower;
|
import com.ard.work.point.tower.service.IArdTowerService;
|
import com.ard.work.point.well.service.IArdWellService;
|
import com.ard.work.sdk.common.GlobalVariable;
|
import com.ard.work.sdk.common.ResolutionEnum;
|
import com.ard.work.sdk.common.SdkErrorCodeEnum;
|
import com.ard.work.sdk.common.osSelect;
|
import com.ard.work.sdk.hik.lib.ExceptionCallBack;
|
import com.ard.work.sdk.hik.lib.HCNetSDK;
|
import com.ard.work.sdk.hp.cache.CameraSerialCache;
|
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.utils.FFmpegUtils;
|
import com.ard.work.utils.gis.GisUtil;
|
import com.sun.jna.Native;
|
import com.sun.jna.Pointer;
|
import com.sun.jna.ptr.IntByReference;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.util.StopWatch;
|
import org.springframework.web.multipart.MultipartFile;
|
import java.awt.geom.Point2D;
|
import java.io.ByteArrayInputStream;
|
import java.io.File;
|
import java.io.InputStream;
|
import java.io.UnsupportedEncodingException;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.nio.ByteBuffer;
|
import java.nio.charset.StandardCharsets;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
import static com.ard.work.sdk.dh.lib.ToolKits.getErrorCodePrint;
|
import static com.ard.work.sdk.hik.lib.HCNetSDK.*;
|
|
/**
|
* @Description: 海康抽象类
|
* @ClassName: DhSdkStrategy
|
* @Author: 刘苏义
|
* @Date: 2023年11月07日10:51:09
|
**/
|
|
@Slf4j
|
public abstract class AbstractHikVisionSDK implements CameraSDK {
|
|
@Resource
|
private RemoteFileService remoteFileService;
|
@Resource
|
private IArdTerrainMarkService ardTerrainMarkService;
|
@Resource
|
private IArdCameraPtzScopeService ardCameraPtzScopeService;
|
@Resource
|
private RedisService redisService;
|
|
@Value("${sdk.tempDir}")
|
private String tempDir;
|
|
@Resource
|
private ApplicationEventPublisher eventPublisher;
|
@Resource
|
private IArdTowerService ardTowerService;
|
@Resource
|
private IArdWellService ardWellService;
|
protected static HCNetSDK hCNetSDK = null;
|
private static HCNetSDK.FExceptionCallBack fExceptionCallBack;//异常回调
|
|
static {
|
initSdk();
|
}
|
|
public static void initSdk() {
|
if (hCNetSDK == null) {
|
if (!CreateSDKInstance()) {
|
System.out.println("Load SDK fail");
|
return;
|
}
|
}
|
// 初始化
|
if (!hCNetSDK.NET_DVR_Init()) {
|
log.debug("SDK初始化失败");
|
}
|
if (fExceptionCallBack == null) {
|
fExceptionCallBack = new ExceptionCallBack();//异常回调
|
//设置异常回调函数(可在回调函数中获取设备上下线状态等)
|
if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, null)) {
|
log.debug("Set fExceptionCallBack function fail");
|
} else {
|
log.debug("Set fExceptionCallBack function successfully!");
|
}
|
}
|
//设置连接时间与重连时间
|
hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
|
hCNetSDK.NET_DVR_SetReconnect(5000, true);
|
log.info("初始化海康sdk完成");
|
}
|
|
/**
|
* 登录(海康)
|
* 刘苏义
|
* 2024/9/5
|
*/
|
@Override
|
public void login(ArdCamera camera) {
|
String ip = camera.getIp();
|
String username = camera.getUsername();
|
String password = camera.getPassword();
|
short port = camera.getPort().shortValue();
|
|
// 设备信息输出参数
|
NET_DVR_DEVICEINFO_V40 deviceInfo = new NET_DVR_DEVICEINFO_V40();
|
NET_DVR_USER_LOGIN_INFO loginInfo = new NET_DVR_USER_LOGIN_INFO();
|
|
// 设置登录信息
|
loginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
|
System.arraycopy(ip.getBytes(), 0, loginInfo.sDeviceAddress, 0, ip.length());
|
|
loginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
|
System.arraycopy(username.getBytes(), 0, loginInfo.sUserName, 0, username.length());
|
|
loginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
|
System.arraycopy(password.getBytes(), 0, loginInfo.sPassword, 0, password.length());
|
|
loginInfo.wPort = port;
|
loginInfo.byVerifyMode = 0;
|
loginInfo.byLoginMode = 0;
|
loginInfo.bUseAsynLogin = false;
|
loginInfo.write();
|
|
// 调用海康 SDK 登录
|
int loginId = hCNetSDK.NET_DVR_Login_V40(loginInfo, deviceInfo);
|
if (loginId < 0) {
|
handleLoginFail(camera);
|
} else {
|
// 登录成功处理
|
handleLoginSuccess(camera, loginId, deviceInfo);
|
}
|
// 发布登录事件
|
eventPublisher.publishEvent(new LoginEvent(camera));
|
}
|
|
/**
|
* 登录失败处理
|
*/
|
private void handleLoginFail(ArdCamera camera) {
|
camera.setChanNum(0);
|
camera.setLoginId(-1L);
|
camera.setState("0");
|
camera.setChannelList(null);
|
int errCode = hCNetSDK.NET_DVR_GetLastError();
|
log.warn("设备[{}:{}]登录失败,错误码:{} 原因:{}", camera.getIp(), camera.getPort(), errCode,
|
SdkErrorCodeEnum.getDescByCode(errCode));
|
}
|
|
/**
|
* 登录成功处理
|
*/
|
private void handleLoginSuccess(ArdCamera camera, int loginId, NET_DVR_DEVICEINFO_V40 deviceInfo) {
|
camera.setLoginId((long) loginId);
|
camera.setState("1");
|
|
int chanNum = deviceInfo.struDeviceV30.byChanNum;
|
int startChan = deviceInfo.struDeviceV30.byStartDChan + 1;
|
// 如果是终端或者超脑,需要特殊处理
|
if ("2".equals(camera.getType()) || "3".equals(camera.getType())) {
|
chanNum = deviceInfo.struDeviceV30.byIPChanNum == 0 ? 64 : deviceInfo.struDeviceV30.byIPChanNum;
|
startChan = deviceInfo.struDeviceV30.byStartDChan;
|
}
|
camera.setChanNum(chanNum);
|
camera.setStartChan(startChan);
|
|
GlobalVariable.hikLoginMap.put(loginId, camera);
|
|
// 获取最新通道
|
List<ArdChannel> channels = getChannels(camera);
|
if (!channels.isEmpty()) {
|
camera.setChannelList(channels);
|
}
|
|
log.info("设备登录成功 [{}:{}]", camera.getIp(), camera.getPort());
|
}
|
|
/**
|
* 注销登录
|
*
|
* @return boolean
|
* @author 刘苏义
|
* @date 2024/9/5 16:58
|
*/
|
@Override
|
public void logout(String cameraId) {
|
Long loginId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, cameraId);
|
if (loginId == null) {
|
return;
|
}
|
Integer lSerialHandle = CameraSerialCache.get(cameraId);
|
if (lSerialHandle != null) {
|
CameraSerialCache.remove(cameraId);
|
if (hCNetSDK.NET_DVR_SerialStop(lSerialHandle)) {
|
log.info("【{}】关闭透明串口成功", cameraId);
|
}
|
}
|
hCNetSDK.NET_DVR_Logout(loginId.intValue());
|
}
|
|
/**
|
* 是否在线
|
*
|
* @param cmd 相机命令
|
*/
|
@Override
|
public Boolean isOnLine(CameraCmd cmd) {
|
try {
|
int userId = cmd.getLoginId().intValue();
|
return hCNetSDK.NET_DVR_RemoteControl(userId, NET_DVR_CHECK_USER_STATUS, null, 0);
|
} catch (Exception ex) {
|
log.error("检测在线异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
/**
|
* 带速度的云台控制操作
|
*
|
* @param cmd 相机命令
|
* @return true 表示控制命令发送成功
|
* @throws CameraSDKException SDK调用失败时抛出
|
*/
|
@Override
|
public Boolean pTZControl(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
int userId = cmd.getLoginId().intValue(); // 已保证登录成功
|
|
int dwStop = cmd.isEnable() ? 0 : 1;
|
int dwPTZCommand;
|
|
switch (cmd.getCode()) {
|
case 1:
|
dwPTZCommand = HCNetSDK.UP_LEFT;
|
break;
|
case 2:
|
dwPTZCommand = HCNetSDK.TILT_UP;
|
break;
|
case 3:
|
dwPTZCommand = HCNetSDK.UP_RIGHT;
|
break;
|
case 4:
|
dwPTZCommand = HCNetSDK.PAN_LEFT;
|
break;
|
case 5:
|
dwPTZCommand = HCNetSDK.RUN_SEQ;
|
break;
|
case 6:
|
dwPTZCommand = HCNetSDK.PAN_RIGHT;
|
break;
|
case 7:
|
dwPTZCommand = HCNetSDK.DOWN_LEFT;
|
break;
|
case 8:
|
dwPTZCommand = HCNetSDK.TILT_DOWN;
|
break;
|
case 9:
|
dwPTZCommand = HCNetSDK.DOWN_RIGHT;
|
break;
|
case 10:
|
dwPTZCommand = HCNetSDK.ZOOM_IN;
|
break;
|
case 11:
|
dwPTZCommand = HCNetSDK.ZOOM_OUT;
|
break;
|
case 12:
|
dwPTZCommand = HCNetSDK.FOCUS_NEAR;
|
break;
|
case 13:
|
dwPTZCommand = HCNetSDK.FOCUS_FAR;
|
break;
|
case 14:
|
dwPTZCommand = HCNetSDK.IRIS_OPEN;
|
break;
|
case 15:
|
dwPTZCommand = HCNetSDK.IRIS_CLOSE;
|
break;
|
case 16:
|
dwPTZCommand = HCNetSDK.WIPER_PWRON;
|
break;
|
default:
|
return false; // 或者记录日志
|
}
|
|
boolean success = hCNetSDK.NET_DVR_PTZControlWithSpeed_Other(
|
userId, cmd.getChanNo(), dwPTZCommand, dwStop, cmd.getSpeed()
|
);
|
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
log.warn("【{}】PTZ控制失败,错误码:{}, 原因:{}", cameraId, errorCode,
|
SdkErrorCodeEnum.getDescByCode(errorCode));
|
}
|
|
return success;
|
}
|
|
/**
|
* 设置聚焦值
|
*
|
* @param cmd 相机命令
|
* @return true 成功,false 失败
|
*/
|
@Override
|
public Boolean setFocusPos(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
Integer dwFocusPos = cmd.getDwFocusPos();
|
int userId = cmd.getLoginId().intValue();
|
|
try {
|
HCNetSDK.NET_DVR_STD_CONFIG stdCfg = new HCNetSDK.NET_DVR_STD_CONFIG();
|
HCNetSDK.NET_DVR_GIS_INFO gisInfo = new HCNetSDK.NET_DVR_GIS_INFO();
|
gisInfo.struPtzPosEx.dwFocus = dwFocusPos;
|
|
stdCfg.read();
|
IntByReference channelRef = new IntByReference(channelNum);
|
stdCfg.lpInBuffer = gisInfo.getPointer();
|
stdCfg.lpCondBuffer = channelRef.getPointer();
|
stdCfg.dwCondSize = 4;
|
stdCfg.dwInSize = gisInfo.size();
|
stdCfg.write(); // 写入前必须调用
|
|
boolean success = hCNetSDK.NET_DVR_SetSTDConfig(userId, NET_DVR_GET_GISINFO, stdCfg);
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】设置聚焦值失败,错误码:{},原因:{}", cameraId, errorCode, errorDesc);
|
return false;
|
}
|
|
return true;
|
} catch (Exception e) {
|
log.error("【{}】设置聚焦值异常: {}", cameraId, e.getMessage(), e);
|
return false;
|
}
|
}
|
|
/**
|
* 获取聚焦值
|
*
|
* @param cmd 相机命令
|
*/
|
@Override
|
public Integer getFocusPos(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
|
HCNetSDK.NET_DVR_STD_CONFIG stdCfg = new HCNetSDK.NET_DVR_STD_CONFIG();
|
HCNetSDK.NET_DVR_GIS_INFO gisInfo = new HCNetSDK.NET_DVR_GIS_INFO();
|
|
stdCfg.read();
|
IntByReference channelRef = new IntByReference(channelNum);
|
stdCfg.lpCondBuffer = channelRef.getPointer();
|
stdCfg.dwCondSize = 4;
|
stdCfg.lpOutBuffer = gisInfo.getPointer();
|
stdCfg.dwOutSize = gisInfo.size();
|
stdCfg.write(); // 设置前需要 write()
|
|
boolean success = hCNetSDK.NET_DVR_GetSTDConfig(userId, NET_DVR_GET_GISINFO, stdCfg);
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取聚焦值失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return null; // 返回 null 表示失败
|
}
|
|
gisInfo.read();
|
return gisInfo.struPtzPosEx.dwFocus;
|
|
} catch (Exception ex) {
|
log.error("获取聚焦值异常:{}", ex.getMessage());
|
return null; // 返回 null 表示失败
|
}
|
}
|
|
/**
|
* 设置预置点
|
*
|
* @param cmd 相机命令
|
*/
|
@Override
|
public Boolean setPreset(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
Integer presetIndex = cmd.getPresetIndex();
|
int userId = cmd.getLoginId().intValue();
|
|
try {
|
boolean success = hCNetSDK.NET_DVR_PTZPreset_Other(userId, channelNum, SET_PRESET, presetIndex);
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】预置点设置失败,错误码:{},原因:{}", cameraId, errorCode, errorDesc);
|
return false;
|
}
|
|
log.info("【{}】预置点设置成功: {}", cameraId, presetIndex);
|
return true;
|
} catch (Exception ex) {
|
log.error("【{}】设置预置点异常: {}", cameraId, ex.getMessage(), ex);
|
return false;
|
}
|
}
|
|
/**
|
* 转到预置点
|
*
|
* @param cmd 相机命令
|
*/
|
@Override
|
public Boolean gotoPreset(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
Integer presetIndex = cmd.getPresetIndex();
|
int userId = cmd.getLoginId().intValue();
|
|
try {
|
boolean success = hCNetSDK.NET_DVR_PTZPreset_Other(userId, channelNum, GOTO_PRESET, presetIndex);
|
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】调用预置点失败,错误码:{}, 原因:{}", cameraId, errorCode, errorDesc);
|
return false;
|
}
|
|
log.info("【{}】调用预置点成功: {}", cameraId, presetIndex);
|
return true;
|
} catch (Exception ex) {
|
log.error("【{}】调用预置点异常: {}", cameraId, ex.getMessage(), ex);
|
return false;
|
}
|
}
|
|
/**
|
* 获取通道压缩参数
|
*
|
* @param cmd 相机命令
|
* @return VideoCompressionCfg
|
* @author 刘苏义
|
* @date 2024/9/5 16:59
|
*/
|
@Override
|
public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_COMPRESSIONCFG_V30 compressionCfg = new NET_DVR_COMPRESSIONCFG_V30();
|
compressionCfg.write();
|
Pointer point = compressionCfg.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
boolean bool = hCNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_COMPRESSCFG_V30, chanNo, point,
|
compressionCfg.size(), ibrBytesReturned);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取通道压缩参数失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null;
|
}
|
compressionCfg.read();
|
//视频编码类型:0-私有264,1-标准h264,2-标准mpeg4,7-M-JPEG,8-MPEG2,9-SVAC,10-标准h265,0xfe- 自动(和源一致),0xff-无效
|
String videoEncType;
|
switch (compressionCfg.struNormHighRecordPara.byVideoEncType) {
|
case 0:
|
videoEncType = "私有264";
|
break;
|
case 1:
|
videoEncType = "标准h264";
|
break;
|
case 2:
|
videoEncType = "标准mpeg4";
|
break;
|
case 7:
|
videoEncType = "M-JPEG";
|
break;
|
case 8:
|
videoEncType = "MPEG2";
|
break;
|
case 9:
|
videoEncType = "SVAC";
|
break;
|
case 10:
|
videoEncType = "标准h265";
|
break;
|
default:
|
videoEncType = "未知";
|
break;
|
}
|
//视频码率
|
String videoBitrate;
|
switch (compressionCfg.struNormHighRecordPara.dwVideoBitrate) {
|
case 0:
|
videoBitrate = "保留";
|
break;
|
case 1:
|
videoBitrate = "16(保留)";
|
break;
|
case 2:
|
videoBitrate = "32";
|
break;
|
case 3:
|
videoBitrate = "48";
|
break;
|
case 4:
|
videoBitrate = "64";
|
break;
|
case 5:
|
videoBitrate = "80";
|
break;
|
case 6:
|
videoBitrate = "96";
|
break;
|
case 7:
|
videoBitrate = "128";
|
break;
|
case 8:
|
videoBitrate = "160";
|
break;
|
case 9:
|
videoBitrate = "192";
|
break;
|
case 10:
|
videoBitrate = "224";
|
break;
|
case 11:
|
videoBitrate = "256";
|
break;
|
case 12:
|
videoBitrate = "320";
|
break;
|
case 13:
|
videoBitrate = "384";
|
break;
|
case 14:
|
videoBitrate = "448";
|
break;
|
case 15:
|
videoBitrate = "512";
|
break;
|
case 16:
|
videoBitrate = "640";
|
break;
|
case 17:
|
videoBitrate = "768";
|
break;
|
case 18:
|
videoBitrate = "896";
|
break;
|
case 19:
|
videoBitrate = "1024";
|
break;
|
case 20:
|
videoBitrate = "1280";
|
break;
|
case 21:
|
videoBitrate = "1536";
|
break;
|
case 22:
|
videoBitrate = "1792";
|
break;
|
case 23:
|
videoBitrate = "2048K";
|
break;
|
case 24:
|
videoBitrate = "3072";
|
break;
|
case 25:
|
videoBitrate = "4096";
|
break;
|
case 26:
|
videoBitrate = "8192";
|
break;
|
case 27:
|
videoBitrate = "16384";
|
break;
|
default:
|
videoBitrate = "其他";
|
break;
|
}
|
//分辨率
|
String resolution =
|
ResolutionEnum.fromCode(compressionCfg.struNormHighRecordPara.byResolution).getResolution();
|
//帧率
|
String nFrameRate;
|
switch (compressionCfg.struNormHighRecordPara.dwVideoFrameRate) {
|
case 0:
|
nFrameRate = "0";
|
break;
|
case 1:
|
nFrameRate = "1/16";
|
break;
|
case 2:
|
nFrameRate = "1/8";
|
break;
|
case 3:
|
nFrameRate = "1/4";
|
break;
|
case 4:
|
nFrameRate = "1/2";
|
break;
|
case 5:
|
nFrameRate = "1";
|
break;
|
case 6:
|
nFrameRate = "2";
|
break;
|
case 7:
|
nFrameRate = "4";
|
break;
|
case 8:
|
nFrameRate = "6";
|
break;
|
case 9:
|
nFrameRate = "8";
|
break;
|
case 10:
|
nFrameRate = "10";
|
break;
|
case 11:
|
nFrameRate = "12";
|
break;
|
case 12:
|
nFrameRate = "16";
|
break;
|
case 13:
|
nFrameRate = "20";
|
break;
|
case 14:
|
nFrameRate = "15";
|
break;
|
case 15:
|
nFrameRate = "18";
|
break;
|
case 16:
|
nFrameRate = "22";
|
break;
|
case 17:
|
nFrameRate = "25";
|
break;
|
case 18:
|
nFrameRate = "30";
|
break;
|
case 19:
|
nFrameRate = "35";
|
break;
|
case 20:
|
nFrameRate = "40";
|
break;
|
default:
|
nFrameRate = "未知";
|
break;
|
}
|
VideoCompressionCfg videoCompressionCfg = new VideoCompressionCfg();
|
videoCompressionCfg.setVideoBitrate(videoBitrate);//比特率
|
videoCompressionCfg.setResolution(resolution);//分辨率
|
videoCompressionCfg.setVideoEncType(videoEncType);//编码
|
videoCompressionCfg.setFrameRate(nFrameRate);//帧率
|
return videoCompressionCfg;
|
}
|
|
/**
|
* 获取ptz信息
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 16:59
|
*/
|
@Override
|
public PtzDto getPtz(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
|
NET_DVR_PTZPOS m_ptzPosCurrent = new NET_DVR_PTZPOS();
|
Pointer point = m_ptzPosCurrent.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
m_ptzPosCurrent.write();
|
boolean bool = hCNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_PTZPOS, chanNo, point,
|
m_ptzPosCurrent.size(), ibrBytesReturned);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取ptz失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null;
|
}
|
m_ptzPosCurrent.read();
|
// DecimalFormat df = new DecimalFormat("0.0");//设置保留位数
|
//16进制转Integer后除10,保留小数点1位
|
//实际显示的PTZ值是获取到的十六进制值的十分之一,
|
//如获取的水平参数P的值是0x1750,实际显示的P值为175度;
|
//获取到的垂直参数T的值是0x0789,实际显示的T值为78.9度;
|
//获取到的变倍参数Z的值是0x1100,实际显示的Z值为110倍。
|
BigDecimal b = BigDecimal.valueOf((float) Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wPanPos)) / 10);
|
BigDecimal c = BigDecimal.valueOf((float) Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wTiltPos)) / 10);
|
BigDecimal d = BigDecimal.valueOf((float) Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wZoomPos)) / 10);
|
float p = b.setScale(1, RoundingMode.HALF_UP).floatValue();
|
float t = c.setScale(1, RoundingMode.HALF_UP).floatValue();
|
float z = d.setScale(1, RoundingMode.HALF_UP).floatValue();
|
return new PtzDto(p, t, z);
|
}
|
|
/**
|
* 获取高精度ptz信息
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:00
|
*/
|
@Override
|
public PtzDto getPtzHigh(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
IntByReference channelRef = new IntByReference(chanNo);
|
Pointer channelPointer = channelRef.getPointer();
|
NET_DVR_STD_CONFIG lpConfigParam6696 = new NET_DVR_STD_CONFIG();
|
NET_DVR_PTZABSOLUTEEX_CFG lpPTZAbsoluteEX_cfg = new NET_DVR_PTZABSOLUTEEX_CFG();
|
lpConfigParam6696.lpCondBuffer = channelPointer;
|
lpConfigParam6696.dwCondSize = 4;
|
lpConfigParam6696.lpInBuffer = null;
|
lpConfigParam6696.dwInSize = 0;
|
lpConfigParam6696.lpOutBuffer = lpPTZAbsoluteEX_cfg.getPointer();
|
lpConfigParam6696.dwOutSize = lpPTZAbsoluteEX_cfg.size();
|
lpConfigParam6696.write();
|
boolean res = hCNetSDK.NET_DVR_GetSTDConfig(userId, HCNetSDK.NET_DVR_GET_PTZABSOLUTEEX, lpConfigParam6696);
|
if (!res) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取高精度PTZ失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null;
|
}
|
lpPTZAbsoluteEX_cfg.read();
|
double p = lpPTZAbsoluteEX_cfg.struPTZCtrl.fPan;
|
double t = lpPTZAbsoluteEX_cfg.struPTZCtrl.fTilt;
|
double z = lpPTZAbsoluteEX_cfg.struPTZCtrl.fZoom;
|
//保留小数点1位
|
BigDecimal bdPan = new BigDecimal(p).setScale(1, RoundingMode.HALF_UP);
|
BigDecimal bdTilt = BigDecimal.valueOf(t < 0 ? t + 360 : t).setScale(1, RoundingMode.HALF_UP);
|
BigDecimal bdZoom = new BigDecimal(z).setScale(1, RoundingMode.HALF_UP);
|
|
return new PtzDto(bdPan.floatValue(), bdTilt.floatValue(), bdZoom.floatValue());
|
} catch (Exception ex) {
|
log.error("获取高精度PTZ绝对位置异常:{}", ex.getMessage());
|
return null;
|
}
|
}
|
|
/**
|
* 设置ptz信息
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:00
|
*/
|
@Override
|
public Boolean setPtz(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
PtzDto ptzDto = cmd.getPtzDto();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_PTZPOS m_ptzPosCurrent = new NET_DVR_PTZPOS();
|
m_ptzPosCurrent.wAction = 1;
|
try {
|
String p = String.valueOf((int) (ptzDto.getP() * 10));
|
String t = String.valueOf((int) (ptzDto.getT() * 10));
|
String z = String.valueOf((int) (ptzDto.getZ() * 10));
|
m_ptzPosCurrent.wPanPos = (short) (Integer.parseInt(p, 16));
|
m_ptzPosCurrent.wTiltPos = (short) (Integer.parseInt(t, 16));
|
m_ptzPosCurrent.wZoomPos = (short) (Integer.parseInt(z, 16));
|
Pointer point = m_ptzPosCurrent.getPointer();
|
m_ptzPosCurrent.write();
|
boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_PTZPOS, channelNum, point,
|
m_ptzPosCurrent.size());
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("设置PTZ参数失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
} catch (Exception ex) {
|
log.error(ex.getMessage());
|
return false;
|
}
|
}
|
|
/**
|
* 设置高精度ptz信息(透传)
|
* 解决新固件2通道无法自动聚焦问题
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:04
|
*/
|
@Override
|
public Boolean setPtzHighT(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
PtzDto ptzDto = cmd.getPtzDto();
|
// 校验范围
|
validatePtzRange(cmd);
|
int userId = cmd.getLoginId().intValue();
|
HCNetSDK.NET_DVR_XML_CONFIG_INPUT inputParam = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
|
inputParam.dwSize = inputParam.size();
|
|
HCNetSDK.BYTE_ARRAY requestUrl = new HCNetSDK.BYTE_ARRAY(256);
|
String urlString = "PUT /ISAPI/PTZCtrl/channels/" + chanNo + "/absoluteEx";
|
System.arraycopy(urlString.getBytes(), 0, requestUrl.byValue, 0, urlString.length());
|
requestUrl.write();
|
inputParam.lpRequestUrl = requestUrl.getPointer();
|
inputParam.dwRequestUrlLen = requestUrl.byValue.length;
|
inputParam.dwRecvTimeOut = 5000;
|
Float T = (ptzDto.getT() >= 300.0f) ? (ptzDto.getT() - 360.0f) : ptzDto.getT();
|
String xmlString = "" +
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
"<PTZAbsoluteEx version=\"2.0\" xmlns=\"http://www.isapi.org/ver20/XMLSchema\">\n" +
|
"<elevation>" + T + "</elevation>\n" +
|
"<azimuth>" + ptzDto.getP() + "</azimuth>\n" +
|
"<absoluteZoom>" + ptzDto.getZ() + "</absoluteZoom>\n" +
|
"<horizontalSpeed>100</horizontalSpeed>\n" +
|
"<verticalSpeed>100</verticalSpeed>\n" +
|
"</PTZAbsoluteEx>";
|
int iStringSize = xmlString.length();
|
HCNetSDK.BYTE_ARRAY ptrByte = new HCNetSDK.BYTE_ARRAY(iStringSize);
|
System.arraycopy(xmlString.getBytes(), 0, ptrByte.byValue, 0, xmlString.length());
|
ptrByte.write();
|
|
inputParam.lpInBuffer = ptrByte.getPointer();
|
inputParam.dwInBufferSize = ptrByte.byValue.length;
|
inputParam.write();
|
|
HCNetSDK.BYTE_ARRAY outputXml = new HCNetSDK.BYTE_ARRAY(16 * 1024);
|
outputXml.read();
|
HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT outputParam = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT();
|
outputParam.dwSize = outputParam.size();
|
outputParam.lpOutBuffer = outputXml.getPointer();
|
outputParam.dwOutBufferSize = outputXml.byValue.length;
|
|
HCNetSDK.BYTE_ARRAY setStatusBuff = new HCNetSDK.BYTE_ARRAY(16 * 1024);
|
setStatusBuff.read();
|
outputParam.lpStatusBuffer = setStatusBuff.getPointer();
|
outputParam.dwStatusSize = setStatusBuff.byValue.length;
|
outputParam.write();
|
boolean bool = hCNetSDK.NET_DVR_STDXMLConfig(userId, inputParam, outputParam);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("设置透传高精度PTZ失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
}
|
|
//非透传 旧设备使用 新固件会导致2通道不自动聚焦
|
@Override
|
public Boolean setPtzHigh(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
PtzDto ptzDto = cmd.getPtzDto();
|
// 校验范围
|
validatePtzRange(cmd);
|
int userId = cmd.getLoginId().intValue();
|
IntByReference channelRef = new IntByReference(chanNo);
|
Pointer ChannelNoPointer = channelRef.getPointer();
|
NET_DVR_STD_CONFIG lpConfigParam6697 = new NET_DVR_STD_CONFIG();
|
NET_DVR_PTZABSOLUTEEX_CFG lpPTZAbsoluteEX_cfgInfo = new NET_DVR_PTZABSOLUTEEX_CFG();
|
lpConfigParam6697.lpCondBuffer = ChannelNoPointer;
|
lpConfigParam6697.dwCondSize = 4;
|
BYTE_ARRAY m_szStatusBuf = new BYTE_ARRAY(4096 * 4);
|
lpConfigParam6697.lpStatusBuffer = m_szStatusBuf.getPointer();
|
lpConfigParam6697.dwStatusSize = 4096 * 4;
|
lpPTZAbsoluteEX_cfgInfo.dwSize = lpPTZAbsoluteEX_cfgInfo.size();
|
|
lpPTZAbsoluteEX_cfgInfo.struPTZCtrl.fPan = ptzDto.getP();
|
float t = ptzDto.getT();
|
float t1 = t > 300 ? (t - 360) : t;
|
BigDecimal bigDecimal = new BigDecimal(t1);
|
lpPTZAbsoluteEX_cfgInfo.struPTZCtrl.fTilt = bigDecimal.setScale(2, RoundingMode.HALF_UP).floatValue();
|
lpPTZAbsoluteEX_cfgInfo.struPTZCtrl.fZoom = ptzDto.getZ();
|
//lpPTZAbsoluteEX_cfgInfo.struPTZCtrl.dwFocus = 100000;//这个曾注掉也不行
|
lpPTZAbsoluteEX_cfgInfo.dwFocalLen = 100000;
|
lpPTZAbsoluteEX_cfgInfo.fHorizontalSpeed = (float) 1000;
|
lpPTZAbsoluteEX_cfgInfo.fVerticalSpeed = (float) 1000;
|
lpPTZAbsoluteEX_cfgInfo.byZoomType = 0;
|
lpPTZAbsoluteEX_cfgInfo.write();
|
lpConfigParam6697.lpInBuffer = lpPTZAbsoluteEX_cfgInfo.getPointer();
|
lpConfigParam6697.dwInSize = lpPTZAbsoluteEX_cfgInfo.dwSize;
|
lpConfigParam6697.write();
|
boolean bool = hCNetSDK.NET_DVR_SetSTDConfig(userId, NET_DVR_SET_PTZABSOLUTEEX, lpConfigParam6697);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("设置高精度PTZ失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 引导目标位置
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:04
|
*/
|
@Override
|
public Boolean guideTargetPosition(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
Float baseYaw = cmd.getBaseYaw();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_PTZPOS m_ptzPosCurrent = new NET_DVR_PTZPOS();
|
m_ptzPosCurrent.wAction = 1;
|
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);
|
//若为车载光电,需要根据修改P值
|
if ("4".equals(ardCamera.getType())) {
|
cameraPTZ.setP((cameraPTZ.getP() - baseYaw + 360) % 360);
|
}
|
// pt校验范围
|
cmd.setPtzDto(cameraPTZ);
|
validatePtzRange(cmd);
|
String p = String.valueOf((int) (cameraPTZ.getP() * 10));
|
//修正俯仰
|
double correctPitch = correctPitch(cmd);
|
String t = String.valueOf((int) ((cameraPTZ.getT() + correctPitch) * 10));
|
String z = String.valueOf((int) (cameraPTZ.getZ() * 10));
|
m_ptzPosCurrent.wPanPos = (short) (Integer.parseInt(p, 16));
|
m_ptzPosCurrent.wTiltPos = (short) (Integer.parseInt(t, 16));
|
m_ptzPosCurrent.wZoomPos = (short) (Integer.parseInt(z, 16));
|
Pointer point = m_ptzPosCurrent.getPointer();
|
m_ptzPosCurrent.write();
|
boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_PTZPOS, chanNo, point,
|
m_ptzPosCurrent.size());
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("设置ptz失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 获取ptz锁定信息
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:06
|
*/
|
@Override
|
public Boolean getPTZLockInfo(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
|
NET_DVR_PTZ_LOCKCFG lockCfg = new NET_DVR_PTZ_LOCKCFG();
|
Pointer point = lockCfg.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
lockCfg.write();
|
boolean bool = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_PTZLOCKCFG, channelNum, point,
|
lockCfg.size(), ibrBytesReturned);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取云台锁定失败,错误码:{},原因:{}", errorCode, errorDesc);
|
throw new CameraSDKException(errorCode, cameraId, errorDesc);
|
}
|
|
lockCfg.read();
|
int byWorkMode = lockCfg.byWorkMode;
|
log.debug("PTZ锁定模式: {}", byWorkMode);
|
//云台锁定控制:0- 解锁,1- 锁定
|
return byWorkMode == 0;
|
}
|
|
@Override
|
public Boolean setPTZLockInfo(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
Boolean isLock = cmd.isEnable();
|
NET_DVR_PTZ_LOCKCFG lockCfg = new NET_DVR_PTZ_LOCKCFG();
|
lockCfg.byWorkMode = isLock ? (byte) 1 : (byte) 0; // 0- 解锁 false,1- 锁定 true
|
lockCfg.write();
|
boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_PTZLOCKCFG, chanNo, lockCfg.getPointer(),
|
lockCfg.size());
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置云台锁定失败,错误码:{},原因:{}", errorCode, errorDesc);
|
throw new CameraSDKException(errorCode, cameraId, errorDesc);
|
}
|
return true;
|
}
|
|
/**
|
* 设置零方位角
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:08
|
*/
|
@Override
|
public Boolean setZeroPtz(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_INITIALPOSITIONCTRL initializations = new NET_DVR_INITIALPOSITIONCTRL();
|
|
initializations.dwSize = initializations.size();
|
initializations.byWorkMode = 0;
|
initializations.dwChan = Short.parseShort(channelNum.toString());
|
|
Pointer point = initializations.getPointer();
|
initializations.write();
|
boolean bool = hCNetSDK.NET_DVR_RemoteControl(userId, NET_DVR_PTZ_INITIALPOSITIONCTRL, point,
|
initializations.size());
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("设置零方位角失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 获取球机PTZ参数取值范围
|
*
|
* @param cmd 相机命令
|
* @return AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:12
|
*/
|
@Override
|
public PtzScope getPtzScope(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_PTZSCOPE m_ptzPosCurrent = new NET_DVR_PTZSCOPE();
|
Pointer point = m_ptzPosCurrent.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
m_ptzPosCurrent.write();
|
boolean bool = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_PTZSCOPE, chanNo, point,
|
m_ptzPosCurrent.size(), ibrBytesReturned);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取ptz范围失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null;
|
}
|
m_ptzPosCurrent.read();
|
PtzScope ptzScope = new PtzScope();
|
ptzScope.setPMax(Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wPanPosMax)) / 10.0f);
|
ptzScope.setPMin(Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wPanPosMin)) / 10.0f);
|
ptzScope.setTMax(Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wTiltPosMax)) / 10.0f);
|
ptzScope.setTMin(Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wTiltPosMin)) / 10.0f);
|
ptzScope.setZMax(Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wZoomPosMax)) / 10.0f);
|
ptzScope.setZMin(Integer.parseInt(Integer.toHexString(m_ptzPosCurrent.wZoomPosMin)) / 10.0f);
|
return ptzScope;
|
}
|
|
/**
|
* 校验 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 >= 300 && 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());
|
}
|
}
|
|
/**
|
* 切换透雾
|
*
|
* @param cmd 相机命令
|
*/
|
@Override
|
public Boolean controlDefog(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
boolean enable = cmd.isEnable();
|
int userId = cmd.getLoginId().intValue();
|
|
try {
|
// 获取前端参数
|
NET_DVR_CAMERAPARAMCFG_EX cameraParam = new NET_DVR_CAMERAPARAMCFG_EX();
|
Pointer point = cameraParam.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
boolean gotParam = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_CCDPARAMCFG_EX, chanNo,
|
point, cameraParam.size(), ibrBytesReturned);
|
if (!gotParam) {
|
log.warn("【{}】获取前端参数失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
cameraParam.read();
|
log.debug("当前透雾模式: {}", cameraParam.struDefogCfg.byMode);
|
|
// 设置透雾配置
|
NET_DVR_DEFOGCFG defogCfg = new NET_DVR_DEFOGCFG();
|
if (enable) {
|
defogCfg.byMode = 2; // 常开模式
|
defogCfg.byLevel = 100; // 0-100
|
} else {
|
defogCfg.byMode = 0; // 不启用
|
}
|
cameraParam.struDefogCfg = defogCfg;
|
cameraParam.write();
|
|
boolean success = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_CCDPARAMCFG_EX,
|
chanNo, point, cameraParam.size());
|
if (!success) {
|
log.warn("【{}】切换透雾失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
log.debug("【{}】切换透雾成功", cameraId);
|
return true;
|
|
} catch (Exception ex) {
|
log.error("切换透雾异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
@Override
|
public Boolean isEnableDefog(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
|
try {
|
NET_DVR_CAMERAPARAMCFG_EX strutCameraParam = new NET_DVR_CAMERAPARAMCFG_EX();
|
Pointer point = strutCameraParam.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
boolean success = hCNetSDK.NET_DVR_GetDVRConfig(
|
userId,
|
NET_DVR_GET_CCDPARAMCFG_EX,
|
chanNo,
|
point,
|
strutCameraParam.size(),
|
ibrBytesReturned
|
);
|
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】获取前端参数失败,错误码:{}, 原因:{}", cameraId, errorCode, errorDesc);
|
return false; // SDK 层只返回 true/false
|
}
|
|
strutCameraParam.read();
|
log.debug("【{}】是否开启透雾:{}", cameraId, strutCameraParam.struDefogCfg.byMode);
|
return strutCameraParam.struDefogCfg.byMode != 0;
|
|
} catch (Exception e) {
|
log.error("【{}】查询透雾异常: {}", cameraId, e.getMessage());
|
return false;
|
}
|
}
|
|
/**
|
* 切换红外
|
*
|
* @param cmd 相机命令
|
* @return com.ard.common.core.web.domain.AjaxResult
|
* @author 刘苏义
|
* @date 2024/9/5 17:21
|
*/
|
@Override
|
public Boolean controlInfrared(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
boolean enable = cmd.isEnable();
|
Integer chanNo = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
|
try {
|
NET_DVR_CAMERAPARAMCFG_EX cameraParamCfg = new NET_DVR_CAMERAPARAMCFG_EX();
|
Pointer point = cameraParamCfg.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
// 获取当前参数
|
boolean b_GetCameraParam = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_CCDPARAMCFG, chanNo, point,
|
cameraParamCfg.size(), ibrBytesReturned);
|
if (!b_GetCameraParam) {
|
log.warn("【{}】获取前端参数失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
cameraParamCfg.read();
|
log.debug("当前红外状态:{}", cameraParamCfg.struDayNight.byDayNightFilterType == 1 ? "夜晚" : "白天");
|
|
// 设置红外
|
NET_DVR_DAYNIGHT dayNight = new NET_DVR_DAYNIGHT();
|
dayNight.byDayNightFilterType = (byte) (enable ? 1 : 0); // 夜晚/白天
|
dayNight.bySwitchScheduleEnabled = 1;
|
dayNight.byDayNightFilterTime = 60;
|
cameraParamCfg.struDayNight = dayNight;
|
cameraParamCfg.write();
|
|
boolean success = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_CCDPARAMCFG, chanNo, point,
|
cameraParamCfg.size());
|
if (!success) {
|
log.warn("【{}】切换红外失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
log.info("【{}】切换红外成功", cameraId);
|
return true;
|
|
} catch (Exception e) {
|
log.error("【{}】切换红外异常: {}", cameraId, e.getMessage());
|
return false;
|
}
|
}
|
|
//查询是否开启红外
|
@Override
|
public Boolean isEnableInfrared(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
|
try {
|
NET_DVR_CAMERAPARAMCFG_EX cameraParamCfg = new NET_DVR_CAMERAPARAMCFG_EX();
|
Pointer point = cameraParamCfg.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
boolean success = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_CCDPARAMCFG, chanNo, point,
|
cameraParamCfg.size(), ibrBytesReturned);
|
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("【{}】获取前端参数失败,错误码:{},原因:{}", cameraId, errorCode, errorDesc);
|
return false;
|
}
|
|
cameraParamCfg.read();
|
// 1表示夜晚/红外开启,0表示白天/红外关闭
|
return cameraParamCfg.struDayNight.byDayNightFilterType == 1;
|
} catch (Exception ex) {
|
log.error("【{}】查询红外开关异常", cameraId, ex);
|
return false;
|
}
|
}
|
|
//聚焦开关
|
@Override
|
public Boolean controlFocusMode(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
boolean enable = cmd.isEnable();
|
Integer channelNum = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
|
NET_DVR_FOCUSMODE_CFG focusModeCfg = new NET_DVR_FOCUSMODE_CFG();
|
Pointer point = focusModeCfg.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
// 获取当前聚焦模式
|
boolean got = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_FOCUSMODECFG, channelNum, point,
|
focusModeCfg.size(), ibrBytesReturned);
|
if (!got) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取前端参数失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
focusModeCfg.read();
|
log.debug("当前聚焦模式:{}", focusModeCfg.byFocusMode);
|
|
// 设置聚焦模式
|
if (enable) {
|
focusModeCfg.byFocusMode = 1; // 手动聚焦
|
focusModeCfg.byAutoFocusMode = 0;
|
} else {
|
focusModeCfg.byFocusMode = 2; // 自动聚焦
|
focusModeCfg.byAutoFocusMode = 1;
|
}
|
focusModeCfg.byFocusDefinitionDisplay = 1;
|
focusModeCfg.byFocusSpeedLevel = 3;
|
focusModeCfg.write();
|
|
boolean setOk = hCNetSDK.NET_DVR_SetDVRConfig(userId, NET_DVR_SET_FOCUSMODECFG, channelNum, point,
|
focusModeCfg.size());
|
if (!setOk) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置聚焦模式失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
log.debug("设置聚焦模式成功");
|
return true;
|
}
|
|
//获取聚焦模式
|
@Override
|
public Boolean getFocusMode(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer channelNum = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_FOCUSMODE_CFG strutFocusMode = new NET_DVR_FOCUSMODE_CFG();
|
Pointer point = strutFocusMode.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
boolean b_GetCameraParam = hCNetSDK.NET_DVR_GetDVRConfig(userId, NET_DVR_GET_FOCUSMODECFG, channelNum, point,
|
strutFocusMode.size(), ibrBytesReturned);
|
if (!b_GetCameraParam) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取聚焦模式失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
throw new CameraSDKException(errorCode, cameraId, errorMsg);
|
}
|
|
strutFocusMode.read();
|
|
// 判断手动/自动聚焦
|
// 0-自动,1-手动,2-半自动
|
return strutFocusMode.byFocusMode == 1; // true=手动
|
}
|
|
//云台加热开关
|
@Override
|
public Boolean controlPTHeater(CameraCmd cmd) {
|
boolean enable = cmd.isEnable();
|
Integer channelNum = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
|
int dwStop = enable ? 0 : 1; // 0=开启, 1=关闭
|
|
boolean bool = hCNetSDK.NET_DVR_PTZControl_Other(userId, channelNum, HEATER_PWRON, dwStop);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置云台加热失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
log.debug("设置云台加热成功");
|
return true;
|
}
|
|
//镜头加热开关
|
@Override
|
public Boolean controlCameraDeicing(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
boolean enable = cmd.isEnable();
|
Integer channelNum = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
|
NET_DVR_DEVSERVER_CFG devServerCfg = new NET_DVR_DEVSERVER_CFG();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
// 获取当前配置
|
boolean b_GetCameraParam = hCNetSDK.NET_DVR_GetDVRConfig(
|
userId, NET_DVR_GET_DEVSERVER_CFG, channelNum,
|
devServerCfg.getPointer(), devServerCfg.size(), ibrBytesReturned);
|
if (!b_GetCameraParam) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取前端参数失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
devServerCfg.read();
|
log.debug("当前镜头除冰状态:{}", devServerCfg.byEnableDeicing);
|
|
// 设置除冰状态
|
devServerCfg.byEnableDeicing = enable ? (byte) 1 : (byte) 0;
|
devServerCfg.write();
|
|
boolean bool = hCNetSDK.NET_DVR_SetDVRConfig(
|
userId, NET_DVR_SET_DEVSERVER_CFG, channelNum,
|
devServerCfg.getPointer(), devServerCfg.size());
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("设置镜头除冰失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return false;
|
}
|
|
log.debug("设置镜头除冰成功");
|
return true;
|
}
|
|
//截图 返给前端
|
@Override
|
public byte[] localCapture(CameraCmd cmd) {
|
Integer channelNum = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
|
// 1. 校验设备工作状态
|
NET_DVR_WORKSTATE_V30 workState = new NET_DVR_WORKSTATE_V30();
|
if (!hCNetSDK.NET_DVR_GetDVRWorkState_V30(userId, workState)) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("获取设备工作状态失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return null;
|
}
|
|
// 2. JPEG 参数
|
NET_DVR_JPEGPARA jpegPara = new NET_DVR_JPEGPARA();
|
jpegPara.wPicSize = 0; // 使用当前分辨率
|
jpegPara.wPicQuality = 0; // 最好质量
|
|
// 3. 抓图到内存
|
int bufferSize = 1024 * 1024; // 1MB
|
ByteBuffer jpegBuffer = ByteBuffer.allocate(bufferSize);
|
IntByReference outLen = new IntByReference(0);
|
|
boolean success = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(
|
userId,
|
channelNum,
|
jpegPara,
|
jpegBuffer,
|
bufferSize,
|
outLen
|
);
|
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
log.warn("抓图失败,错误码:{},原因:{}", errorCode, errorDesc);
|
return null;
|
}
|
|
// 4. 只取有效长度的数据
|
byte[] imageBytes = new byte[outLen.getValue()];
|
jpegBuffer.rewind();
|
jpegBuffer.get(imageBytes);
|
|
return imageBytes;
|
}
|
|
|
//截图 存服务器
|
@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 userId = cmd.getLoginId().intValue();
|
|
//图片信息
|
NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
|
//设置图片分辨率
|
jpeg.wPicSize = 0;
|
//设置图片质量:0-最好,1-较好,2-一般
|
jpeg.wPicQuality = 0;
|
IntByReference a = new IntByReference();
|
//设置图片大小
|
ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
|
// 抓图到内存
|
boolean bool = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(userId, chanNo, jpeg, jpegBuffer, 1024 * 1024, a);
|
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("抓图失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return "";
|
}
|
|
log.debug("hikSdk(抓图)-结果状态值(0表示成功):{}", hCNetSDK.NET_DVR_GetLastError());
|
|
// 获取实际图片数据
|
int actualLength = a.getValue();
|
byte[] imageBytes;
|
if (actualLength > 0 && actualLength < 1024 * 1024) {
|
imageBytes = new byte[actualLength];
|
System.arraycopy(jpegBuffer.array(), 0, imageBytes, 0, actualLength);
|
} else {
|
imageBytes = jpegBuffer.array();
|
}
|
|
// 存储到minio
|
String objectName = cameraId + "_" + chanNo + ".jpeg";
|
String url = "";
|
File tempFile = null;
|
|
try {
|
// 创建临时文件
|
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) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String type = cmd.getType() == null ? "other" : cmd.getType();
|
Integer userId = cmd.getLoginId().intValue();
|
|
//图片信息
|
NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
|
//设置图片分辨率
|
jpeg.wPicSize = 0;
|
//设置图片质量:0-最好,1-较好,2-一般
|
jpeg.wPicQuality = 0;
|
IntByReference a = new IntByReference();
|
//设置图片大小
|
ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
|
// 抓图到内存,单帧数据捕获并保存成JPEG存放在指定的内存空间中
|
boolean bool = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(userId, chanNo, jpeg, jpegBuffer, 1024 * 1024, a);
|
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("抓图失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return "";
|
}
|
|
// 获取实际图片数据(只复制有效长度)
|
int actualLength = a.getValue();
|
byte[] imageBytes;
|
if (actualLength > 0 && actualLength < 1024 * 1024) {
|
imageBytes = new byte[actualLength];
|
System.arraycopy(jpegBuffer.array(), 0, imageBytes, 0, actualLength);
|
} else {
|
imageBytes = jpegBuffer.array();
|
}
|
|
String url = "";
|
File tempFile = null;
|
|
try {
|
// 创建临时文件
|
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 record(CameraCmd cmd) {
|
String url = "";
|
File tempFile = null;
|
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
// 本地临时录像地址
|
String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
|
boolean enable = cmd.isEnable();
|
Integer userId = cmd.getLoginId().intValue();
|
|
//强制I帧结构体对象
|
NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME();
|
netDvrIFrame.read();
|
netDvrIFrame.dwChannel = chanNo;
|
netDvrIFrame.byStreamType = 0;
|
netDvrIFrame.dwSize = netDvrIFrame.size();
|
netDvrIFrame.write();
|
|
if (!hCNetSDK.NET_DVR_RemoteControl(userId, 3402, netDvrIFrame.getPointer(), netDvrIFrame.dwSize)) {
|
log.error("强制I帧 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
|
}
|
|
//预览参数
|
NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
|
previewInfo.read();
|
previewInfo.lChannel = chanNo;
|
previewInfo.dwStreamType = 0;
|
previewInfo.dwLinkMode = 0;
|
previewInfo.hPlayWnd = null;
|
previewInfo.bBlocked = 0;
|
previewInfo.byNPQMode = 0;
|
previewInfo.write();
|
|
if (enable) {
|
// 开始录像
|
if (!GlobalVariable.previewMap.containsKey(name)) {
|
int lRealHandle = hCNetSDK.NET_DVR_RealPlay_V40(userId, previewInfo, null, null);
|
if (lRealHandle == -1) {
|
log.error("取流失败:{}", hCNetSDK.NET_DVR_GetLastError());
|
return "";
|
}
|
log.debug("取流成功");
|
GlobalVariable.previewMap.put(name, lRealHandle);
|
}
|
|
if (!hCNetSDK.NET_DVR_SaveRealData_V30((int) GlobalVariable.previewMap.get(name), 2, path)) {
|
log.error("保存视频文件到临时文件夹失败 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
|
return "";
|
}
|
log.debug("录像开始");
|
|
} else {
|
// 停止录像
|
if (GlobalVariable.previewMap.containsKey(name)) {
|
Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
|
hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
|
GlobalVariable.previewMap.remove(name);
|
}
|
log.debug("录像停止");
|
|
// 检查录像文件是否存在
|
File videoFile = new File(path);
|
if (!videoFile.exists() || videoFile.length() == 0) {
|
log.error("录像文件不存在或为空: {}", path);
|
return "";
|
}
|
|
// 存入minio - 使用 FileMultipartFile
|
String bucketName = "record";
|
String objectName = cameraId + "_" + chanNo + ".mp4";
|
|
// 创建临时文件对象(实际上 path 已经是文件路径)
|
tempFile = new File(path);
|
|
// 使用 FileMultipartFile 转换
|
MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.VIDEO_MP4);
|
|
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName);
|
if (sysFileR.getCode() == R.SUCCESS) {
|
url = sysFileR.getData().getUrl();
|
log.debug("上传文件成功:{}", url);
|
}
|
}
|
|
return url;
|
|
} catch (Exception ex) {
|
log.error("录像异常:{}", ex.getMessage());
|
return "";
|
}
|
}
|
|
@Override
|
public Boolean recordStart(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
// 本地临时录像地址
|
String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
|
Integer userId = cmd.getLoginId().intValue();
|
//强制I帧结构体对象
|
NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME(); //新建结构体对象
|
netDvrIFrame.read();
|
netDvrIFrame.dwChannel = chanNo;//因为上文代码中设置了通道号,按照上文中的设置
|
netDvrIFrame.byStreamType = 0;
|
netDvrIFrame.dwSize = netDvrIFrame.size();
|
netDvrIFrame.write();
|
if (!hCNetSDK.NET_DVR_RemoteControl(userId, 3402, netDvrIFrame.getPointer(), netDvrIFrame.dwSize)) {
|
int code = hCNetSDK.NET_DVR_GetLastError();
|
log.error("设置强制I帧错误:{}", SdkErrorCodeEnum.getDescByCode(code) + "(" + code + ")");
|
}
|
//预览参数
|
NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
|
previewInfo.read();
|
previewInfo.lChannel = chanNo;
|
previewInfo.dwStreamType = 0;//码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推
|
previewInfo.dwLinkMode = 0;//连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输),7
|
// -RTSP/HTTPS,8-NPQ
|
previewInfo.hPlayWnd = null;//播放窗口的句柄,为NULL表示不解码显示。
|
previewInfo.bBlocked = 0;//0- 非阻塞取流,1-阻塞取流
|
previewInfo.byNPQMode = 0;//NPQ模式:0-直连模式,1-过流媒体模式
|
previewInfo.write();
|
if (GlobalVariable.previewMap.containsKey(name)) {
|
Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
|
hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
|
GlobalVariable.previewMap.remove(name);
|
log.debug("停止当前录像");
|
}
|
int lRealHandle = hCNetSDK.NET_DVR_RealPlay_V40(userId, previewInfo, null, null);
|
if (lRealHandle == -1) {
|
log.error("取流失败:{}", hCNetSDK.NET_DVR_GetLastError());
|
return false;
|
}
|
log.debug("取流成功");
|
GlobalVariable.threadMap.put(cameraId, Thread.currentThread().getName());
|
GlobalVariable.previewMap.put(name, lRealHandle);
|
if (!hCNetSDK.NET_DVR_SaveRealData_V30((int) GlobalVariable.previewMap.get(name), 2, path)) {
|
log.error("保存视频文件到临时文件夹失败 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
|
return false;
|
}
|
log.debug("录像开始");
|
return true;
|
} catch (Exception ex) {
|
log.error("开始录像异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
@Override
|
public String recordStop(CameraCmd cmd) {
|
String url = "";
|
File videoFile = null;
|
File transcodeFile = null;
|
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
// 本地临时录像地址
|
String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
|
|
// 停止录像
|
if (GlobalVariable.previewMap.containsKey(name)) {
|
Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
|
hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
|
GlobalVariable.previewMap.remove(name);
|
}
|
log.debug("录像停止");
|
|
// 检查原录像文件是否存在
|
videoFile = new File(path);
|
if (!videoFile.exists() || videoFile.length() == 0) {
|
log.error("录像文件不存在或为空: {}", path);
|
return "";
|
}
|
|
// ffmpeg转码
|
StopWatch watchStop = new StopWatch();
|
watchStop.start();
|
String newpath = FileUtils.createFile(tempDir + "/record/" + name + "_c.mp4");
|
transcodeFile = new File(newpath);
|
FFmpegUtils.transcodeToMP4(path, newpath);
|
watchStop.stop();
|
log.warn("转码耗时:{}", watchStop.getTotalTimeMillis());
|
|
// 检查转码后的文件是否存在
|
if (!transcodeFile.exists() || transcodeFile.length() == 0) {
|
log.error("转码文件不存在或为空: {}", newpath);
|
return "";
|
}
|
|
// 存入minio - 使用 FileMultipartFile
|
String bucketName = "record";
|
String objectName = cameraId + "_" + chanNo + ".mp4";
|
String type = cmd.getType();
|
|
MultipartFile multipartFile = new FileMultipartFile(transcodeFile, objectName, MimeTypeUtils.VIDEO_MP4);
|
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
|
|
if (sysFileR.getCode() == R.SUCCESS) {
|
url = sysFileR.getData().getUrl();
|
log.info("上传文件成功:{}", url);
|
}
|
|
return url;
|
|
} catch (Exception ex) {
|
log.error("录像异常:{}", ex.getMessage());
|
return "";
|
} finally {
|
// 清理临时文件
|
try {
|
if (videoFile != null && videoFile.exists()) {
|
videoFile.delete();
|
log.debug("删除原录像文件: {}", videoFile.getPath());
|
}
|
if (transcodeFile != null && transcodeFile.exists()) {
|
transcodeFile.delete();
|
log.debug("删除转码文件: {}", transcodeFile.getPath());
|
}
|
} catch (Exception e) {
|
log.warn("删除临时文件失败", e);
|
}
|
}
|
}
|
|
//获取IP通道
|
@Override
|
public List<ArdChannel> getChannels(ArdCamera camera) {
|
//获取通道
|
List<ArdChannel> channelList = new ArrayList<>();
|
int loginId = camera.getLoginId().intValue();
|
int startChan = camera.getStartChan();
|
int chanNum = camera.getChanNum();
|
|
// 1. 资源复用:在循环外创建结构体和引用,减少 GC 压力
|
NET_DVR_PICCFG_V40 strPicCfg = new NET_DVR_PICCFG_V40();
|
int structSize = strPicCfg.size();
|
strPicCfg.dwSize = structSize; // 初始化大小
|
IntByReference pInt = new IntByReference(0);
|
|
for (int i = 0; i < chanNum; i++) {
|
{
|
int chanNo = i + startChan;
|
ArdChannel channel = new ArdChannel();
|
strPicCfg.dwSize = structSize;
|
strPicCfg.write();
|
boolean success = hCNetSDK.NET_DVR_GetDVRConfig(loginId,
|
HCNetSDK.NET_DVR_GET_PICCFG_V40, chanNo, strPicCfg.getPointer(), structSize, pInt);
|
if (!success) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取通道【{}】失败,错误码:%s,原因:%s", chanNo, errorCode, errorDesc);
|
log.debug(errorMsg);
|
continue;
|
}
|
// 3. 读取结果
|
strPicCfg.read();
|
// 获取通道名称
|
String channelName;
|
try {
|
// 尝试 GBK,如果全是乱码或空,可根据业务需求尝试 UTF-8 (视具体设备固件而定)
|
channelName = new String(strPicCfg.sChanName, "GBK").trim();
|
} catch (UnsupportedEncodingException e) {
|
channelName = "通道" + chanNo;
|
log.error("字符集解码失败", e);
|
}
|
if (channelName.isEmpty()) {
|
channelName = "通道" + chanNo;
|
}
|
channel.setDeviceId(camera.getId());
|
channel.setName(channelName);
|
channel.setChanNo(chanNo);
|
// 获取视频编码
|
CameraCmd cameraCmd = new CameraCmd(camera.getId(), chanNo);
|
cameraCmd.setLoginId(camera.getLoginId());
|
VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(cameraCmd);
|
if (StringUtils.isNotNull(videoCompressionCfg)) {
|
channel.setVideoCode(videoCompressionCfg.getVideoEncType());
|
}
|
// 获取rtsp地址
|
String rtspUrl = String.format("rtsp://%s:%s@%s:%d/h264/ch%d/main/av_stream",
|
camera.getUsername(),
|
camera.getPassword(),
|
camera.getIp(),
|
camera.getRtspPort(),
|
channel.getChanNo()
|
);
|
channel.setLiveAddress(rtspUrl);
|
channelList.add(channel);
|
}
|
}
|
return channelList;
|
}
|
|
//获取GIS信息数据
|
@Override
|
public PtzParamDTO getGisInfo(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
Integer userId = cmd.getLoginId().intValue();
|
NET_DVR_STD_CONFIG stdCfg = new NET_DVR_STD_CONFIG();
|
NET_DVR_GIS_INFO gisInfo = new NET_DVR_GIS_INFO();
|
stdCfg.read();
|
IntByReference channelRef = new IntByReference(chanNo);
|
stdCfg.lpCondBuffer = channelRef.getPointer();
|
stdCfg.dwCondSize = 4;
|
stdCfg.lpOutBuffer = gisInfo.getPointer();
|
stdCfg.dwOutSize = gisInfo.size();
|
stdCfg.write();//设置前之前要write()
|
boolean bool = hCNetSDK.NET_DVR_GetSTDConfig(userId, NET_DVR_GET_GISINFO, stdCfg);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取GIS信息数据失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return new PtzParamDTO();
|
}
|
gisInfo.read();
|
PtzParamDTO ptzParamDTO = new PtzParamDTO();
|
ptzParamDTO.setP(gisInfo.struPtzPos.fPanPos);
|
ptzParamDTO.setT(gisInfo.struPtzPos.fTiltPos < 0 ? gisInfo.struPtzPos.fTiltPos + 360 :
|
gisInfo.struPtzPos.fTiltPos);
|
ptzParamDTO.setZ(gisInfo.struPtzPos.fZoomPos);
|
ptzParamDTO.setFHorFieldAngle(gisInfo.fHorizontalValue);
|
ptzParamDTO.setFVerFieldAngle(gisInfo.fVerticalValue);
|
return ptzParamDTO;
|
}
|
|
@Override
|
public Double correctPitch(CameraCmd cmd) {
|
double pitchAngle = 0.0;
|
try {
|
Point targetPositions = cmd.getTargetPosition();
|
ArdTerrainMark ardTerrainMark = new ArdTerrainMark();
|
ardTerrainMark.setCameraId(cmd.getCameraId());
|
List<ArdTerrainMark> ardTerrainMarks = ardTerrainMarkService.selectArdTerrainMarkList(ardTerrainMark);
|
//过滤500米范围内的集合
|
ardTerrainMarks = ardTerrainMarks.stream().filter(n -> GisUtil.getDistance(targetPositions,
|
new Point(n.getLongitude(), n.getLatitude(), n.getAltitude())) <= 500).collect(Collectors.toList());
|
//计算T平均值
|
OptionalDouble averageT = ardTerrainMarks.stream().mapToDouble(ArdTerrainMark::getT).average();
|
// 输出
|
if (averageT.isPresent()) {
|
pitchAngle = averageT.getAsDouble();
|
log.debug("修正俯仰值:{}", pitchAngle);
|
}
|
} catch (Exception ex) {
|
log.error("修正俯仰值异常:{}", ex.getMessage());
|
|
}
|
return pitchAngle;
|
}
|
|
//3D定位
|
@Override
|
public Boolean set3DPosition(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
PointFrame pointFrame = cmd.getPointFrame();
|
int userId = cmd.getLoginId().intValue();
|
NET_DVR_POINT_FRAME netDvrPointFrame = new NET_DVR_POINT_FRAME();
|
|
netDvrPointFrame.xTop = pointFrame.getXTop();
|
netDvrPointFrame.yTop = pointFrame.getYTop();
|
netDvrPointFrame.xBottom = pointFrame.getXBottom();
|
netDvrPointFrame.yBottom = pointFrame.getYBottom();
|
netDvrPointFrame.write();
|
boolean bool = hCNetSDK.NET_DVR_PTZSelZoomIn_EX(userId, chanNo, netDvrPointFrame);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("3D定位失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
}
|
|
@Override
|
public DeviceCFG getSystemInfo(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
int userId = cmd.getLoginId().intValue();
|
|
NET_DVR_DEVICECFG_V40 netDvrDevicecfgV40 = new NET_DVR_DEVICECFG_V40();
|
netDvrDevicecfgV40.write();
|
Pointer point = netDvrDevicecfgV40.getPointer();
|
IntByReference ibrBytesReturned = new IntByReference(0);
|
|
boolean bool = hCNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_DEVICECFG_V40, chanNo, point,
|
netDvrDevicecfgV40.size(), ibrBytesReturned);
|
if (!bool) {
|
int errorCode = hCNetSDK.NET_DVR_GetLastError();
|
String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
|
String errorMsg = String.format("获取系统信息失败,错误码:%s,原因:%s", errorCode, errorDesc);
|
log.warn(errorMsg);
|
return null;
|
}
|
netDvrDevicecfgV40.read();
|
DeviceCFG deviceCFG = new DeviceCFG();
|
deviceCFG.setSDVRName(new String(netDvrDevicecfgV40.sDVRName, StandardCharsets.UTF_8));
|
deviceCFG.setDwSoftwareVersion(parseVersion(netDvrDevicecfgV40.dwSoftwareVersion));
|
log.debug("获取系统信息成功:{}", deviceCFG);
|
return deviceCFG;
|
|
}
|
|
public static String parseVersion(int dwSoftwareVersion) {
|
int major, minor, patch;
|
|
if (dwSoftwareVersion >= 0x03000000) { // V3.0以上
|
major = (dwSoftwareVersion >> 24) & 0xFF; // 高8位
|
minor = (dwSoftwareVersion >> 16) & 0xFF; // 次高8位
|
patch = dwSoftwareVersion & 0xFFFF; // 低16位
|
} else { // V3.0以下
|
major = (dwSoftwareVersion >> 16) & 0xFFFF; // 高16位
|
minor = dwSoftwareVersion & 0xFFFF; // 低16位
|
patch = 0; // 无修复版本号
|
}
|
|
return String.format("%d.%d.%d", major, minor, patch);
|
}
|
|
/**
|
* 动态库加载
|
*/
|
private static boolean CreateSDKInstance() {
|
if (hCNetSDK == null) {
|
synchronized (HCNetSDK.class) {
|
String strDllPath = "";
|
try {
|
String userDir = System.getProperty("user.dir");
|
|
if (osSelect.isWindows()) {
|
strDllPath = userDir + "\\lib\\hik\\win64\\HCNetSDK.dll";
|
} else if (osSelect.isLinux()) {
|
if (osSelect.isArm64()) {
|
strDllPath = userDir + "/lib/hik/linux-arm64/libhcnetsdk.so";
|
} else if (osSelect.isX64()) {
|
strDllPath = userDir + "/lib/hik/linux64/libhcnetsdk.so";
|
} else {
|
throw new UnsupportedOperationException("❌ 不支持的 Linux 架构: " + osSelect.getArch());
|
}
|
} else {
|
throw new UnsupportedOperationException("❌ 不支持的操作系统: " + osSelect.getOsName());
|
}
|
|
// 校验文件是否存在
|
File sdkFile = new File(strDllPath);
|
if (!sdkFile.exists()) {
|
System.err.println("❌ SDK 动态库不存在: " + strDllPath);
|
return false;
|
}
|
|
System.out.println("✅ 尝试加载动态库: " + strDllPath);
|
hCNetSDK = Native.loadLibrary(strDllPath, HCNetSDK.class);
|
System.out.println("✅ SDK 加载成功!");
|
} catch (Exception ex) {
|
System.err.println("❌ SDK 加载失败: " + strDllPath + "\n异常信息: " + ex.getMessage());
|
ex.printStackTrace();
|
return false;
|
}
|
}
|
}
|
return true;
|
}
|
|
@Override
|
public List<Map<String, Object>> getCameraVideoLable(Long userId, CameraCmd cmd) throws ServiceException {
|
Long loginId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, cmd.getCameraId());
|
if (loginId == null) {
|
throw new ServiceException("相机不在线");
|
}
|
cmd.setLoginId(loginId);
|
PtzParamDTO data = getGisInfo(cmd);
|
ArdCamera ardCamera = redisService.getCacheMapValue(CacheConstants.CAMERA_LIST, cmd.getCameraId());
|
List<ArdWell> ardWellResult = ardWellService.selectArdWellByUserId(userId);
|
if (ardCamera.getTowerId() == null) {
|
throw new ServiceException("该光电没有挂接塔");
|
}
|
ArdTower ardTower = ardTowerService.selectArdTowerById(ardCamera.getTowerId());
|
/*Float p = (Float) data.get("p");
|
Float t = (Float) data.get("t");
|
Float fHorFieldAngle = (Float) data.get("fHorFieldAngle");// 水平视场角
|
Float fVerFieldAngle = (Float) data.get("fVerFieldAngle");// 垂直视场角*/
|
Float p = data.getP();
|
Float t = data.getT();
|
Float fHorFieldAngle = data.getFHorFieldAngle();// 水平视场角
|
Float fVerFieldAngle = data.getFVerFieldAngle();// 垂直视场角
|
if (t > 359.0) {
|
throw new ServiceException("俯仰角过小");
|
}
|
|
//小三角形腰
|
Double xy =
|
Math.tan(Math.PI / 2 - (Math.PI * 2 - Math.PI * t / 180) - Math.PI * fVerFieldAngle / 180 / 2) * ardCamera.getAltitude() / Math.cos(Math.PI * fHorFieldAngle / 180 / 2);
|
//大三角形腰
|
Double dy =
|
Math.tan(Math.PI / 2 - (Math.PI * 2 - Math.PI * t / 180) + Math.PI * fVerFieldAngle / 180 / 2) * ardCamera.getAltitude() / Math.cos(Math.PI * fHorFieldAngle / 180 / 2);
|
|
if (dy < 0) {
|
dy = -1 * dy;
|
}
|
|
if (xy < 0) {
|
xy = -1 * xy;
|
}
|
|
Double lonA =
|
ardCamera.getLongitude() + xy * Math.sin((p + fHorFieldAngle / 2) * Math.PI / 180) * 180 / (Math.PI * 6371229 * Math.cos(ardCamera.getLatitude() * Math.PI / 180));
|
Double latA =
|
ardCamera.getLatitude() + xy * Math.cos((p + fHorFieldAngle / 2) * Math.PI / 180) / (Math.PI * 6371229 / 180);
|
|
System.out.println("A点经度:" + lonA);
|
System.out.println("A点纬度:" + latA);
|
|
Double lonB =
|
ardCamera.getLongitude() + xy * Math.sin((p - fHorFieldAngle / 2) * Math.PI / 180) * 180 / (Math.PI * 6371229 * Math.cos(ardCamera.getLatitude() * Math.PI / 180));
|
Double latB =
|
ardCamera.getLatitude() + xy * Math.cos((p - fHorFieldAngle / 2) * Math.PI / 180) / (Math.PI * 6371229 / 180);
|
|
System.out.println("B点经度:" + lonB);
|
System.out.println("B点纬度:" + latB);
|
|
Double lonC =
|
ardCamera.getLongitude() + dy * Math.sin((p + fHorFieldAngle / 2) * Math.PI / 180) * 180 / (Math.PI * 6371229 * Math.cos(ardCamera.getLatitude() * Math.PI / 180));
|
Double latC =
|
ardCamera.getLatitude() + dy * Math.cos((p + fHorFieldAngle / 2) * Math.PI / 180) / (Math.PI * 6371229 / 180);
|
|
System.out.println("C点经度:" + lonC);
|
System.out.println("C点纬度:" + latC);
|
|
Double lonD =
|
ardCamera.getLongitude() + dy * Math.sin((p - fHorFieldAngle / 2) * Math.PI / 180) * 180 / (Math.PI * 6371229 * Math.cos(ardCamera.getLatitude() * Math.PI / 180));
|
Double latD =
|
ardCamera.getLatitude() + dy * Math.cos((p - fHorFieldAngle / 2) * Math.PI / 180) / (Math.PI * 6371229 / 180);
|
|
System.out.println("D点经度:" + lonD);
|
System.out.println("D点纬度:" + latD);
|
|
//Double dg = Math.tan(Math.PI/2 - (Math.PI * 2 - Math.PI * t/180) - Math.PI * fVerFieldAngle/180/2)
|
// *ardCamera.getAltitude();//大三角形高
|
//Double xg = Math.tan(Math.PI/2 - (Math.PI * 2 - Math.PI * t/180) + Math.PI * fVerFieldAngle/180/2)
|
// *ardCamera.getAltitude();//小三角形高
|
|
//设置多边形
|
List<Point2D.Double> pts = new ArrayList<Point2D.Double>();
|
pts.add(new Point2D.Double(lonA, latA));
|
pts.add(new Point2D.Double(lonB, latB));
|
pts.add(new Point2D.Double(lonD, latD));
|
pts.add(new Point2D.Double(lonC, latC));
|
|
List<ArdWell> innerList = new ArrayList();//查找多边形内兴趣点
|
for (ArdWell ardWell : ardWellResult) {
|
Point2D.Double point = new Point2D.Double(ardWell.getLongitude(), ardWell.getLatitude());
|
if (IsPtInPoly(point, pts)) {
|
innerList.add(ardWell);
|
}
|
}
|
|
List<Map<String, Object>> result = new ArrayList();
|
|
if (ardTower.getAltitude() > 5) {//有高程
|
for (ArdWell ardWell : innerList) {
|
Map<String, Object> map = new HashMap();
|
double distance_Target = RealDistance(ardCamera.getLongitude(), ardCamera.getLatitude(),
|
ardWell.getLongitude(), ardWell.getLatitude());
|
|
double Angle_A = GetAngle(ardCamera.getLongitude(), ardCamera.getLatitude(), ardWell.getLongitude(),
|
ardWell.getLatitude());
|
//double Angle_E = Math.atan((ardCameras.getAltitude() - 0) / distance_Target) * 180 / Math.PI;//无高程
|
double Angle_E =
|
Math.atan((ardCamera.getAltitude() + ardTower.getAltitude() - ardWell.getAltitude()) / distance_Target) * 180 / Math.PI;//有高程
|
|
Angle_A = Angle_A - (p - fHorFieldAngle / 2);//视场角内方位
|
Angle_E = Angle_E - (360 - t - fVerFieldAngle / 2);//视场角内俯仰
|
if (!(Angle_E / fVerFieldAngle < 0.1 || Angle_A / fHorFieldAngle < 0.08 || Angle_A / fHorFieldAngle > 0.92)) {//最上,最左,最右去掉
|
map.put("id", ardWell.getId());
|
map.put("wellId", ardWell.getWellId());
|
map.put("horFieldProportion", Angle_A / fHorFieldAngle);//横向占比
|
map.put("verFieldProportion", Angle_E / fVerFieldAngle);//纵向占比
|
result.add(map);
|
}
|
}
|
} else {//无高程
|
for (ArdWell ardWell : innerList) {
|
Map<String, Object> map = new HashMap();
|
double distance_Target = RealDistance(ardCamera.getLongitude(), ardCamera.getLatitude(),
|
ardWell.getLongitude(), ardWell.getLatitude());
|
|
double Angle_A = GetAngle(ardCamera.getLongitude(), ardCamera.getLatitude(), ardWell.getLongitude(),
|
ardWell.getLatitude());
|
double Angle_E = Math.atan((ardCamera.getAltitude() - 0) / distance_Target) * 180 / Math.PI;//无高程
|
//double Angle_E = Math.atan((ardCameras.getAltitude() + ardTowers.getAltitude() - ardAlarmpointsWell
|
// .getAltitude()) / distance_Target) * 180 / Math.PI;//有高程
|
|
Angle_A = Angle_A - (p - fHorFieldAngle / 2);//视场角内方位
|
Angle_E = Angle_E - (360 - t - fVerFieldAngle / 2);//视场角内俯仰
|
if (!(Angle_E / fVerFieldAngle < 0.1 || Angle_A / fHorFieldAngle < 0.08 || Angle_A / fHorFieldAngle > 0.92)) {//最上,最左,最右去掉
|
map.put("id", ardWell.getId());
|
map.put("wellId", ardWell.getWellId());
|
map.put("horFieldProportion", Angle_A / fHorFieldAngle);//横向占比
|
map.put("verFieldProportion", Angle_E / fVerFieldAngle);//纵向占比
|
result.add(map);
|
}
|
}
|
}
|
|
return result;
|
}
|
|
public static double RealDistance(double base_pos_longitude, double base_pos_latitude, double tar_pos_longitude,
|
double tar_pos_latitude) {
|
double er;
|
double f;
|
double g;
|
double l;
|
double sg;
|
double sl;
|
double sf;
|
double s;
|
double c;
|
double w;
|
double r;
|
double d;
|
double h1;
|
double h2;
|
double fl;
|
|
er = 6378.137;
|
fl = 1 / 298.257;
|
f = (base_pos_latitude + tar_pos_latitude) * Math.PI / 360;
|
g = (base_pos_latitude - tar_pos_latitude) * Math.PI / 360;
|
l = (base_pos_longitude - tar_pos_longitude) * Math.PI / 360;
|
|
sg = Math.sin(g);
|
sl = Math.sin(l);
|
sf = Math.sin(f);
|
|
sg = Math.pow(sg, 2);
|
sl = Math.pow(sl, 2);
|
sf = Math.pow(sf, 2);
|
|
s = sg * (1 - sl) + (1 - sf) * sl;
|
c = (1 - sg) * (1 - sl) + sf * sl;
|
|
w = Math.atan(Math.sqrt(s / c));
|
r = Math.sqrt(s * c) / w;
|
d = 2 * w * er;
|
h1 = (3 * r - 1) / 2 / c;
|
h2 = (3 * r + 1) / 2 / s;
|
|
return 1000 * d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg));//84
|
}
|
|
public static double GetAngle(double base_pos_longitude, double base_pos_latitude, double tar_pos_longitude,
|
double tar_pos_latitude) {
|
double lat1 = base_pos_latitude * Math.PI / 180;
|
double lon1 = base_pos_longitude * Math.PI / 180;
|
double lat2 = tar_pos_latitude * Math.PI / 180;
|
double lon2 = tar_pos_longitude * Math.PI / 180;
|
double d = Math.sin(lat2) * Math.sin(lat1) + Math.cos(lat2) * Math.cos(lat1) * Math.cos(lon2 - lon1);
|
if (d != 1) {
|
d = Math.asin(Math.cos(lat2) * Math.sin(lon2 - lon1) / Math.sqrt(1 - d * d));
|
d = d * 180 / Math.PI;
|
} else {
|
d = 0;
|
}
|
if (lat1 > lat2) {
|
d = 180 - d;
|
} else {
|
if (lon1 > lon2) {
|
d = 360 + d;
|
}
|
}
|
return d;
|
}
|
|
public boolean IsPtInPoly(Point2D.Double point, List<Point2D.Double> pts) {
|
int N = pts.size();
|
boolean boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
|
int intersectCount = 0;//cross points count of x
|
double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
|
Point2D.Double p1, p2;//neighbour bound vertices
|
Point2D.Double p = point; //当前点
|
|
p1 = pts.get(0);//left vertex
|
for (int i = 1; i <= N; ++i) {//check all rays
|
if (p.equals(p1)) {
|
return boundOrVertex;//p is an vertex
|
}
|
|
p2 = pts.get(i % N);//right vertex
|
if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {//ray is outside of our interests
|
p1 = p2;
|
continue;//next ray left point
|
}
|
|
if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {//ray is crossing over by the algorithm
|
// (common part of)
|
if (p.y <= Math.max(p1.y, p2.y)) {//x is before of ray
|
if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {//overlies on a horizontal ray
|
return boundOrVertex;
|
}
|
|
if (p1.y == p2.y) {//ray is vertical
|
if (p1.y == p.y) {//overlies on a vertical ray
|
return boundOrVertex;
|
} else {//before ray
|
++intersectCount;
|
}
|
} else {//cross point on the left side
|
double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;//cross point of y
|
if (Math.abs(p.y - xinters) < precision) {//overlies on a ray
|
return boundOrVertex;
|
}
|
|
if (p.y < xinters) {//before ray
|
++intersectCount;
|
}
|
}
|
}
|
} else {//special case when ray is crossing through the vertex
|
if (p.x == p2.x && p.y <= p2.y) {//p crossing over p2
|
Point2D.Double p3 = pts.get((i + 1) % N); //next vertex
|
if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {//p.x lies between p1.x & p3.x
|
++intersectCount;
|
} else {
|
intersectCount += 2;
|
}
|
}
|
}
|
p1 = p2;//next ray left point
|
}
|
|
if (intersectCount % 2 == 0) {//偶数在多边形外
|
return false;
|
} else { //奇数在多边形内
|
return true;
|
}
|
}
|
}
|