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

186 lines
8.2 KiB

package com.inspect.nvr.service;
import com.inspect.nvr.domain.Infrared.Camera;
import com.inspect.nvr.domain.Infrared.NvrInfo;
import com.inspect.nvr.domain.Infrared.TemperatureData;
import com.inspect.nvr.jna.lincseek.IRNetSDK;
import com.inspect.nvr.jna.lincseek.IRNetSDKCallback;
import com.inspect.nvr.jna.lincseek.IRNetSDKConst;
import com.inspect.nvr.jna.lincseek.IRNetSDKStruct;
import com.inspect.nvr.jna.lincseek.IRNetSDKStruct.IRNETHANDLE;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Service
public class LincseekCameraService extends CommonCameraService {
// [新增]全局流水号生成器
private static final AtomicInteger SERIAL_COUNTER = new AtomicInteger(1);
// Key: CmdSerial (流水号), Value: CompletableFuture (用于通知调用线程)
private static final ConcurrentHashMap<String, CompletableFuture<byte[]>> PENDING_REQUESTS = new ConcurrentHashMap<>();
@Resource
private IRNetSDK irNetSDK;
@Resource
private LincseekLoginService lincseekLoginService;
public static int nextSerial() {
return SERIAL_COUNTER.updateAndGet(current -> (current + 1) & 0xFFFF);
}
public static Pointer stringToPointer(String str) {
if (str == null) return null;
byte[] bytes = str.getBytes(); // 1. 转换为字节数组
Pointer pointer = new Memory(bytes.length + 1); // 2. 分配内存(+1 用于存放 \0)
pointer.write(0, bytes, 0, bytes.length); // 3. 写入字节
pointer.setByte(bytes.length, (byte) 0); // 4. 追加空字节(Null-termination)
return pointer;
}
/**
* 受并发控制的朗驰抓图
*/
public byte[] capture(NvrInfo nvrInfo, int channel) {
return withConcurrencyControl(nvrInfo, channel, () -> captureWithRetry(nvrInfo, channel));
}
private byte[] captureWithRetry(NvrInfo nvrInfo, int channel) {
Path fullPath = getFullPath("lc", nvrInfo.getNvrIp(), channel);
ensureDirectoryExists(fullPath.getParent());
int retryCount = 0;
int maxRetries = DEFAULT_MAX_RETRIES;
while (retryCount < maxRetries) {
try {
byte[] imageBytes = jpegCapSingle(nvrInfo, channel, fullPath);
log.info("[朗驰]抓图成功:第{}次,文件地址:{}", retryCount + 1, fullPath);
return imageBytes;
} catch (Exception e) {
log.error("[朗驰]抓图异常(第{}次):{}", retryCount + 1, e.getMessage());
} finally {
retryCount++;
}
}
// 当所有抓图方式均失败时,记录失败图片
writeCaptureFailedImage(fullPath);
log.error("[朗驰]抓图失败,图片地址:{}", fullPath);
return new byte[0];
}
private byte[] jpegCapSingle(NvrInfo nvrInfo, int channel, Path fullPath) {
IRNetSDKCallback.JpegDataCallback jpegCb = (hHandle, mCh, pBuffer, size, extraData, userdata) -> {
String requestKey = userdata.getString(0);
CompletableFuture<byte[]> future = PENDING_REQUESTS.remove(requestKey);
if (future != null) {
log.info("[朗驰]匹配到抓图回调 (requestKey={}, 通道={}, 大小={}B)", requestKey, mCh, size);
byte[] jpeg = pBuffer.getByteArray(0, size);
future.complete(jpeg);
} else {
// 可能是由于超时已经被移除了,或者是其他类型的抓图
log.error("[朗驰]未匹配的抓图回调,requestKey={}", requestKey);
}
};
int mySerialId = nextSerial();
String requestKey = ":" + mySerialId;
Pointer userData = stringToPointer(requestKey);
IRNETHANDLE capHandle = irNetSDK.IRNET_ClientJpegCapStart("video server", nvrInfo.getNvrIp(), nvrInfo.getAccount(), nvrInfo.getPassword(), (short) nvrInfo.getServerPort().intValue(), jpegCb, userData);
if (!IRNETHANDLE.INVALID_HANDLE_VALUE.equals(capHandle)) {
CompletableFuture<byte[]> future = new CompletableFuture<>();
log.info("capHandle: {}, {}", capHandle.toString(), capHandle.hashCode());
PENDING_REQUESTS.put(requestKey, future);
final int TIMEOUT_SEC = 10;
try {
boolean isCaptured = irNetSDK.IRNET_ClientJpegCapSingle(capHandle, channel, 100);
if (!isCaptured) {
throw new RuntimeException("SDK抓图失败");
}
byte[] byteArray = future.get(TIMEOUT_SEC, TimeUnit.SECONDS);
Files.write(fullPath, byteArray);
return byteArray;
} catch (Exception e) {
throw new RuntimeException("[朗驰]抓图异常:", e);
} finally {
PENDING_REQUESTS.remove(requestKey);
irNetSDK.IRNET_ClientJpegCapStop(capHandle);
}
}
return null;
}
public TemperatureData getTemp(Camera camera) {
NvrInfo nvrInfo = new NvrInfo();
nvrInfo.setNvrIp(camera.getIp());
nvrInfo.setServerPort(camera.getPort());
nvrInfo.setAccount(camera.getUserName());
nvrInfo.setPassword(camera.getPassword());
int channel = camera.getChannel();
return withConcurrencyControl(nvrInfo, channel, () -> tempWithRetry(nvrInfo, channel));
}
/**
* 获取最高温、最低温、平均温
*/
private TemperatureData tempWithRetry(NvrInfo nvrInfo, int channel) {
IRNETHANDLE msgHandle = irNetSDK.IRNET_ClientMessageOpen("video server", nvrInfo.getNvrIp(), nvrInfo.getAccount(), nvrInfo.getPassword(), (short) nvrInfo.getServerPort().intValue());
try {
if (!IRNETHANDLE.INVALID_HANDLE_VALUE.equals(msgHandle)) {
IRNetSDKStruct.VSNET_TEMP_VALUE_EX temps = new IRNetSDKStruct.VSNET_TEMP_VALUE_EX();
temps.write();
int r = irNetSDK.IRNET_ClientMessageOpt(msgHandle, IRNetSDKConst.MessageOpt.MESSAGE_CMD_GET_TEMPVALUE_EX, channel, temps.getPointer(), null, null);
if (r == 1) {
temps.read();
float maxTemp = temps.m_maxtempinfo.m_temp_value;
int maxX = temps.m_maxtempinfo.m_temp_x;
int maxY = temps.m_maxtempinfo.m_temp_y;
float minTemp = temps.m_mintempinfo.m_temp_value;
int minX = temps.m_mintempinfo.m_temp_x;
int minY = temps.m_mintempinfo.m_temp_y;
float avgTemp = temps.m_avgtempinfo;
log.info("[朗驰]测温结果: 最高={}℃({},{}), 最低={}℃({},{}), 平均={}℃",
maxTemp, maxX, maxY, minTemp, minX, minY, avgTemp);
return new TemperatureData(
String.valueOf(maxTemp),
String.valueOf(minTemp),
avgTemp,
0,
channel,
-1
);
}
}
} catch (Exception e) {
log.error("[朗驰]测温异常:", e);
} finally {
irNetSDK.IRNET_ClientMessageClose(msgHandle);
}
return null;
}
/**
* 跳转预置位
*/
public boolean ptzPreset(Camera camera) {
//预置位跳转
NvrInfo nvrInfo = new NvrInfo();
nvrInfo.setNvrIp(camera.getIp());
nvrInfo.setServerPort(camera.getPort());
nvrInfo.setAccount(camera.getUserName());
nvrInfo.setPassword(camera.getPassword());
nvrInfo.setChannelNumber(camera.getChannel());
IRNETHANDLE hHandle = lincseekLoginService.login(nvrInfo);
boolean ret = irNetSDK.IRNET_ClientPTZCtrl(hHandle, IRNetSDKConst.PTZ_GOTOPOINT, camera.getPointNum(), 0, null, 0);
log.info("[朗驰]跳转预置位{}, 结果: {}", camera.getPointNum(), ret);
return ret;
}
}