diff --git a/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java b/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java index 26c80750..72bd084d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java +++ b/src/main/java/com/kamco/cd/kamcoback/gukyuin/service/GukYuinApiService.java @@ -453,7 +453,7 @@ public class GukYuinApiService { return new ResponseObj(ApiResponseCode.DUPLICATE_DATA, "이미 국유인 연동을 한 회차입니다."); } - if (!Files.isDirectory(Path.of("/kamco-nfs/dataset/export/" + info.getUid()))) { + if (!Files.isDirectory(Path.of(datasetDir + info.getUid()))) { return new ResponseObj( ApiResponseCode.NOT_FOUND_DATA, "파일 경로에 회차 실행 파일이 생성되지 않았습니다. 확인 부탁드립니다."); } @@ -468,7 +468,7 @@ public class GukYuinApiService { reqDto.setCrtrYr(String.valueOf(info.getTargetYyyy())); reqDto.setChnDtctSno(String.valueOf(maxStage + 1)); reqDto.setChnDtctId(info.getUid()); - reqDto.setPathNm("/kamco-nfs/dataset/export/" + info.getUid()); + reqDto.setPathNm(datasetDir + info.getUid()); // 1회차를 종료 상태로 처리하고 2회차를 보내야 함 // 추론(learn), 학습데이터(inference) 둘 다 종료 처리 diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceSendDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceSendDto.java index 143dd054..e5ffaab9 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceSendDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceSendDto.java @@ -5,8 +5,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; +import lombok.extern.slf4j.Slf4j; /** AI API 추론 실행 DTO */ +@Slf4j @Getter @Setter @NoArgsConstructor @@ -30,6 +32,7 @@ public class InferenceSendDto { public void changeValForProd() { if (pred_requests_areas != null) { pred_requests_areas.changeValForProd(); + log.info("[CHANGE]pred_requests_areas={}", pred_requests_areas); } if (this.cd_model_path != null) { this.cd_model_path = this.cd_model_path.replace(DEV_PATH_PREFIX, PROD_PATH_PREFIX); @@ -37,6 +40,9 @@ public class InferenceSendDto { if (this.cls_model_path != null) { this.cls_model_path = this.cls_model_path.replace(DEV_PATH_PREFIX, PROD_PATH_PREFIX); } + if (this.cd_model_config != null) { + this.cd_model_config = this.cd_model_config.replace(DEV_PATH_PREFIX, PROD_PATH_PREFIX); + } } @Getter diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index cfc0f2ef..857454d8 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -591,6 +591,7 @@ public class InferenceResultService { * * @param dto */ + // 같은함수가 왜 두개지 private Long ensureAccepted(InferenceSendDto dto) { if (dto == null) { @@ -598,6 +599,15 @@ public class InferenceResultService { throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); } + // [중복]운영환경일때 경로수정 dean 260226 + if (profile != null && profile.equals("prod")) { + log.info("========================================================"); + log.info("[CHANGE INFERENCE] profile = {} Inforence req", profile); + log.info("========================================================"); + log.info(""); + dto.changeValForProd(); + } + // 1) 요청 로그 try { log.debug("Inference request dto={}", objectMapper.writeValueAsString(dto)); @@ -700,13 +710,6 @@ public class InferenceResultService { sendDto.setCls_model_version(modelInfo.getModelVer()); sendDto.setCd_model_type(modelType); sendDto.setPriority(5d); - - // 운영환경일때 경로수정 dean 260226 - if (profile != null && profile.equals("prod")) { - log.info("profile = {} change inforence req", profile); - sendDto.changeValForProd(); - } - log.info("[Inference Send]SendDto={}", sendDto); return sendDto; } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java new file mode 100644 index 00000000..a34fe9ef --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java @@ -0,0 +1,224 @@ +package com.kamco.cd.kamcoback.inference.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.kamco.cd.kamcoback.common.exception.CustomApiException; +import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene; +import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient; +import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult; +import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas; +import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic; +import com.kamco.cd.kamcoback.model.dto.ModelMngDto.ModelType; +import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService; +import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; + +@Service +@Log4j2 +@RequiredArgsConstructor +public class InferenceRunService { + private final ExternalHttpClient externalHttpClient; + private final MapSheetMngCoreService mapSheetMngCoreService; + private final ModelMngCoreService modelMngCoreService; + private final ObjectMapper objectMapper; + + @Value("${spring.profiles.active}") + private String profile; + + @Value("${inference.url}") + private String inferenceUrl; + + // TODO 이거 쓰는건가? + public void run(Integer compareYear, Integer targetYear, UUID modelUuid) { + + List compareList = mapSheetMngCoreService.getMapSheetMngHst(compareYear); + List targetList = mapSheetMngCoreService.getMapSheetMngHst(targetYear); + + log.info( + "hst list count compareList = {}, targetList = {}", compareList.size(), targetList.size()); + + Set compareSet = new HashSet<>(compareList); + Set targetSet = new HashSet<>(targetList); + + long intersectionCount = + targetSet.stream() + .distinct() + .filter(compareSet::contains) + .count(); // compare와 target에 공통으로 존재하는 도협 수 + + long excludedTargetCount = + targetSet.stream() + .distinct() + .filter(s -> !compareSet.contains(s)) + .count(); // target 에만 존재하는 도협 수 (compare 에는 없음) + + long onlyCompareCount = + compareSet.stream() + .distinct() + .filter(s -> !targetSet.contains(s)) + .count(); // compare 에만 존재하는 도협 수 (target 에는 없음) + + log.info( + """ + ===== MapSheet Year Comparison ===== + target Total: {} + compare Total: {} + Intersection: {} + target Only (Excluded from compare): {} + compare Only: {} + ==================================== + """, + targetSet.size(), + compareSet.size(), + intersectionCount, + excludedTargetCount, + onlyCompareCount); + + List filteredTargetList = + targetSet.stream() // target 기준으로 + .filter(compareSet::contains) // compare에 있는 도협만 남김 + .toList(); + + Scene modelComparePath = getSceneInference(compareYear.toString(), filteredTargetList, "", ""); + + Scene modelTargetPath = getSceneInference(targetYear.toString(), filteredTargetList, "", ""); + + // ai 서버에 전달할 파라미터 생성 + pred_requests_areas predRequestsAreas = new pred_requests_areas(); + predRequestsAreas.setInput1_year(compareYear); + predRequestsAreas.setInput2_year(targetYear); + predRequestsAreas.setInput1_scene_path(modelComparePath.getFilePath()); + predRequestsAreas.setInput2_scene_path(modelTargetPath.getFilePath()); + + InferenceSendDto m1 = this.getModelInfo(modelUuid); + m1.setPred_requests_areas(predRequestsAreas); + + // ai 추론 실행 api 호출 + Long batchId = ensureAccepted(m1); + } + + private Scene getSceneInference( + String yyyy, List mapSheetNums, String mapSheetScope, String detectOption) { + return mapSheetMngCoreService.getSceneInference( + yyyy, mapSheetNums, mapSheetScope, detectOption); + } + + /** + * 추론 AI API 호출 + * + * @param dto + */ + private Long ensureAccepted(InferenceSendDto dto) { + + if (dto == null) { + log.warn("not InferenceSendDto dto"); + throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); + } + + // 1) 요청 로그 + try { + log.info("Inference request dto={}", objectMapper.writeValueAsString(dto)); + } catch (JsonProcessingException e) { + log.warn("Failed to serialize inference dto", e); + } + + // 3) HTTP 호출 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + ExternalCallResult result = + externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class); + + if (result.statusCode() < 200 || result.statusCode() >= 300) { + log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body()); + throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY); + } + + // 4) 응답 파싱 + try { + List> list = + objectMapper.readValue(result.body(), new TypeReference<>() {}); + + if (list.isEmpty()) { + throw new IllegalStateException("Inference response is empty"); + } + + Object batchIdObj = list.get(0).get("batch_id"); + if (batchIdObj == null) { + throw new IllegalStateException("batch_id not found in response"); + } + + return Long.valueOf(batchIdObj.toString()); + + } catch (Exception e) { + log.error("Failed to parse inference response. body={}", result.body(), e); + throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY); + } + } + + /** + * 모델정보 조회 dto 생성 후 반환 + * + * @param uuid + * @return + */ + private InferenceSendDto getModelInfo(UUID uuid) { + + Basic modelInfo = modelMngCoreService.findByModelUuid(uuid); + + String cdModelPath = ""; + String cdModelConfigPath = ""; + String cdClsModelPath = ""; + + if (modelInfo.getCdModelPath() != null && modelInfo.getCdModelFileName() != null) { + cdModelPath = + Paths.get(modelInfo.getCdModelPath(), modelInfo.getCdModelFileName()).toString(); + } + + if (modelInfo.getCdModelConfig() != null && modelInfo.getCdModelConfigFileName() != null) { + cdModelConfigPath = + Paths.get(modelInfo.getCdModelConfig(), modelInfo.getCdModelConfigFileName()).toString(); + } + + if (modelInfo.getClsModelPath() != null && modelInfo.getClsModelFileName() != null) { + cdClsModelPath = + Paths.get(modelInfo.getClsModelPath(), modelInfo.getClsModelFileName()).toString(); + } + + String modelType = ""; + + if (modelInfo.getModelType().equals(ModelType.G1.getId())) { + modelType = ModelType.G1.getId(); + } else if (modelInfo.getModelType().equals(ModelType.G2.getId())) { + modelType = ModelType.G2.getId(); + } else { + modelType = ModelType.G3.getId(); + } + + InferenceSendDto sendDto = new InferenceSendDto(); + sendDto.setModel_version(modelInfo.getModelVer()); + sendDto.setCd_model_path(cdModelPath); + sendDto.setCd_model_config(cdModelConfigPath); + sendDto.setCls_model_path(cdClsModelPath); + sendDto.setCls_model_version(modelInfo.getModelVer()); + sendDto.setCd_model_type(modelType); + sendDto.setPriority(5.0); + return sendDto; + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/MapSheetInferenceJobService.java b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/MapSheetInferenceJobService.java index ad0ed616..ee1b0b7e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/scheduler/service/MapSheetInferenceJobService.java +++ b/src/main/java/com/kamco/cd/kamcoback/scheduler/service/MapSheetInferenceJobService.java @@ -355,12 +355,7 @@ public class MapSheetInferenceJobService { m.setCls_model_version(progressDto.getClsModelVersion()); m.setCd_model_type(type); m.setPriority(5d); - // 운영환경일때 경로수정 dean 260226 - if (profile != null && profile.equals("prod")) { - log.info("profile = {} [M]change inforence req", profile); - m.changeValForProd(); - } - log.info("[Inference]SendDto={}", m); + log.info("[BEFORE INFERENCE] BEFORE SendDto={}", m); // 추론 실행 api 호출 Long batchId = ensureAccepted(m); @@ -380,15 +375,27 @@ public class MapSheetInferenceJobService { * @param dto * @return */ + // 같은함수가 왜 두개지 private Long ensureAccepted(InferenceSendDto dto) { if (dto == null) { log.warn("not InferenceSendDto dto"); throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); } + // [중복운영환경일때 경로수정 dean 260226 + if (profile != null && profile.equals("prod")) { + log.info("========================================================"); + log.info("[CHANGE INFERENCE] profile = {} Inforence req", profile); + log.info("========================================================"); + log.info(""); + dto.changeValForProd(); + } // 1) 요청 로그 - log.info("Inference request dto={}", dto); - + log.info(""); + log.info("========================================================"); + log.info("[SEND INFERENCE] Inference request dto={}", dto); + log.info("========================================================"); + log.info(""); // 2) local 환경 임시 처리 if ("local".equals(profile)) { if (dto.getPred_requests_areas() == null) { @@ -403,6 +410,7 @@ public class MapSheetInferenceJobService { headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + // TODO 어떤 URL로 어떤파리티러로 요청한 로딩해야지 ExternalCallResult result = externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class); @@ -417,6 +425,7 @@ public class MapSheetInferenceJobService { objectMapper.readValue(result.body(), new TypeReference<>() {}); if (list.isEmpty()) { + // 어떤 URL로 어떤파리티러로 요청한 정보를 봐야 재현을 할듯하지요 throw new IllegalStateException("Inference response is empty"); }