Sfoglia il codice sorgente

update vision gateway for subscribable telemetry

Dr-Swopt 1 settimana fa
parent
commit
7df2916c9d
2 ha cambiato i file con 48 aggiunte e 20 eliminazioni
  1. 12 12
      CLAUDE.md
  2. 36 8
      src/palm-oil/vision.gateway.ts

+ 12 - 12
CLAUDE.md

@@ -68,11 +68,13 @@ Route table (`${serviceId}:${operation}`):
 | `Chat:send` | `{ message: string }` | POST to `N8N_WEBHOOK_URL` server-to-server, unwrap array to first element, emit response |
 | `Chat:clear` | — | Generate new session UUID, emit `{ status: 'success' }` |
 | `PalmHistory:GetImage` | `{ archiveId: string }` | Load image from disk, encode to Base64 data URL, emit response |
+| `Surveillance:SubscribeTelemetry` | — | Join `telemetry-room`; immediately push latest snapshot; reply `{ status: 'subscribed' }` |
+| `Surveillance:UnsubscribeTelemetry` | — | Leave `telemetry-room`; reply `{ status: 'unsubscribed' }` |
 
 Client lifecycle:
-- On connect: push latest surveillance snapshot (if available)
-- Store session UUID in `client.data.sessionId` (used for Chat payloads)
-- Every 500ms tick: broadcasts `response` packet with `SystemMetrics` to all connected clients (via `SurveillanceService` callback registered in `onModuleInit`)
+- On connect: push latest surveillance snapshot (if available); store session UUID in `client.data.sessionId`
+- On disconnect: leave `telemetry-room`
+- Every 500ms tick: broadcasts `response` packet with `SystemMetrics` to `telemetry-room` subscribers only (via `SurveillanceService` callback registered in `onModuleInit`)
 
 **`HistoryEntity`** (`src/palm-oil/entities/history.entity.ts`) — TypeORM entity fields:
 - `id` (PK auto), `archive_id` (unique), `batch_id` (nullable, indexed), `filename`
@@ -163,14 +165,12 @@ Origins hardcoded in `main.ts`:
 
 Update `main.ts` to add new origins (e.g., new Android device IPs).
 
-### Key Differences from `nestjs/` (server-desktop)
+### Key Differences from `server-desktop`
 
-| Concern | `nestjs/` | `server-android/` |
+| Concern | `server-desktop` | `server-android` |
 |---|---|---|
-| ONNX runtime | `onnxruntime-node` (native) | `onnxruntime-web` (WASM, `numThreads=1`) |
-| Image processing | `sharp` | `Jimp` |
-| SQLite driver | `sqlite3` | `sql.js` (TypeORM `sqljs`) |
-| WebSocket namespaces | `/vision` + `/monitor` (separate) | Root namespace (unified) |
-| WebSocket protocol | Flat events (`vision:analyze`) | FIS envelope (`request`/`response`) |
-| Surveillance | Separate `SurveillanceGateway` on `/monitor` | Inline broadcast in `VisionGateway` |
-| Process metrics | `pidusage` | `systeminformation` |
+| ONNX runtime | `onnxruntime-node` native (+ WASM fallback via `INFERENCE_BACKEND`) | `onnxruntime-web` WASM only (`numThreads=1`) |
+| SQLite driver | `sqlite3` (native C++ bindings) | `sql.js` (TypeORM `sqljs`) |
+| Provider selection | Env var `INFERENCE_BACKEND` (default: `onnx-native`) | Hardcoded `OnnxWasmProvider` |
+| `postprocessShared` | Defined in `onnx-native.provider`, imported by WASM provider | Defined inline in `onnx-wasm.provider` |
+| WebSocket surveillance | Separate `SurveillanceGateway` broadcasts `monitor:data` | Inline broadcast in `VisionGateway` to `telemetry-room` |

+ 36 - 8
src/palm-oil/vision.gateway.ts

@@ -114,7 +114,7 @@ export class VisionGateway
         complete: true,
         payload: metrics,
       };
-      this.server?.emit('response', packet);
+      this.server?.to('telemetry-room').emit('response', packet);
     });
   }
 
@@ -142,6 +142,7 @@ export class VisionGateway
   }
 
   handleDisconnect(client: Socket) {
+    client.leave('telemetry-room');
     this.logger.log(`🔌 Vision client disconnected: ${client.id}`);
   }
 
@@ -154,13 +155,15 @@ export class VisionGateway
    * Routing key: `${serviceId}:${operation}`
    *
    * Supported routes:
-   *   PalmVision:analyze   — decode Base64 frame, run ONNX, persist, reply
-   *   History:getAll       — fetch last 50 history records
-   *   History:delete       — delete one record by archiveId
-   *   History:clearAll     — wipe all records and archived images
-   *   Chat:send            — proxy message to n8n webhook
-   *   Chat:clear           — reset session UUID
-   *   PalmHistory:GetImage — stream archived image as Base64 data URL
+   *   PalmVision:analyze                  — decode Base64 frame, run ONNX, persist, reply
+   *   History:getAll                      — fetch last 50 history records
+   *   History:delete                      — delete one record by archiveId
+   *   History:clearAll                    — wipe all records and archived images
+   *   Chat:send                           — proxy message to n8n webhook
+   *   Chat:clear                          — reset session UUID
+   *   PalmHistory:GetImage                — stream archived image as Base64 data URL
+   *   Surveillance:SubscribeTelemetry     — join telemetry-room for metric push
+   *   Surveillance:UnsubscribeTelemetry   — leave telemetry-room
    */
   @SubscribeMessage('request')
   async handleMessage(
@@ -340,6 +343,31 @@ export class VisionGateway
           break;
         }
 
+        // ── Surveillance:SubscribeTelemetry ────────────────────────────────
+        case 'Surveillance:SubscribeTelemetry': {
+          await client.join('telemetry-room');
+          this.logger.log(`📊 ${client.id} joined telemetry-room`);
+          const snapshot = this.surveillanceService.getLatestMetrics();
+          if (snapshot) {
+            const snapId = crypto.randomUUID();
+            client.emit('response', {
+              id: snapId, messageID: snapId,
+              serviceId: 'Surveillance', operation: 'metricsUpdate',
+              complete: true, payload: snapshot,
+            } satisfies FisAppResponse);
+          }
+          reply({ status: 'subscribed' });
+          break;
+        }
+
+        // ── Surveillance:UnsubscribeTelemetry ──────────────────────────────
+        case 'Surveillance:UnsubscribeTelemetry': {
+          await client.leave('telemetry-room');
+          this.logger.log(`📊 ${client.id} left telemetry-room`);
+          reply({ status: 'unsubscribed' });
+          break;
+        }
+
         // ── Unknown route ──────────────────────────────────────────────────
         default: {
           this.logger.warn(`⚠️  Unknown route: ${route}`);