wing-ops/frontend/src/tabs/aerial/hooks/useOilDetection.ts
Nan Kyung Lee 626fea4c75 feat(aerial): CCTV 오일 감지 GPU 추론 연동 및 HNS 초기 핀 제거
CCTV 오일 유출 감지:
- GPU 추론 서버 FastAPI 서비스 (oil_inference_server.py)
- Express 프록시 엔드포인트 (POST /api/aerial/oil-detect)
- 프론트엔드 API 연동 (oilDetection.ts, useOilDetection.ts)
- 4종 유류 클래스별 색상 오버레이 (OilDetectionOverlay.tsx)
- 캡처 기능 (비디오+오버레이 합성 PNG 다운로드)
- Rate limit HLS 스트리밍 skip + 한도 500 상향

HNS 대기확산:
- 초기 핀 포인트 제거 (지도 클릭으로 선택)
- 좌표 미선택 시 안내 메시지 표시

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:31:02 +09:00

85 lines
2.4 KiB
TypeScript

import { useState, useEffect, useRef, useCallback } from 'react';
import type { OilDetectionResult, OilDetectionConfig } from '../utils/oilDetection';
import { detectOilSpillAPI, DEFAULT_OIL_DETECTION_CONFIG } from '../utils/oilDetection';
interface UseOilDetectionOptions {
videoRef: React.RefObject<HTMLVideoElement | null>;
enabled: boolean;
config?: Partial<OilDetectionConfig>;
}
interface UseOilDetectionReturn {
result: OilDetectionResult | null;
isAnalyzing: boolean;
error: string | null;
}
export function useOilDetection(options: UseOilDetectionOptions): UseOilDetectionReturn {
const { videoRef, enabled, config } = options;
const [result, setResult] = useState<OilDetectionResult | null>(null);
const [isAnalyzing, setIsAnalyzing] = useState(false);
const [error, setError] = useState<string | null>(null);
const configRef = useRef<OilDetectionConfig>({
...DEFAULT_OIL_DETECTION_CONFIG,
...config,
});
const isBusyRef = useRef(false);
useEffect(() => {
configRef.current = {
...DEFAULT_OIL_DETECTION_CONFIG,
...config,
};
}, [config]);
const analyze = useCallback(async () => {
if (isBusyRef.current) return; // 이전 호출이 진행 중이면 스킵
const video = videoRef.current;
if (!video || video.readyState < 2) return;
isBusyRef.current = true;
setIsAnalyzing(true);
try {
const detection = await detectOilSpillAPI(video, configRef.current);
setResult(detection);
setError(null);
} catch (err) {
// API 실패 시 이전 결과 유지, 에러 메시지만 갱신
const message = err instanceof Error ? err.message : '추론 서버 연결 불가';
setError(message);
console.warn('[OilDetection] API 호출 실패:', message);
} finally {
isBusyRef.current = false;
setIsAnalyzing(false);
}
}, [videoRef]);
useEffect(() => {
if (!enabled) {
setResult(null);
setIsAnalyzing(false);
setError(null);
isBusyRef.current = false;
return;
}
setIsAnalyzing(true);
// 첫 분석: 2초 후 (영상 로딩 대기)
const firstTimeout = setTimeout(analyze, 2000);
// 반복 분석
const intervalId = setInterval(analyze, configRef.current.captureIntervalMs);
return () => {
clearTimeout(firstTimeout);
clearInterval(intervalId);
};
}, [enabled, analyze]);
return { result, isAnalyzing, error };
}