실시간 이벤트 (SignalR)
라이브 업데이트는 **/hub/realtime**의 단일 SignalR 허브를 통해 전달됩니다. 인증은 REST API와
동일한 JWT를 사용하며, ?access_token=<jwt>(또는 Authorization 헤더)로 전달합니다.
구독 그룹
섹션 제목: “구독 그룹”고빈도 스트림은 그룹별로 선택 가입(opt-in)하며, 이를 소비하는 화면이 마운트된 동안에만 가입되고 재연결 시 자동으로 재가입됩니다:
| 그룹 | 전달 내용 | 소비 주체 |
|---|---|---|
sensors | 라이브 센서 판독 배치 | Realtime Monitor |
predictions | 새 예측 배치 | 대시보드 차트 |
logs | 전체 로그 스트림(모든 레벨) | LogViewer 페이지 전용 |
alerts | Warning/Error 로그 항목만 | 알림 벨, 앱 전역 |
alerts 그룹은 전체 logs 소방호스(firehose)를 모든 대시보드로 스트리밍하지 않고도 경고가
어디서나 표면화되도록 하기 위해 존재합니다.
이벤트 (서버 → 클라이언트)
섹션 제목: “이벤트 (서버 → 클라이언트)”허브는 12개의 이벤트를 발행하며, 아래 표가 그 전체 목록입니다. 필드 이름은 camelCase로 전달됩니다 — JSON 허브 프로토콜이 서버 측 페이로드 레코드에 ASP.NET Core의 camelCase 정책을 적용하기 때문입니다. 별도 표기가 없는 한 타임스탬프는 Unix epoch 밀리초입니다.
| 이벤트 | 전달 대상 | 페이로드 |
|---|---|---|
Connected | 접속한 해당 클라이언트에게만 | connectionId, timestamp |
SensorReadingBatch | sensors 그룹 | ticks — 마지막 플러시 이후 합쳐진 센서 틱(형상은 아래 참조) |
NewPredictionBatch | predictions 그룹 | predictions — 마지막 플러시 이후 합쳐진 예측 레코드(형상은 아래 참조) |
LogEntry | logs 그룹 | id, timestamp(ISO-8601 문자열), source, level, message, logger(nullable), correlationId(nullable. 한 틱의 모든 줄에 같은 id가 붙으며, 입력 읽기부터 각 출력 쓰기까지 이어짐), inputDatasourceId, inputDatasourceName, outputDatasourceId, outputDatasourceName(모두 nullable. 해당 줄이 추론 틱에 속할 때 설정됨 — 이름은 줄이 기록된 시점의 이름이며, id는 이름을 바꿔도 유지됨) |
AlertLogEntry | alerts 그룹 | LogEntry와 동일한 형상이며, Warning 및 Error 항목만 전달 |
InferenceStateChanged | 모든 클라이언트 | state ∈ idle · ready · running, activeInputDatasourceId(nullable), loadedModelId(nullable), loadedModelVersion(nullable), timestamp — 모든 수명 주기 전환마다 방출 |
InferenceFaulted | 모든 클라이언트 | reason, datasourceId(nullable), timestamp, severity(기본값 error), code(nullable) — 차단성 장애 배너도 함께 표시 |
HealthMetricsUpdate | 모든 클라이언트 | status, timestamp, gpu(nullable 객체), cpu, memory, storage(nullable 객체), uptimeSeconds(추론 엔진), backendUptimeSeconds, engineRestartCount, lastEngineRestartAt(nullable)(중첩 형상은 아래 참조) |
ModelActivated | 모든 클라이언트 | modelId, version, inputShape, outputShape, fileSizeMb |
ModelUploadProgress | 모든 클라이언트 | modelId, version, phase, isError, errorMessage(nullable) |
ModelUploadFailed | 모든 클라이언트 | modelId, version, reason |
OutputWriteFailed | 모든 클라이언트 | datasourceId, reason, code(nullable), severity(warning), timestamp, count — 싱크별로 합쳐짐: 첫 실패는 즉시 경고하고, 이후에는 30초 윈도우당 최대 한 번의 후속 이벤트에 count만큼의 반복이 접혀 들어감 |
이벤트 이름은 서버 허브 클라이언트 인터페이스의 메서드 이름이므로, 위 이름들이 클라이언트가 구독하는 실제 와이어 이름입니다.
중첩 페이로드 형상
섹션 제목: “중첩 페이로드 형상”SensorReadingBatch.ticks의 각 항목은 하나의 틱 — 단일 생산 시점에 찍힌 모든 채널
값입니다:
| 필드 | 의미 |
|---|---|
timestamp | 해당 틱 전체의 생산 시점 |
values | 채널당 값 하나; values[i]는 채널 인덱스 i |
HealthMetricsUpdate는 다음 객체를 중첩합니다:
| 객체 | 필드 |
|---|---|
gpu | utilization, memoryUsedMb, memoryTotalMb, memoryUsagePercent, temperatureCelsius — GPU가 없는 호스트에서, 그리고 첫 폴링이 성공하기 전에는 객체 전체가 null |
gpu.memoryKind | shared(Jetson: GPU가 시스템 RAM을 사용하므로 GPU 메모리 필드는 호스트 값과 같음), dedicated(독립 VRAM), 알 수 없으면 null |
cpu | usagePercent, temperatureCelsius(호스트에 CPU 온도 센서가 없으면 null) |
memory | usagePercent, usedMb, totalMb — 호스트의 RAM |
memory 스왑 | swapUsedMb, swapTotalMb, swapUsagePercent — 호스트에 스왑이 없거나 읽을 수 없으면 null |
memory.inference | usedMb, limitMb, usagePercent, source(nullable) — 추론 컨테이너의 메모리 사용량과, 컨테이너가 중지되는 한도(RAM + 스왑). 한도가 없거나 읽을 수 없으면 객체 전체가 null |
storage | totalGb, usedGb, freeGb, usagePercent, databaseMb — 각각 nullable. 디스크를 읽을 수 없으면 객체 전체가 null |
memoryKind, 스왑, inference 필드는 추가된 필드입니다. 추론 엔진이 보고하는 값이므로,
이전 버전의 엔진에서는 null로 도착하며 대시보드는 보고되지 않음으로 표시합니다.
storage는 백엔드가 직접 측정합니다.
NewPredictionBatch.predictions의 각 항목은 하나의 예측 레코드입니다:
| 필드 | 의미 |
|---|---|
requestId | 해당 레코드를 만든 추론 요청의 식별자 |
predictions | 모델 출력, 출력 채널당 값 하나 |
confidenceScores | 출력 채널당 신뢰도 값 하나 |
modelId | 예측을 생성한 모델 |
inferenceTimeMs | 모델 실행 시간 |
timestamp, windowStartTimestamp, windowEndTimestamp, emittedAt | 다음 절 참조 |
예측 타임스탬프 의미
섹션 제목: “예측 타임스탬프 의미”예측은 입력의 과거 윈도우를 서술합니다. 모든 타임스탬프는 하나의 시계에서 나온 Unix epoch 밀리초입니다 — 백엔드가 판독 시점에 각 샘플을 한 번만 찍고, 그 값이 파이프라인 전체로 반향되며 결코 재생성되지 않습니다:
| 필드 | 의미 |
|---|---|
windowStartTimestamp | 윈도우에서 가장 오래된 입력 샘플 |
windowEndTimestamp | 가장 최신 입력 샘플 — 예측이 유효한 “기준 시점” |
timestamp | windowEndTimestamp를 미러링(호환성) |
emittedAt | 방출 시점의 백엔드 벽시계 — emittedAt − windowEndTimestamp ≈ 엔드투엔드 지연 시간 |
대시보드에서 예측 트레이스는 실제 파이프라인 지연 시간만큼 센서 트레이스보다 눈에 띄게 뒤따릅니다. 그 간격은 렌더링 결함이 아니라 의도된 운영 신호입니다.
전달 보장
섹션 제목: “전달 보장”느린 소비자가 추론을 결코 스로틀하지 않습니다: 대시보드 브로드캐스트는 발사 후 망각 방식이며,
따라잡지 못하는 클라이언트로 향하는 이벤트는 드롭되고 카운트됩니다(/api/inference/backpressure).
알림 대상 이벤트는 중복 제거되고 속도 제한되어 벨을 유용하게 유지합니다 —
알림을 참조하세요.