improve tracking logic

master
MrPlatnum 2025-09-14 15:01:58 +02:00
parent 9446e3b2de
commit 95d92cc4d9
5 changed files with 138 additions and 153 deletions

View File

@ -59,8 +59,6 @@ export class DemographicsFeedbackComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.createForm(); this.createForm();
// Start device orientation tracking for this component
this.metricsService.startDeviceOrientationTracking();
} }
logInteraction(event: Event) { logInteraction(event: Event) {

View File

@ -41,8 +41,7 @@ export class SpatialPositionAssessmentComponent implements AfterViewInit, OnDest
ngAfterViewInit() { ngAfterViewInit() {
const mv = this.modelViewerRef.nativeElement; const mv = this.modelViewerRef.nativeElement;
this.metricsService.startDeviceOrientationTracking(); this.metricsService.startTracking(mv);
this.metricsService.startArTracking(mv);
mv.addEventListener('ar-status', (e: any) => { mv.addEventListener('ar-status', (e: any) => {
if (e.detail.status === 'session-started') { if (e.detail.status === 'session-started') {
setTimeout(() => this.captureAnchor(), 500); setTimeout(() => this.captureAnchor(), 500);

View File

@ -48,8 +48,7 @@ export class SpatialStabilityAssessmentComponent implements AfterViewInit, OnDes
ngAfterViewInit() { ngAfterViewInit() {
const modelViewer = this.modelViewerRef.nativeElement; const modelViewer = this.modelViewerRef.nativeElement;
this.metricsService.startDeviceOrientationTracking(); this.metricsService.startTracking(modelViewer);
this.metricsService.startArTracking(modelViewer);
modelViewer.addEventListener('ar-status', (event: any) => { modelViewer.addEventListener('ar-status', (event: any) => {
if (event.detail.status === 'session-started' && !this.isModelPlaced) { if (event.detail.status === 'session-started' && !this.isModelPlaced) {
@ -143,7 +142,7 @@ export class SpatialStabilityAssessmentComponent implements AfterViewInit, OnDes
if (wasCompleted) { if (wasCompleted) {
this.currentPhase = 4; this.currentPhase = 4;
} }
if(wasCompleted) { if(wasCompleted) {
this.testComplete.emit(); this.testComplete.emit();
} }

View File

@ -48,8 +48,8 @@ export class TextLegibilityAssessmentComponent implements AfterViewInit, OnDestr
} }
ngAfterViewInit() { ngAfterViewInit() {
this.metricsService.startDeviceOrientationTracking(); const mv = this.modelViewerRef.nativeElement;
this.metricsService.startArTracking(this.modelViewerRef.nativeElement); this.metricsService.startTracking(mv);
} }
public logInteraction(event: Event) { public logInteraction(event: Event) {

View File

@ -3,166 +3,155 @@ import { inject, Injectable } from '@angular/core';
import { interval, Subscription, tap } from 'rxjs'; import { interval, Subscription, tap } from 'rxjs';
export interface InteractionEvent { export interface InteractionEvent {
timestamp: number; timestamp: number;
type: string; type: string;
elementId?: string; elementId?: string;
elementTag?: string; elementTag?: string;
elementClasses?: string; elementClasses?: string;
value?: any; value?: any;
} }
export interface DeviceOrientation { export interface DeviceOrientation {
timestamp: number; timestamp: number;
alpha: number | null; alpha: number | null;
beta: number | null; beta: number | null;
gamma: number | null; gamma: number | null;
} }
export interface ArTrackingData { export interface ArTrackingData {
timestamp: number; timestamp: number;
anchor: string | null; anchor: string | null;
cameraOrbit: { cameraOrbit: {
theta: number; theta: number;
phi: number; phi: number;
radius: number; radius: number;
} | null; } | null;
cameraTarget: { cameraTarget: {
x: number; x: number;
y: number; y: number;
z: number; z: number;
} | null; } | null;
} }
export interface MetricsLog { export interface MetricsLog {
interactions: InteractionEvent[]; interactions: InteractionEvent[];
deviceOrientations: DeviceOrientation[]; deviceOrientations: DeviceOrientation[];
arData: ArTrackingData[]; arData: ArTrackingData[];
} }
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class MetricsTrackerService { export class MetricsTrackerService {
private http = inject(HttpClient); private http = inject(HttpClient);
private serverUrl = '/api/log'; private serverUrl = '/api/log';
private metricsLog: MetricsLog = {
interactions: [],
deviceOrientations: [],
arData: []
};
private deviceOrientationSubscription: Subscription | null = null; private metricsLog: MetricsLog = {
private arTrackingSubscription: Subscription | null = null; interactions: [],
private lastDeviceOrientation: DeviceOrientationEvent | null = null; deviceOrientations: [],
arData: []
constructor() {
this.handleDeviceOrientation = this.handleDeviceOrientation.bind(this);
}
private handleDeviceOrientation(event: DeviceOrientationEvent): void {
this.lastDeviceOrientation = event;
}
// --- Public API ---
public logInteraction(event: Event): void {
if (event.target) {
const target = event.target as HTMLElement;
const interaction: InteractionEvent = {
timestamp: Date.now(),
type: event.type,
elementId: target.id || undefined,
elementTag: target.tagName,
elementClasses: target.className,
value: (target as any).value ?? undefined
};
this.metricsLog.interactions.push(interaction);
}
else if (event instanceof CustomEvent && event.detail) {
const interaction: InteractionEvent = {
timestamp: Date.now(),
type: event.type,
value: event.detail
};
this.metricsLog.interactions.push(interaction);
}
else {
console.warn("logInteraction called with an unknown event type:", event);
}
}
public startDeviceOrientationTracking(): void {
if (this.deviceOrientationSubscription || typeof window === 'undefined') return;
window.addEventListener('deviceorientation', this.handleDeviceOrientation);
this.deviceOrientationSubscription = interval(500).subscribe(() => {
if (this.lastDeviceOrientation) {
const orientation: DeviceOrientation = {
timestamp: this.lastDeviceOrientation.timeStamp,
alpha: this.lastDeviceOrientation.alpha,
beta: this.lastDeviceOrientation.beta,
gamma: this.lastDeviceOrientation.gamma
};
this.metricsLog.deviceOrientations.push(orientation);
}
});
}
public startArTracking(modelViewerElement: any): void {
if (this.arTrackingSubscription || !modelViewerElement) return;
this.arTrackingSubscription = interval(500).subscribe(() => {
if (!modelViewerElement.arActive) {
return;
}
const anchor = modelViewerElement.getAnchor ? modelViewerElement.getAnchor() : 'getAnchor not available';
const orbit = modelViewerElement.getCameraOrbit();
const target = modelViewerElement.getCameraTarget();
const arData: ArTrackingData = {
timestamp: Date.now(),
anchor: anchor.includes('not placed') ? null : anchor,
cameraOrbit: orbit ? { theta: orbit.theta, phi: orbit.phi, radius: orbit.radius } : null,
cameraTarget: target ? { x: target.x, y: target.y, z: target.z } : null,
};
this.metricsLog.arData.push(arData);
});
}
public sendMetricsToServer(testName: string, formData?: any) {
const payload = {
testName,
metricsLog: this.metricsLog,
...(formData && { formData })
}; };
console.log(payload)
return this.http.post(this.serverUrl, payload).pipe(
tap({
next: (response) => {
console.log(`Metrics for '${testName}' sent successfully:`, response);
this.resetMetrics();
},
error: (err) => console.error(`Failed to send metrics for '${testName}':`, err)
})
);
}
public cleanup(): void { private trackingSubscription: Subscription | null = null;
if (typeof window !== 'undefined') { private lastDeviceOrientation: DeviceOrientationEvent | null = null;
window.removeEventListener('deviceorientation', this.handleDeviceOrientation);
private handleDeviceOrientation(event: DeviceOrientationEvent): void {
this.lastDeviceOrientation = event;
}
// --- Public API ---
public logInteraction(event: Event): void {
if (event.target) {
const target = event.target as HTMLElement;
const interaction: InteractionEvent = {
timestamp: Date.now(),
type: event.type,
elementId: target.id || undefined,
elementTag: target.tagName,
elementClasses: target.className,
value: (target as any).value ?? undefined
};
this.metricsLog.interactions.push(interaction);
}
else if (event instanceof CustomEvent && event.detail) {
const interaction: InteractionEvent = {
timestamp: Date.now(),
type: event.type,
value: event.detail
};
this.metricsLog.interactions.push(interaction);
}
else {
console.warn("logInteraction called with an unknown event type:", event);
}
} }
this.deviceOrientationSubscription?.unsubscribe();
this.arTrackingSubscription?.unsubscribe();
this.deviceOrientationSubscription = null;
this.arTrackingSubscription = null;
}
public resetMetrics(): void {
this.metricsLog = { interactions: [], deviceOrientations: [], arData: [] }; public startTracking(modelViewerElement: any): void {
} if (this.trackingSubscription || !modelViewerElement) return;
}
if (typeof window !== 'undefined') {
window.addEventListener('deviceorientation', this.handleDeviceOrientation);
}
this.trackingSubscription = interval(500).subscribe(() => {
if (!modelViewerElement.arActive) {
return;
}
const timestamp = Date.now();
const anchor = modelViewerElement.getAnchor ? modelViewerElement.getAnchor() : 'getAnchor not available';
const orbit = modelViewerElement.getCameraOrbit ? modelViewerElement.getCameraOrbit() : null;
const target = modelViewerElement.getCameraTarget ? modelViewerElement.getCameraTarget() : null;
const arData: ArTrackingData = {
timestamp: timestamp,
anchor: anchor.includes('not placed') ? null : anchor,
cameraOrbit: orbit ? { theta: orbit.theta, phi: orbit.phi, radius: orbit.radius } : null,
cameraTarget: target ? { x: target.x, y: target.y, z: target.z } : null,
};
this.metricsLog.arData.push(arData);
let orientation: DeviceOrientation | null = null;
if (this.lastDeviceOrientation) {
orientation = {
timestamp: timestamp,
alpha: this.lastDeviceOrientation.alpha,
beta: this.lastDeviceOrientation.beta,
gamma: this.lastDeviceOrientation.gamma
};
this.metricsLog.deviceOrientations.push(orientation);
}
});
}
public sendMetricsToServer(testName: string, formData?: any) {
const payload = {
testName,
metricsLog: this.metricsLog,
...(formData && { formData })
};
console.log(payload)
return this.http.post(this.serverUrl, payload).pipe(
tap({
next: (response) => {
console.log(`Metrics for '${testName}' sent successfully:`, response);
this.resetMetrics();
},
error: (err) => console.error(`Failed to send metrics for '${testName}':`, err)
})
);
}
public cleanup(): void {
if (typeof window !== 'undefined') {
window.removeEventListener('deviceorientation', this.handleDeviceOrientation);
}
this.trackingSubscription?.unsubscribe();
this.trackingSubscription = null;
}
public resetMetrics(): void {
this.metricsLog = { interactions: [], deviceOrientations: [], arData: [] };
}
}