diff --git a/src/app/components/test-suite/assessments/demographics-feedback/demographics-feedback.component.ts b/src/app/components/test-suite/assessments/demographics-feedback/demographics-feedback.component.ts index dc3edb1..73320ba 100644 --- a/src/app/components/test-suite/assessments/demographics-feedback/demographics-feedback.component.ts +++ b/src/app/components/test-suite/assessments/demographics-feedback/demographics-feedback.component.ts @@ -59,8 +59,6 @@ export class DemographicsFeedbackComponent implements OnInit, OnDestroy { ngOnInit() { this.createForm(); - // Start device orientation tracking for this component - this.metricsService.startDeviceOrientationTracking(); } logInteraction(event: Event) { diff --git a/src/app/components/test-suite/assessments/spatial-position-assessment/spatial-position-assessment.component.ts b/src/app/components/test-suite/assessments/spatial-position-assessment/spatial-position-assessment.component.ts index 6d4f795..fe56c57 100644 --- a/src/app/components/test-suite/assessments/spatial-position-assessment/spatial-position-assessment.component.ts +++ b/src/app/components/test-suite/assessments/spatial-position-assessment/spatial-position-assessment.component.ts @@ -41,8 +41,7 @@ export class SpatialPositionAssessmentComponent implements AfterViewInit, OnDest ngAfterViewInit() { const mv = this.modelViewerRef.nativeElement; - this.metricsService.startDeviceOrientationTracking(); - this.metricsService.startArTracking(mv); + this.metricsService.startTracking(mv); mv.addEventListener('ar-status', (e: any) => { if (e.detail.status === 'session-started') { setTimeout(() => this.captureAnchor(), 500); diff --git a/src/app/components/test-suite/assessments/spatial-stability-assessment/spatial-stability-assessment.component.ts b/src/app/components/test-suite/assessments/spatial-stability-assessment/spatial-stability-assessment.component.ts index 89daf38..c626cc4 100644 --- a/src/app/components/test-suite/assessments/spatial-stability-assessment/spatial-stability-assessment.component.ts +++ b/src/app/components/test-suite/assessments/spatial-stability-assessment/spatial-stability-assessment.component.ts @@ -48,8 +48,7 @@ export class SpatialStabilityAssessmentComponent implements AfterViewInit, OnDes ngAfterViewInit() { const modelViewer = this.modelViewerRef.nativeElement; - this.metricsService.startDeviceOrientationTracking(); - this.metricsService.startArTracking(modelViewer); + this.metricsService.startTracking(modelViewer); modelViewer.addEventListener('ar-status', (event: any) => { if (event.detail.status === 'session-started' && !this.isModelPlaced) { @@ -143,7 +142,7 @@ export class SpatialStabilityAssessmentComponent implements AfterViewInit, OnDes if (wasCompleted) { this.currentPhase = 4; } - + if(wasCompleted) { this.testComplete.emit(); } diff --git a/src/app/components/test-suite/assessments/text-legibility-assessment/text-legibility-assessment.component.ts b/src/app/components/test-suite/assessments/text-legibility-assessment/text-legibility-assessment.component.ts index c30666b..3ffbd12 100644 --- a/src/app/components/test-suite/assessments/text-legibility-assessment/text-legibility-assessment.component.ts +++ b/src/app/components/test-suite/assessments/text-legibility-assessment/text-legibility-assessment.component.ts @@ -48,8 +48,8 @@ export class TextLegibilityAssessmentComponent implements AfterViewInit, OnDestr } ngAfterViewInit() { - this.metricsService.startDeviceOrientationTracking(); - this.metricsService.startArTracking(this.modelViewerRef.nativeElement); + const mv = this.modelViewerRef.nativeElement; + this.metricsService.startTracking(mv); } public logInteraction(event: Event) { diff --git a/src/app/services/metrics-tracker.service.ts b/src/app/services/metrics-tracker.service.ts index 5d6f69f..203d025 100644 --- a/src/app/services/metrics-tracker.service.ts +++ b/src/app/services/metrics-tracker.service.ts @@ -3,166 +3,155 @@ import { inject, Injectable } from '@angular/core'; import { interval, Subscription, tap } from 'rxjs'; export interface InteractionEvent { - timestamp: number; - type: string; - elementId?: string; - elementTag?: string; - elementClasses?: string; - value?: any; + timestamp: number; + type: string; + elementId?: string; + elementTag?: string; + elementClasses?: string; + value?: any; } export interface DeviceOrientation { - timestamp: number; - alpha: number | null; - beta: number | null; - gamma: number | null; + timestamp: number; + alpha: number | null; + beta: number | null; + gamma: number | null; } export interface ArTrackingData { - timestamp: number; - anchor: string | null; - cameraOrbit: { - theta: number; - phi: number; - radius: number; - } | null; - cameraTarget: { - x: number; - y: number; - z: number; - } | null; + timestamp: number; + anchor: string | null; + cameraOrbit: { + theta: number; + phi: number; + radius: number; + } | null; + cameraTarget: { + x: number; + y: number; + z: number; + } | null; } export interface MetricsLog { - interactions: InteractionEvent[]; - deviceOrientations: DeviceOrientation[]; - arData: ArTrackingData[]; + interactions: InteractionEvent[]; + deviceOrientations: DeviceOrientation[]; + arData: ArTrackingData[]; } @Injectable({ - providedIn: 'root' + providedIn: 'root' }) export class MetricsTrackerService { - private http = inject(HttpClient); - private serverUrl = '/api/log'; - - private metricsLog: MetricsLog = { - interactions: [], - deviceOrientations: [], - arData: [] - }; + private http = inject(HttpClient); + private serverUrl = '/api/log'; - private deviceOrientationSubscription: Subscription | null = null; - private arTrackingSubscription: Subscription | null = null; - private lastDeviceOrientation: DeviceOrientationEvent | null = null; - - 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 }) + private metricsLog: MetricsLog = { + interactions: [], + deviceOrientations: [], + arData: [] }; - 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); + private trackingSubscription: Subscription | null = null; + private lastDeviceOrientation: DeviceOrientationEvent | null = null; + + 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: [] }; + } +} \ No newline at end of file