Merge pull request 'feat/infer_dev_260107' (#201) from feat/infer_dev_260107 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/201
This commit is contained in:
2026-01-12 19:30:33 +09:00
10 changed files with 375 additions and 7 deletions

View File

@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.inference;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.inference.service.InferenceResultService;
import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService;
@@ -217,4 +218,23 @@ public class InferenceResultApiController {
// inferenceResultService.getInferenceResultGeomList(id, searchGeoReq);
// return ApiResponseDto.ok(geomList);
// }
@Operation(summary = "추론관리 추론진행 서버 현황", description = "추론관리 추론진행 서버 현황")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Page.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/serverStatus")
public ApiResponseDto<List<InferenceServerStatusDto>> getInferenceServerStatusList() {
return ApiResponseDto.ok(inferenceResultService.getInferenceServerStatusList());
}
}

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.inference.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid;
@@ -196,4 +197,107 @@ public class InferenceResultDto {
private String mapSheetNum;
private String mapSheetName;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class InferenceServerStatusDto {
private String serverName;
@JsonIgnore
private float cpu_user;
@JsonIgnore private float cpu_system;
@JsonIgnore private float memused;
private Long kbmemused;
private float gpuUtil;
//private String cpuStatusName;
//private String memStatusName;
//private String gpuStatusName;
//private float cpu_use_rate;
//private float gpu_use_rate;
//private float mem_use_rate;
public float getCpuUseRate()
{
return this.cpu_user+this.cpu_system;
}
public String getServerStatus()
{
String enumId = "SAFETY";
//if( this.cpu_user+this.cpu_system >= 80 )enumId = "CAUTION";
return enumId;
}
public String getServerStatusName()
{
//String enumId = "SAFETY";
//if( this.cpu_user+this.cpu_system >= 80 )enumId = "CAUTION";
return ServerStatus.SAFETY.getText();
}
public String getCpuStatus()
{
String enumId = "SAFETY";
if( this.cpu_user+this.cpu_system >= 80 )enumId = "CAUTION";
return enumId;
}
public String getGpuStatus()
{
String enumId = "SAFETY";
if( this.gpuUtil >= 80 )enumId = "CAUTION";
return enumId;
}
public String getMemStatus()
{
String enumId = "SAFETY";
if( this.memused >= 80 )enumId = "CAUTION";
return enumId;
}
public String getCpuStatusName()
{
if( this.cpu_user+this.cpu_system >= 80 )return ServerStatus.CAUTION.getText();
return ServerStatus.SAFETY.getText();
}
public String getGpuStatusName()
{
if( this.gpuUtil >= 80 )return ServerStatus.CAUTION.getText();
return ServerStatus.SAFETY.getText();
}
public String getMemStatusName()
{
if( this.memused >= 80 )return ServerStatus.CAUTION.getText();
return ServerStatus.SAFETY.getText();
}
}
@Getter
@AllArgsConstructor
public enum ServerStatus implements EnumType {
SAFETY("원활"),
CAUTION("주의"),
FAILUR("장애"),
;
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
}

View File

@@ -1,6 +1,8 @@
package com.kamco.cd.kamcoback.inference.service;
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.config.resttemplate.ExternalHttpClient;
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
@@ -9,6 +11,7 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Detail;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetNumDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
@@ -23,6 +26,7 @@ import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -33,6 +37,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
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;
import org.springframework.transaction.annotation.Transactional;
@@ -93,7 +98,7 @@ public class InferenceResultService {
// 추론 테이블 저장
UUID uuid = inferenceResultCoreService.saveInferenceInfo(req);
this.startInference(req);
this.startInference(req, uuid);
return uuid;
}
@@ -165,7 +170,7 @@ public class InferenceResultService {
*
* @param req
*/
private void startInference(InferenceResultDto.RegReq req) {
private void startInference(InferenceResultDto.RegReq req, UUID uuid) {
List<MapSheetNumDto> mapSheetNum = req.getMapSheetNum();
List<String> mapSheetNumList = new ArrayList<>();
@@ -193,9 +198,8 @@ public class InferenceResultService {
m2.setPred_requests_areas(predRequestsAreas);
m3.setPred_requests_areas(predRequestsAreas);
ensureAccepted(m1);
// ensureAccepted(m2);
// ensureAccepted(m3);
Long batchId = this.ensureAccepted(m1);
inferenceResultCoreService.update(uuid, batchId, "IN_PROGRESS");
}
/**
@@ -203,7 +207,7 @@ public class InferenceResultService {
*
* @param dto
*/
private void ensureAccepted(InferenceSendDto dto) {
private Long ensureAccepted(InferenceSendDto dto) {
log.info("dto null? {}", dto == null);
ObjectMapper om = new ObjectMapper();
try {
@@ -227,6 +231,28 @@ public class InferenceResultService {
int status = result.statusCode();
String body = result.body();
if (status < 200 || status >= 300) {
throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY);
}
Long batchId = 0L;
try {
List<Map<String, Object>> list =
om.readValue(body, new TypeReference<List<Map<String, Object>>>() {});
Integer batchIdInt = (Integer) list.get(0).get("batch_id");
batchId = batchIdInt.longValue();
if (list.isEmpty()) {
throw new IllegalStateException("Inference response is empty");
}
} catch (Exception e) {
log.error(e.getMessage());
}
return batchId;
}
/**
@@ -361,4 +387,8 @@ public class InferenceResultService {
public List<MapSheet> listGetScenes5k(Long id) {
return inferenceResultCoreService.listGetScenes5k(id);
}
public List<InferenceServerStatusDto> getInferenceServerStatusList() {
return inferenceResultCoreService.getInferenceServerStatusList();
}
}

View File

@@ -6,6 +6,7 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetNumDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.postgres.entity.MapInkx5kEntity;
@@ -216,4 +217,17 @@ public class InferenceResultCoreService {
.map(MapInkx5kEntity::toEntity)
.toList();
}
public void update(UUID uuid, Long batchId, String status) {
MapSheetLearnEntity entity =
mapSheetLearnRepository
.getInferenceResultByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException(uuid.toString()));
entity.setBatchId(batchId);
entity.setStatus(status);
}
public List<InferenceServerStatusDto> getInferenceServerStatusList(){return mapSheetLearnRepository.getInferenceServerStatusList();
}
}

View File

@@ -0,0 +1,48 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.time.OffsetDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "gpu_metrics")
public class GpuMetricEntity {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid", nullable = false)
private UUID id;
@NotNull
@ColumnDefault("nextval('gpu_metrics_id_seq')")
@Column(name = "id", nullable = false)
private Integer id1;
@NotNull
@ColumnDefault("now()")
@Column(name = "\"timestamp\"", nullable = false)
private OffsetDateTime timestamp;
@NotNull
@Column(name = "server_name", nullable = false, length = Integer.MAX_VALUE)
private String serverName;
@Column(name = "gpu_util")
private Float gpuUtil;
@Column(name = "gpu_mem_used")
private Float gpuMemUsed;
@Column(name = "gpu_mem_total")
private Float gpuMemTotal;
}

View File

@@ -40,7 +40,7 @@ public class MapSheetLearnEntity {
@Column(name = "title", nullable = false, length = 200)
private String title;
@Size(max = 10)
@Size(max = 20)
@Column(name = "status", length = 10)
private String status;
@@ -103,6 +103,9 @@ public class MapSheetLearnEntity {
@Column(name = "updated_uid")
private Long updatedUid;
@Column(name = "batch_id")
private Long batchId;
public InferenceResultDto.ResultList toDto() {
return new InferenceResultDto.ResultList(
this.uuid,

View File

@@ -0,0 +1,59 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.time.OffsetDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "system_metrics")
public class SystemMetricEntity {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid", nullable = false)
private UUID id;
@NotNull
@ColumnDefault("nextval('system_metrics_id_seq')")
@Column(name = "id", nullable = false)
private Integer id1;
@NotNull
@Column(name = "\"timestamp\"", nullable = false)
private OffsetDateTime timestamp;
@NotNull
@Column(name = "server_name", nullable = false, length = Integer.MAX_VALUE)
private String serverName;
@Column(name = "cpu_user")
private Float cpuUser;
@Column(name = "cpu_system")
private Float cpuSystem;
@Column(name = "cpu_iowait")
private Float cpuIowait;
@Column(name = "cpu_idle")
private Float cpuIdle;
@Column(name = "kbmemfree")
private Long kbmemfree;
@Column(name = "kbmemused")
private Long kbmemused;
@Column(name = "memused")
private Float memused;
}

View File

@@ -1,10 +1,18 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import java.util.Optional;
import java.util.UUID;
import java.util.List;
import org.springframework.data.domain.Page;
public interface MapSheetLearnRepositoryCustom {
Page<MapSheetLearnEntity> getInferenceMgnResultList(InferenceResultDto.SearchListReq req);
Optional<MapSheetLearnEntity> getInferenceResultByUuid(UUID uuid);
List<InferenceServerStatusDto> getInferenceServerStatusList();
}

View File

@@ -1,15 +1,21 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import static com.kamco.cd.kamcoback.postgres.entity.QGpuMetricEntity.gpuMetricEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QSystemMetricEntity.systemMetricEntity;
import com.kamco.cd.kamcoback.common.utils.DateRange;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
@@ -73,4 +79,50 @@ public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCusto
return new PageImpl<>(content, pageable, total == null ? 0L : total);
}
@Override
public Optional<MapSheetLearnEntity> getInferenceResultByUuid(UUID uuid) {
return Optional.ofNullable(
queryFactory
.selectFrom(mapSheetLearnEntity)
.where(mapSheetLearnEntity.uuid.eq(uuid))
.fetchOne());
}
@Override
public List<InferenceServerStatusDto> getInferenceServerStatusList() {
BooleanBuilder builder = new BooleanBuilder();
List<Integer> latestIds = queryFactory
.select(systemMetricEntity.id1.max())
.from(systemMetricEntity)
.groupBy(systemMetricEntity.serverName)
.fetch();
List<Integer> latestGpuIds = queryFactory
.select(gpuMetricEntity.id1.max())
.from(gpuMetricEntity)
.groupBy(gpuMetricEntity.serverName)
.fetch();
List<InferenceServerStatusDto> foundContent = queryFactory
.select(Projections.constructor(
InferenceServerStatusDto.class,
systemMetricEntity.serverName,
systemMetricEntity.cpuUser,
systemMetricEntity.cpuSystem,
systemMetricEntity.memused,
systemMetricEntity.kbmemused,
gpuMetricEntity.gpuUtil
))
.from(systemMetricEntity)
.leftJoin(gpuMetricEntity).on(gpuMetricEntity.serverName.eq(systemMetricEntity.serverName))
.where(systemMetricEntity.id1.in(latestIds)) // In 절 사용
.orderBy(systemMetricEntity.serverName.asc())
.limit(4)
.fetch();
return foundContent;
}
}

View File

@@ -0,0 +1,30 @@
package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Log4j2
@Service
@RequiredArgsConstructor
public class MapSheetInferenceJobService {
private final InferenceResultCoreService inferenceResultCoreService;
@Scheduled(fixedDelay = 60_000)
public void runBatch() {
log.info("1분 배치 시작");
try {
// TODO: 배치 로직 작성
Thread.sleep(3000); // 예시: 처리 시간 3초
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("배치 중 인터럽트 발생", e);
}
log.info("1분 배치 종료");
}
}