|
|
|
|
@ -28,6 +28,9 @@ public class ImageCaptureService {
|
|
|
|
|
// 设备句柄映射表 (IP -> Handle)
|
|
|
|
|
private final Map<String, Handle> handleMap = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
// 设备列表(全局变量)
|
|
|
|
|
private ArrayList<MV_CC_DEVICE_INFO> stDeviceList = null;
|
|
|
|
|
|
|
|
|
|
// SDK初始化状态
|
|
|
|
|
private boolean isSdkInitialized = false;
|
|
|
|
|
|
|
|
|
|
@ -39,7 +42,10 @@ public class ImageCaptureService {
|
|
|
|
|
@PostConstruct
|
|
|
|
|
public void init() {
|
|
|
|
|
try {
|
|
|
|
|
log.info("初始化海康相机SDK...");
|
|
|
|
|
log.info("========== 开始初始化海康相机 ==========");
|
|
|
|
|
|
|
|
|
|
// 1. 初始化SDK
|
|
|
|
|
log.info("步骤1: 初始化SDK...");
|
|
|
|
|
int nRet = MvCameraControl.MV_CC_Initialize();
|
|
|
|
|
if (MV_OK != nRet) {
|
|
|
|
|
log.error("初始化SDK失败,errcode: [0x{}]", Integer.toHexString(nRet));
|
|
|
|
|
@ -48,14 +54,151 @@ public class ImageCaptureService {
|
|
|
|
|
log.info("SDK Version: {}", MV_CC_GetSDKVersion());
|
|
|
|
|
log.info("SDK初始化成功");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 枚举设备
|
|
|
|
|
log.info("步骤2: 枚举设备...");
|
|
|
|
|
try {
|
|
|
|
|
stDeviceList = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE);
|
|
|
|
|
if (stDeviceList == null || stDeviceList.isEmpty()) {
|
|
|
|
|
log.warn("未找到任何设备");
|
|
|
|
|
} else {
|
|
|
|
|
log.info("枚举到 {} 个设备", stDeviceList.size());
|
|
|
|
|
// 打印所有设备信息
|
|
|
|
|
for (int i = 0; i < stDeviceList.size(); i++) {
|
|
|
|
|
MV_CC_DEVICE_INFO device = stDeviceList.get(i);
|
|
|
|
|
if (device.transportLayerType == MV_GIGE_DEVICE) {
|
|
|
|
|
log.info("设备[{}] - IP: {}, 型号: {}",
|
|
|
|
|
i, device.gigEInfo.currentIp, device.gigEInfo.modelName);
|
|
|
|
|
} else if (device.transportLayerType == MV_USB_DEVICE) {
|
|
|
|
|
log.info("设备[{}] - USB序列号: {}",
|
|
|
|
|
i, device.usb3VInfo.serialNumber);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (CameraControlException e) {
|
|
|
|
|
log.error("枚举设备失败", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 初始化配置的相机
|
|
|
|
|
if (appConfig.getHikCamera() != null && !appConfig.getHikCamera().isEmpty()){
|
|
|
|
|
log.info("步骤3: 从配置初始化 {} 个相机...", appConfig.getHikCamera().size());
|
|
|
|
|
initCamerasFromConfig(appConfig.getHikCamera());
|
|
|
|
|
} else {
|
|
|
|
|
log.warn("配置中没有海康相机");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("========== 海康相机初始化完成 ==========");
|
|
|
|
|
log.info("已初始化相机: {}", handleMap.keySet());
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("初始化SDK异常", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 确保相机已初始化(如果未初始化则立即初始化)
|
|
|
|
|
* @param ip 相机IP
|
|
|
|
|
* @return 是否初始化成功
|
|
|
|
|
*/
|
|
|
|
|
public boolean ensureCameraInitialized(String ip) {
|
|
|
|
|
// 检查SDK是否初始化
|
|
|
|
|
if (!isSdkInitialized) {
|
|
|
|
|
log.warn("SDK未初始化,正在重新初始化...");
|
|
|
|
|
try {
|
|
|
|
|
int nRet = MvCameraControl.MV_CC_Initialize();
|
|
|
|
|
if (MV_OK != nRet) {
|
|
|
|
|
log.error("SDK重新初始化失败,errcode: [0x{}]", Integer.toHexString(nRet));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
isSdkInitialized = true;
|
|
|
|
|
log.info("SDK重新初始化成功");
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("SDK重新初始化异常", e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查相机句柄是否存在
|
|
|
|
|
if (!handleMap.containsKey(ip)) {
|
|
|
|
|
log.warn("相机 {} 未初始化,正在重新初始化...", ip);
|
|
|
|
|
// 枚举设备(如果有全局设备列表则直接使用)
|
|
|
|
|
if (stDeviceList == null || stDeviceList.isEmpty()) {
|
|
|
|
|
try {
|
|
|
|
|
stDeviceList = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE);
|
|
|
|
|
if (stDeviceList == null || stDeviceList.isEmpty()) {
|
|
|
|
|
log.error("未找到任何设备");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
log.info("重新枚举到 {} 个设备", stDeviceList.size());
|
|
|
|
|
} catch (CameraControlException e) {
|
|
|
|
|
log.error("枚举设备失败", e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 初始化单个相机
|
|
|
|
|
Handle handle = initSingleCamera(ip, stDeviceList);
|
|
|
|
|
if (handle == null) {
|
|
|
|
|
log.error("相机 {} 重新初始化失败", ip);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
log.info("相机 {} 重新初始化成功", ip);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 主动初始化所有配置的相机
|
|
|
|
|
* 可在需要时主动调用
|
|
|
|
|
*/
|
|
|
|
|
public void initAllCameras() {
|
|
|
|
|
log.info("========== 主动初始化所有相机 ==========");
|
|
|
|
|
|
|
|
|
|
// 确保SDK已初始化
|
|
|
|
|
if (!isSdkInitialized) {
|
|
|
|
|
log.warn("SDK未初始化,正在初始化...");
|
|
|
|
|
try {
|
|
|
|
|
int nRet = MvCameraControl.MV_CC_Initialize();
|
|
|
|
|
if (MV_OK != nRet) {
|
|
|
|
|
log.error("SDK初始化失败,errcode: [0x{}]", Integer.toHexString(nRet));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
isSdkInitialized = true;
|
|
|
|
|
log.info("SDK初始化成功");
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("SDK初始化异常", e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清空已初始化的相机,重新初始化
|
|
|
|
|
handleMap.clear();
|
|
|
|
|
|
|
|
|
|
// 从配置初始化
|
|
|
|
|
if (appConfig.getHikCamera() != null && !appConfig.getHikCamera().isEmpty()){
|
|
|
|
|
for (AppConfig.Camera camera : appConfig.getHikCamera()) {
|
|
|
|
|
String ip = camera.getIp();
|
|
|
|
|
log.info("初始化相机: {}", ip);
|
|
|
|
|
if (stDeviceList == null || stDeviceList.isEmpty()) {
|
|
|
|
|
try {
|
|
|
|
|
stDeviceList = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE);
|
|
|
|
|
} catch (CameraControlException e) {
|
|
|
|
|
log.error("枚举设备失败", e);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Handle handle = initSingleCamera(ip, stDeviceList);
|
|
|
|
|
if (handle != null) {
|
|
|
|
|
log.info("相机 {} 初始化成功", ip);
|
|
|
|
|
} else {
|
|
|
|
|
log.error("相机 {} 初始化失败", ip);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("========== 相机初始化完成 ==========");
|
|
|
|
|
log.info("已初始化相机: {}", handleMap.keySet());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 销毁资源
|
|
|
|
|
*/
|
|
|
|
|
@ -118,8 +261,7 @@ public class ImageCaptureService {
|
|
|
|
|
|
|
|
|
|
log.info("开始批量初始化 {} 个相机...", ipList.size());
|
|
|
|
|
|
|
|
|
|
// 枚举所有设备
|
|
|
|
|
ArrayList<MV_CC_DEVICE_INFO> stDeviceList = null;
|
|
|
|
|
// 枚举所有设备(使用全局变量)
|
|
|
|
|
try {
|
|
|
|
|
stDeviceList = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE);
|
|
|
|
|
if (stDeviceList == null || stDeviceList.isEmpty()) {
|
|
|
|
|
@ -168,6 +310,14 @@ public class ImageCaptureService {
|
|
|
|
|
return initCameras(ipList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化单个相机
|
|
|
|
|
* @param ip 相机IP
|
|
|
|
|
* @return 相机句柄
|
|
|
|
|
*/
|
|
|
|
|
private Handle initSingleCamera(String ip) {
|
|
|
|
|
return initSingleCamera(ip, stDeviceList);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 初始化单个相机
|
|
|
|
|
* @param ip 相机IP
|
|
|
|
|
@ -236,33 +386,60 @@ public class ImageCaptureService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据IP拍照并保存
|
|
|
|
|
* 根据IP拍照并保存(带重试机制)
|
|
|
|
|
* @param ip 相机IP
|
|
|
|
|
* @param savePath 保存路径
|
|
|
|
|
* @return 是否成功
|
|
|
|
|
*/
|
|
|
|
|
public boolean captureImage(String ip, String savePath) {
|
|
|
|
|
if (!isSdkInitialized) {
|
|
|
|
|
log.error("SDK未初始化");
|
|
|
|
|
return captureImageWithRetry(ip, savePath, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据IP拍照并保存(可配置重试次数)
|
|
|
|
|
* @param ip 相机IP
|
|
|
|
|
* @param savePath 保存路径
|
|
|
|
|
* @param maxRetries 最大重试次数
|
|
|
|
|
* @return 是否成功
|
|
|
|
|
*/
|
|
|
|
|
public boolean captureImageWithRetry(String ip, String savePath, int maxRetries) {
|
|
|
|
|
// 确保相机已初始化(如果未初始化则立即初始化)
|
|
|
|
|
if (!ensureCameraInitialized(ip)) {
|
|
|
|
|
log.error("相机 {} 初始化失败,无法拍照", ip);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取相机句柄
|
|
|
|
|
Handle hCamera = handleMap.get(ip);
|
|
|
|
|
if (hCamera == null) {
|
|
|
|
|
log.error("相机 {} 未初始化,请先调用init方法", ip);
|
|
|
|
|
log.warn("相机 {} 句柄为空,尝试自动初始化...", ip);
|
|
|
|
|
if (!ensureCameraInitialized(ip)) {
|
|
|
|
|
log.error("相机 {} 自动初始化失败,无法拍照", ip);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
hCamera = handleMap.get(ip);
|
|
|
|
|
if (hCamera == null) {
|
|
|
|
|
log.error("相机 {} 自动初始化后仍无法获取句柄", ip);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
log.info("相机 {} 自动初始化成功", ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("开始拍照,相机: {}, 保存路径: {}", ip, savePath);
|
|
|
|
|
log.info("开始拍照,相机: {}, 保存路径: {}, 最大重试: {} 次", ip, savePath, maxRetries);
|
|
|
|
|
|
|
|
|
|
int attempt = 0;
|
|
|
|
|
int lastErrorCode = 0;
|
|
|
|
|
|
|
|
|
|
while (attempt < maxRetries) {
|
|
|
|
|
attempt++;
|
|
|
|
|
try {
|
|
|
|
|
// 获取PayloadSize
|
|
|
|
|
MVCC_INTVALUE stParam = new MVCC_INTVALUE();
|
|
|
|
|
int nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "PayloadSize", stParam);
|
|
|
|
|
if (MV_OK != nRet) {
|
|
|
|
|
log.error("获取PayloadSize失败,errcode: [0x{}]", Integer.toHexString(nRet));
|
|
|
|
|
return false;
|
|
|
|
|
lastErrorCode = nRet;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取一帧图像
|
|
|
|
|
@ -271,22 +448,50 @@ public class ImageCaptureService {
|
|
|
|
|
nRet = MvCameraControl.MV_CC_GetOneFrameTimeout(hCamera, pData, stImageInfo, 3000);
|
|
|
|
|
|
|
|
|
|
if (MV_OK != nRet) {
|
|
|
|
|
log.error("获取图像失败,errcode: [0x{}]", Integer.toHexString(nRet));
|
|
|
|
|
return false;
|
|
|
|
|
log.warn("获取图像失败 (第{}次),errcode: [0x{}]", attempt, Integer.toHexString(nRet));
|
|
|
|
|
lastErrorCode = nRet;
|
|
|
|
|
if (attempt < maxRetries) {
|
|
|
|
|
log.info("等待200ms后重试...");
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(200);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("获取图像成功: 帧[{}] 宽[{}] 高[{}]",
|
|
|
|
|
stImageInfo.frameNum, stImageInfo.width, stImageInfo.height);
|
|
|
|
|
|
|
|
|
|
// 保存为JPEG格式
|
|
|
|
|
return saveImageAsJpeg(hCamera, pData, stImageInfo, savePath);
|
|
|
|
|
boolean saveResult = saveImageAsJpeg(hCamera, pData, stImageInfo, savePath);
|
|
|
|
|
if (saveResult) {
|
|
|
|
|
log.info("拍照成功!共尝试 {} 次", attempt);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
lastErrorCode = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("拍照异常", e);
|
|
|
|
|
return false;
|
|
|
|
|
log.error("拍照异常 (第{}次)", attempt, e);
|
|
|
|
|
lastErrorCode = -1;
|
|
|
|
|
if (attempt < maxRetries) {
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(200);
|
|
|
|
|
} catch (InterruptedException ie) {
|
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.error("拍照失败,已重试 {} 次,最后错误码: [0x{}]", attempt, Integer.toHexString(lastErrorCode));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 保存图像为JPEG格式
|
|
|
|
|
* @param hCamera 相机句柄
|
|
|
|
|
@ -415,8 +620,9 @@ public class ImageCaptureService {
|
|
|
|
|
public List<String> captureBatch(String ip, String basePath, int count) {
|
|
|
|
|
List<String> successPaths = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
if (!isSdkInitialized) {
|
|
|
|
|
log.error("SDK未初始化");
|
|
|
|
|
// 确保相机已初始化
|
|
|
|
|
if (!ensureCameraInitialized(ip)) {
|
|
|
|
|
log.error("相机 {} 初始化失败,无法连拍", ip);
|
|
|
|
|
return successPaths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|