/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of OmiLAXR.xAPI. */ #if XAPI_REGISTRY_EXISTS using System.ComponentModel; using OmiLAXR.Composers; using OmiLAXR.TrackingBehaviours; using UnityEngine; namespace OmiLAXR.xAPI.Composers.Physiology { /// /// xAPI composer for creating learning analytics statements from heart rate tracking data. /// Generates physiological measurement statements when heart rate events are detected. /// Currently marked as not implemented - serves as a template for future heart rate analytics integration. /// [AddComponentMenu("OmiLAXR / 4) Composers / [xAPI] Heart Rate Composer")] [Description("Creates statements:\n- actor measured heart rate with heartRate(Int)")] public sealed class HeartRateComposer : xApiComposer { /// /// Categorizes this composer under physiological tracking for organizational purposes. /// Groups related composers together in the analytics pipeline for easier management. /// /// ComposerGroup.Physiology indicating this handles physiological data public override ComposerGroup GetGroup() => ComposerGroup.Physiology; /// /// Identifies the author of this composer implementation for attribution and support. /// /// Author information including name and contact details public override Author GetAuthor() => new Author("Sergej Görzen", "goerzen@cs.rwth-aachen.de"); /// /// Configures the xAPI statement composition logic for heart rate tracking events. /// Currently throws NotImplementedException - intended for future implementation /// when heart rate tracking capabilities are fully developed. /// /// HeartRateTrackingBehaviour instance to bind event handlers to protected override void Compose(HeartRateTrackingBehaviour tb) { // Disable the component to prevent unnecessary processing enabled = false; tb.OnHeartBeat.AddHandler((sender, hr) => { var stmt = actor.Does(xapi.ppm.verbs.measured) .Activity(xapi.ppm.activities.heartRate) .WithValue(xapi.ppm.extensions.result.heartRate(hr)); SendStatement(sender, stmt); }); tb.OnStatsUpdated.AddHandler((sender, stats) => { var stmt = actor.Does(xapi.ppm.verbs.updated) .Activity(xapi.ppm.activities.heartRate) .WithResult( xapi.ppm.extensions.result.hrBpmMean((int)Mathf.Round(stats.meanBpm)) .hrBpmVariance(stats.varianceBpm) .hrBpmStd(stats.stdBpm) .hrvSdnnMs(stats.sdnnMs) .hrvRmssdMs(stats.rmssdMs) .hrvPnn50(stats.pnn50) ) .WithContext( xapi.ppm.extensions.context.windowSeconds(stats.windowSeconds).method("BPM→IBI approx") ); SendStatement(sender, stmt); }); } } } #endif