|
@@ -36,7 +36,6 @@ import {
|
|
|
} from '@angular/core';
|
|
} from '@angular/core';
|
|
|
import { CommonModule } from '@angular/common';
|
|
import { CommonModule } from '@angular/common';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
import { FormsModule } from '@angular/forms';
|
|
|
-import { ImageProcessorService } from '../../services/image-processor.service';
|
|
|
|
|
import { LocalHistoryService } from '../../services/local-history.service';
|
|
import { LocalHistoryService } from '../../services/local-history.service';
|
|
|
import { InferenceService, LocalEngine } from '../../core/services/inference.service';
|
|
import { InferenceService, LocalEngine } from '../../core/services/inference.service';
|
|
|
import { VisionSocketService } from '../../services/vision-socket.service';
|
|
import { VisionSocketService } from '../../services/vision-socket.service';
|
|
@@ -87,10 +86,12 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
// ── Batch ingestion state (backend/socket mode only) ───────────────────────
|
|
// ── Batch ingestion state (backend/socket mode only) ───────────────────────
|
|
|
batchQueue = signal<File[]>([]);
|
|
batchQueue = signal<File[]>([]);
|
|
|
currentBatchIndex = signal<number>(0);
|
|
currentBatchIndex = signal<number>(0);
|
|
|
- isBatchActive = computed(() => this.batchQueue().length > 0);
|
|
|
|
|
|
|
+ private _batchRunning = signal<boolean>(false);
|
|
|
|
|
+ isBatchActive = computed(() => this._batchRunning() && this.batchQueue().length > 0);
|
|
|
|
|
|
|
|
sessionManifest: BatchResult[] = [];
|
|
sessionManifest: BatchResult[] = [];
|
|
|
completedReport = signal<FullSessionReport | null>(null);
|
|
completedReport = signal<FullSessionReport | null>(null);
|
|
|
|
|
+ selectedAuditEntry = signal<BatchResult | null>(null);
|
|
|
|
|
|
|
|
/** Wall-clock timestamp of when sendBase64 was called for the current image */
|
|
/** Wall-clock timestamp of when sendBase64 was called for the current image */
|
|
|
private _pingTime = 0;
|
|
private _pingTime = 0;
|
|
@@ -110,7 +111,6 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
private webcamStream: MediaStream | null = null;
|
|
private webcamStream: MediaStream | null = null;
|
|
|
|
|
|
|
|
constructor(
|
|
constructor(
|
|
|
- private imageProcessor: ImageProcessorService,
|
|
|
|
|
public inferenceService: InferenceService,
|
|
public inferenceService: InferenceService,
|
|
|
private localHistory: LocalHistoryService,
|
|
private localHistory: LocalHistoryService,
|
|
|
public visionSocket: VisionSocketService,
|
|
public visionSocket: VisionSocketService,
|
|
@@ -254,7 +254,6 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
this.processSingleFile(files[0]);
|
|
this.processSingleFile(files[0]);
|
|
|
} else {
|
|
} else {
|
|
|
this.batchQueue.set(Array.from(files));
|
|
this.batchQueue.set(Array.from(files));
|
|
|
- this.currentBatchIndex.set(0);
|
|
|
|
|
this.sessionManifest = [];
|
|
this.sessionManifest = [];
|
|
|
this.startBatchProcessing();
|
|
this.startBatchProcessing();
|
|
|
}
|
|
}
|
|
@@ -270,6 +269,7 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
this.sessionManifest = [];
|
|
this.sessionManifest = [];
|
|
|
this.completedReport.set(null);
|
|
this.completedReport.set(null);
|
|
|
this.currentBatchIndex.set(0);
|
|
this.currentBatchIndex.set(0);
|
|
|
|
|
+ this._batchRunning.set(true);
|
|
|
this.processNextInBatch();
|
|
this.processNextInBatch();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -355,11 +355,13 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
this.completedReport.set(report);
|
|
this.completedReport.set(report);
|
|
|
this.batchQueue.set([]);
|
|
this.batchQueue.set([]);
|
|
|
this.currentBatchIndex.set(0);
|
|
this.currentBatchIndex.set(0);
|
|
|
|
|
+ this._batchRunning.set(false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
abortBatch(): void {
|
|
abortBatch(): void {
|
|
|
this.batchQueue.set([]);
|
|
this.batchQueue.set([]);
|
|
|
this.currentBatchIndex.set(0);
|
|
this.currentBatchIndex.set(0);
|
|
|
|
|
+ this._batchRunning.set(false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onDragOver(event: DragEvent): void {
|
|
onDragOver(event: DragEvent): void {
|
|
@@ -512,14 +514,47 @@ export class AnalyzerComponent implements OnInit, OnDestroy {
|
|
|
|
|
|
|
|
onGalleryFileSelected(event: Event): void {
|
|
onGalleryFileSelected(event: Event): void {
|
|
|
const input = event.target as HTMLInputElement;
|
|
const input = event.target as HTMLInputElement;
|
|
|
- const file = input.files?.[0];
|
|
|
|
|
- if (!file) return;
|
|
|
|
|
- this.socketGalleryFile = file;
|
|
|
|
|
|
|
+ const files = input.files;
|
|
|
|
|
+ if (!files || files.length === 0) return;
|
|
|
|
|
+ this.snappedFrame = null;
|
|
|
|
|
+ this.visionSocket.clearResult();
|
|
|
|
|
+ if (files.length > 1) {
|
|
|
|
|
+ this.batchQueue.set(Array.from(files));
|
|
|
|
|
+ this.currentBatchIndex.set(0);
|
|
|
|
|
+ this.sessionManifest = [];
|
|
|
|
|
+ this.socketGalleryFile = null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.socketGalleryFile = files[0];
|
|
|
|
|
+ this.batchQueue.set([]);
|
|
|
|
|
+ }
|
|
|
|
|
+ // Reset input so the same files can be re-selected if needed
|
|
|
|
|
+ input.value = '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onGalleryDrop(event: DragEvent): void {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ event.stopPropagation();
|
|
|
|
|
+ this.isDragging = false;
|
|
|
|
|
+ const files = event.dataTransfer?.files;
|
|
|
|
|
+ if (!files || files.length === 0) return;
|
|
|
this.snappedFrame = null;
|
|
this.snappedFrame = null;
|
|
|
this.visionSocket.clearResult();
|
|
this.visionSocket.clearResult();
|
|
|
|
|
+ if (files.length > 1) {
|
|
|
|
|
+ this.batchQueue.set(Array.from(files).filter(f => f.type.startsWith('image/')));
|
|
|
|
|
+ this.currentBatchIndex.set(0);
|
|
|
|
|
+ this.sessionManifest = [];
|
|
|
|
|
+ this.socketGalleryFile = null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.socketGalleryFile = files[0];
|
|
|
|
|
+ this.batchQueue.set([]);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
analyzeGalleryImage(): void {
|
|
analyzeGalleryImage(): void {
|
|
|
|
|
+ if (this.batchQueue().length > 1) {
|
|
|
|
|
+ this.startBatchProcessing();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
if (!this.socketGalleryFile) return;
|
|
if (!this.socketGalleryFile) return;
|
|
|
const reader = new FileReader();
|
|
const reader = new FileReader();
|
|
|
reader.onload = (e) => {
|
|
reader.onload = (e) => {
|