Sự kiện thời gian thực (SignalR)
Các cập nhật trực tiếp đi qua một hub SignalR tại /hub/realtime. Xác thực dùng
cùng JWT như REST API, truyền dưới dạng ?access_token=<jwt> (hoặc một header
Authorization).
Các nhóm đăng ký
Phần tiêu đề “Các nhóm đăng ký”Các luồng tần số cao là opt-in theo từng nhóm, chỉ tham gia khi một màn hình tiêu thụ được mount và tự động tham gia lại khi kết nối lại:
| Nhóm | Mang | Được tiêu thụ bởi |
|---|---|---|
sensors | Các lô đọc cảm biến trực tiếp | Realtime Monitor |
predictions | Các lô dự đoán mới | Biểu đồ bảng điều khiển |
logs | Toàn bộ luồng log (mọi mức) | Chỉ trang LogViewer |
alerts | Chỉ các mục log Warning/Error | Chuông thông báo, toàn ứng dụng |
Nhóm alerts tồn tại để các cảnh báo nổi lên khắp nơi mà không phải stream toàn bộ
“vòi rồng” logs tới mọi bảng điều khiển.
Sự kiện (server → client)
Phần tiêu đề “Sự kiện (server → client)”Hub phát ra mười hai sự kiện, và bảng dưới đây liệt kê đầy đủ. Tên trường đến ở dạng camelCase: giao thức hub JSON áp dụng chính sách camelCase của ASP.NET Core lên các record payload phía server. Mọi timestamp là Unix epoch mili-giây trừ khi có ghi chú khác.
| Sự kiện | Gửi tới | Payload |
|---|---|---|
Connected | Chỉ client vừa kết nối | connectionId, timestamp |
SensorReadingBatch | Nhóm sensors | ticks — các tick cảm biến được gộp từ lần flush trước (hình dạng bên dưới) |
NewPredictionBatch | Nhóm predictions | predictions — các bản ghi dự đoán được gộp từ lần flush trước (hình dạng bên dưới) |
LogEntry | Nhóm logs | id, timestamp (chuỗi ISO-8601), source, level, message, logger (nullable), correlationId (nullable; cùng một id trên mọi dòng của một tick, từ lần đọc đầu vào đến từng lần ghi đầu ra), inputDatasourceId, inputDatasourceName, outputDatasourceId, outputDatasourceName (đều nullable; có giá trị khi dòng thuộc về một tick suy luận — tên là tên tại thời điểm ghi dòng, id giữ nguyên khi đổi tên) |
AlertLogEntry | Nhóm alerts | Cùng hình dạng với LogEntry, chỉ mang các mục mức Warning và Error |
InferenceStateChanged | Mọi client | state ∈ idle · ready · running, activeInputDatasourceId (nullable), loadedModelId (nullable), loadedModelVersion (nullable), timestamp — phát ra ở mọi chuyển tiếp vòng đời |
InferenceFaulted | Mọi client | reason, datasourceId (nullable), timestamp, severity (mặc định error), code (nullable) — đồng thời hiện banner lỗi chặn |
HealthMetricsUpdate | Mọi client | status, timestamp, gpu (đối tượng nullable), cpu, memory, storage (đối tượng nullable), uptimeSeconds (engine suy luận), backendUptimeSeconds, engineRestartCount, lastEngineRestartAt (nullable) (hình dạng lồng nhau ở bên dưới) |
ModelActivated | Mọi client | modelId, version, inputShape, outputShape, fileSizeMb |
ModelUploadProgress | Mọi client | modelId, version, phase, isError, errorMessage (nullable) |
ModelUploadFailed | Mọi client | modelId, version, reason |
OutputWriteFailed | Mọi client | datasourceId, reason, code (nullable), severity (warning), timestamp, count — gộp theo từng sink: lần thất bại đầu tiên cảnh báo tức thì, sau đó nhiều nhất một sự kiện tiếp theo mỗi cửa sổ 30 giây, với count lần lặp được gộp vào |
Tên sự kiện chính là tên phương thức trên interface hub client phía server, nên các tên ở trên là tên trên dây (wire name) mà client đăng ký nghe.
Hình dạng payload lồng nhau
Phần tiêu đề “Hình dạng payload lồng nhau”Mỗi phần tử của SensorReadingBatch.ticks là một tick — mọi giá trị kênh được đóng
dấu tại một thời điểm sinh dữ liệu duy nhất:
| Trường | Ý nghĩa |
|---|---|
timestamp | Thời điểm sinh dữ liệu cho cả tick |
values | Một giá trị cho mỗi kênh; values[i] là kênh chỉ số i |
HealthMetricsUpdate lồng các đối tượng sau:
| Đối tượng | Trường |
|---|---|
gpu | utilization, memoryUsedMb, memoryTotalMb, memoryUsagePercent, temperatureCelsius — cả đối tượng là null trên host không có GPU, và trước lần poll thành công đầu tiên |
gpu.memoryKind | shared (Jetson: GPU dùng RAM hệ thống, nên các trường bộ nhớ GPU lặp lại số đo của host), dedicated (VRAM riêng), hoặc null khi không rõ |
cpu | usagePercent, temperatureCelsius (null khi host không có cảm biến nhiệt CPU) |
memory | usagePercent, usedMb, totalMb — RAM của host |
memory swap | swapUsedMb, swapTotalMb, swapUsagePercent — null khi host không có swap hoặc không đọc được |
memory.inference | usedMb, limitMb, usagePercent, source (nullable) — bộ nhớ container suy luận so với giới hạn mà tại đó container bị dừng (RAM cộng swap); cả đối tượng là null khi container không có giới hạn hoặc không đọc được |
storage | totalGb, usedGb, freeGb, usagePercent, databaseMb — mỗi trường đều nullable; cả đối tượng là null khi không đọc được ổ đĩa |
Các trường memoryKind, swap và inference là bổ sung: engine suy luận báo cáo
chúng, nên với engine cũ hơn chúng đến dưới dạng null và bảng điều khiển hiển
thị là không được báo cáo. storage do chính backend đo.
Mỗi phần tử của NewPredictionBatch.predictions là một bản ghi dự đoán:
| Trường | Ý nghĩa |
|---|---|
requestId | Định danh của yêu cầu suy luận đã tạo ra bản ghi |
predictions | Đầu ra mô hình, một giá trị cho mỗi kênh đầu ra |
confidenceScores | Một giá trị độ tin cậy cho mỗi kênh đầu ra |
modelId | Mô hình đã tạo ra dự đoán |
inferenceTimeMs | Thời gian thực thi mô hình |
timestamp, windowStartTimestamp, windowEndTimestamp, emittedAt | Xem mục kế tiếp |
Ngữ nghĩa timestamp của dự đoán
Phần tiêu đề “Ngữ nghĩa timestamp của dự đoán”Một dự đoán mô tả một cửa sổ đầu vào trong quá khứ. Mọi timestamp đều là Unix epoch mili-giây từ một đồng hồ — backend đóng dấu mỗi mẫu một lần tại thời điểm đọc, và giá trị đó được truyền qua pipeline, không bao giờ được sinh lại:
| Trường | Ý nghĩa |
|---|---|
windowStartTimestamp | Mẫu đầu vào cũ nhất trong cửa sổ |
windowEndTimestamp | Mẫu đầu vào mới nhất — thời điểm “tính đến” mà dự đoán có hiệu lực |
timestamp | Phản chiếu windowEndTimestamp (tương thích) |
emittedAt | Thời gian đồng hồ backend lúc phát — emittedAt − windowEndTimestamp ≈ độ trễ end-to-end |
Trên bảng điều khiển, vệt dự đoán trễ hơn vệt cảm biến một cách rõ ràng đúng bằng độ trễ pipeline thực. Khoảng cách đó là một tín hiệu vận hành có chủ đích, không phải lỗi render.
Bảo đảm phân phối
Phần tiêu đề “Bảo đảm phân phối”Consumer chậm không bao giờ làm chậm suy luận: các broadcast tới bảng điều khiển là
fire-and-forget, và các sự kiện tới một client không theo kịp sẽ bị bỏ và đếm
(/api/inference/backpressure). Các sự kiện đáng thông báo được khử trùng lặp và
giới hạn tốc độ để chuông vẫn hữu ích — xem Thông báo.