|
|
@@ -3,7 +3,13 @@ import { Injectable, OnDestroy } from '@angular/core';
|
|
|
import { map, Observable, Subject, take } from 'rxjs';
|
|
|
import { DpService } from 'dp-ui/dp.service';
|
|
|
import { FisAppMessage, MessageHeader, AppMessageType } from 'dp-ui/fisappmessage/apprequestmessagetype';
|
|
|
-import { InferenceFrame, DetectionResult, MPOB_CLASSES, HEALTH_ALERT_CLASSES } from './inference.service';
|
|
|
+import {
|
|
|
+ DetectionResult,
|
|
|
+ HistoryRecord,
|
|
|
+ InferenceFrame,
|
|
|
+ HEALTH_ALERT_CLASSES,
|
|
|
+ MPOB_CLASSES,
|
|
|
+} from './inference.service';
|
|
|
|
|
|
interface PalmVisionConfig {
|
|
|
connection: {
|
|
|
@@ -17,12 +23,22 @@ export interface EdgeResultPayload {
|
|
|
frame: string;
|
|
|
filename?: string;
|
|
|
batchId?: string;
|
|
|
+ mode: string;
|
|
|
detections: DetectionResult[];
|
|
|
industrial_summary: Record<string, number>;
|
|
|
inference_ms: number;
|
|
|
processing_ms?: number;
|
|
|
}
|
|
|
|
|
|
+export interface SaveExternalResultResponse {
|
|
|
+ archive_id: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface ImageRecord {
|
|
|
+ archive_id: string;
|
|
|
+ image_data: string;
|
|
|
+}
|
|
|
+
|
|
|
@Injectable({ providedIn: 'root' })
|
|
|
export class RemoteInferenceService implements OnDestroy {
|
|
|
private config: PalmVisionConfig | null = null;
|
|
|
@@ -42,7 +58,7 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = () => {
|
|
|
const frame = reader.result as string;
|
|
|
- this.send<any>('PalmVision', 'analyze', { frame, sourceLabel, batchId })
|
|
|
+ this.send<unknown>('PalmVision', 'analyze', { frame, sourceLabel, batchId })
|
|
|
.subscribe({
|
|
|
next: raw => observer.next(this.mapAnalysisResponse(raw, batchId)),
|
|
|
error: err => observer.error(err),
|
|
|
@@ -54,29 +70,9 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- getHistory(): Observable<any[]> {
|
|
|
- return this.send<any>('History', 'getAll', undefined).pipe(
|
|
|
- map(body => {
|
|
|
- console.log('[Vault Transport Debug] Raw History Network Payload:', body);
|
|
|
-
|
|
|
- if (Array.isArray(body)) {
|
|
|
- return body;
|
|
|
- }
|
|
|
-
|
|
|
- if (body && typeof body === 'object') {
|
|
|
- const extractedRecords = body.records || body.data || body.items || body.history;
|
|
|
- if (Array.isArray(extractedRecords)) {
|
|
|
- return extractedRecords;
|
|
|
- }
|
|
|
-
|
|
|
- const values = Object.values(body);
|
|
|
- if (values.length > 0 && values.every(v => v !== null && typeof v === 'object')) {
|
|
|
- return values as any[];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return [];
|
|
|
- })
|
|
|
+ getHistory(): Observable<HistoryRecord[]> {
|
|
|
+ return this.send<unknown>('History', 'getAll', undefined).pipe(
|
|
|
+ map(body => this.normalizeHistoryResponse(body)),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
@@ -88,25 +84,16 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
return this.send('History', 'clearAll', undefined);
|
|
|
}
|
|
|
|
|
|
- getImage(archiveId: string): Observable<{ archiveId: string; image_data: string }> {
|
|
|
- return this.send('PalmHistory', 'GetImage', { archiveId });
|
|
|
+ getImage(archiveId: string): Observable<ImageRecord> {
|
|
|
+ return this.send<ImageRecord>('PalmHistory', 'GetImage', { archiveId });
|
|
|
}
|
|
|
|
|
|
- getBatchDetails(batchId: string): Observable<any> {
|
|
|
- return this.send<any>('History', 'getBatchDetails', { batchId });
|
|
|
+ getBatchDetails(batchId: string): Observable<unknown> {
|
|
|
+ return this.send<unknown>('History', 'getBatchDetails', { batchId });
|
|
|
}
|
|
|
|
|
|
- saveExternalResult(payload: EdgeResultPayload): Observable<any> {
|
|
|
- const adjustedPayload: any = {
|
|
|
- ...payload,
|
|
|
- frame: (payload as any).frame || (payload as any).imageDataUrl || '',
|
|
|
- };
|
|
|
-
|
|
|
- if (adjustedPayload.imageDataUrl) {
|
|
|
- delete adjustedPayload.imageDataUrl;
|
|
|
- }
|
|
|
-
|
|
|
- return this.send('PalmHistory', 'SaveExternalResult', adjustedPayload);
|
|
|
+ saveExternalResult(payload: EdgeResultPayload): Observable<SaveExternalResultResponse> {
|
|
|
+ return this.send<SaveExternalResultResponse>('PalmHistory', 'SaveExternalResult', payload);
|
|
|
}
|
|
|
|
|
|
ngOnDestroy(): void {
|
|
|
@@ -121,7 +108,6 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
private send<T>(serviceId: string, operation: string, payload: unknown): Observable<T> {
|
|
|
const messageID = crypto.randomUUID();
|
|
|
|
|
|
- // Package parameters inside a fully compliant enterprise envelope structure
|
|
|
const message: FisAppMessage = {
|
|
|
header: {
|
|
|
messageID,
|
|
|
@@ -133,17 +119,9 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
};
|
|
|
|
|
|
return new Observable<T>(observer => {
|
|
|
- // Direct call routing through the shared enterprise stream engine
|
|
|
this.dpService.stream(message).subscribe({
|
|
|
next: (res: any) => {
|
|
|
- // Gracefully intercept and isolate system finalization frames before parsing
|
|
|
- if (res && res.complete === true) {
|
|
|
- observer.complete();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Extract body mapping parameters directly from enterprise results packets
|
|
|
- const body = typeof res === 'string' ? JSON.parse(res) : (res?.message ? JSON.parse(res.message) : res);
|
|
|
+ const body = typeof res === 'string' ? JSON.parse(res) : (res?.message ? (typeof res.message === 'string' ? JSON.parse(res.message) : res.message) : res);
|
|
|
if (body?.error) {
|
|
|
observer.error(new Error(body.error));
|
|
|
} else {
|
|
|
@@ -156,8 +134,34 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- private mapAnalysisResponse(raw: any, batchId?: string): InferenceFrame {
|
|
|
- const detections: DetectionResult[] = (raw?.detections ?? []).map((d: any) => ({
|
|
|
+ /**
|
|
|
+ * Resolves heterogeneous backend envelope shapes into a flat HistoryRecord array.
|
|
|
+ * Candidate keys are tried in priority order; the Object.values() heuristic is intentionally
|
|
|
+ * excluded because it produces non-deterministic results when the envelope has mixed keys.
|
|
|
+ */
|
|
|
+ private normalizeHistoryResponse(body: unknown): HistoryRecord[] {
|
|
|
+ if (Array.isArray(body)) return body as HistoryRecord[];
|
|
|
+
|
|
|
+ if (body && typeof body === 'object') {
|
|
|
+ const envelope = body as Record<string, unknown>;
|
|
|
+ for (const key of ['records', 'data', 'items', 'history', 'response'] as const) {
|
|
|
+ if (Array.isArray(envelope[key])) return envelope[key] as HistoryRecord[];
|
|
|
+ }
|
|
|
+
|
|
|
+ // Protobuf-spread shape: { 0: r, 1: r, ..., request: protoReq } — numeric keys only,
|
|
|
+ // discarding any non-numeric metadata keys injected by the transport layer.
|
|
|
+ const numericKeys = Object.keys(envelope).filter(k => /^\d+$/.test(k));
|
|
|
+ if (numericKeys.length > 0) {
|
|
|
+ return numericKeys.map(k => envelope[k]) as HistoryRecord[];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ private mapAnalysisResponse(raw: unknown, batchId?: string): InferenceFrame {
|
|
|
+ const r = raw as Record<string, any>;
|
|
|
+ const detections: DetectionResult[] = (r?.detections ?? []).map((d: any) => ({
|
|
|
bunch_id: d.bunch_id,
|
|
|
class: d.class,
|
|
|
confidence: d.confidence,
|
|
|
@@ -166,17 +170,16 @@ export class RemoteInferenceService implements OnDestroy {
|
|
|
norm_box: d.norm_box,
|
|
|
}));
|
|
|
|
|
|
- const industrial_summary: Record<string, number> = raw?.industrial_summary
|
|
|
- ?? raw?.technical_evidence?.industrial_summary
|
|
|
- ?? {};
|
|
|
+ const industrial_summary: Record<string, number> =
|
|
|
+ r?.industrial_summary ?? r?.technical_evidence?.industrial_summary ?? {};
|
|
|
|
|
|
return {
|
|
|
- frameId: raw?.archive_id ?? crypto.randomUUID(),
|
|
|
+ frameId: r?.archive_id ?? crypto.randomUUID(),
|
|
|
batchId,
|
|
|
- imageDataUrl: raw?.image_data ?? raw?.imageDataUrl ?? '',
|
|
|
+ imageDataUrl: r?.image_data ?? r?.imageDataUrl ?? '',
|
|
|
detections,
|
|
|
- inference_ms: raw?.inference_ms ?? 0,
|
|
|
- processing_ms: raw?.processing_ms ?? 0,
|
|
|
+ inference_ms: r?.inference_ms ?? 0,
|
|
|
+ processing_ms: r?.processing_ms ?? 0,
|
|
|
total_count: detections.length,
|
|
|
industrial_summary,
|
|
|
source: 'remote',
|