first push!

This commit is contained in:
2026-02-20 17:53:43 +01:00
parent ab073a6a39
commit e723ab86b9
274 changed files with 89671 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class ForceCurveDataLogger : MonoBehaviour
{
private List<float> _strokeBuffer = new List<float>();
void Start()
{
if (PerformanceMonitorManager.Instance != null)
PerformanceMonitorManager.Instance.OnForceCurveUpdated += HandleData;
}
void HandleData(List<float> points)
{
// 1. SIGNAL: Manager sends empty list when a NEW stroke starts
if (points.Count == 0)
{
if (_strokeBuffer.Count > 5)
{
LogFinishedStroke();
}
_strokeBuffer.Clear();
}
else
{
// 2. Accumulate the cumulative data
_strokeBuffer = new List<float>(points);
}
}
void LogFinishedStroke()
{
float maxForce = _strokeBuffer.Max();
float totalImpulse = _strokeBuffer.Sum(); // Area under the curve
string csv = string.Join(",", _strokeBuffer.Select(p => p.ToString("0")));
Debug.Log($"<color=#FF7F00><b>[STROKE RECORDED]</b></color> Impulse: {totalImpulse:N0} | Pts: {_strokeBuffer.Count} | Max: {maxForce}");
Debug.Log("CSV:" + csv);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a905dc9499e83430980b57cbc956dd70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,86 @@
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class ForceCurveHistoryDebugger : MonoBehaviour
{
private LineRenderer _lr;
private List<float> _historyPoints = new List<float>();
[Header("Settings")]
public float xScale = 0.05f; // Distance between points
public float yScale = 0.05f; // Height multiplier
public int maxPoints = 1000; // Keep last 1000 points (approx 20-30 seconds)
private int _lastPointsCount = 0;
private void Awake()
{
_lr = GetComponent<LineRenderer>();
_lr.positionCount = 0;
_lr.startWidth = 0.05f;
_lr.endWidth = 0.05f;
}
private void Start()
{
if (PerformanceMonitorManager.Instance != null)
{
PerformanceMonitorManager.Instance.OnForceCurveUpdated += OnCurveData;
}
}
private void OnCurveData(List<float> fullCurve)
{
// Calculate how many NEW points were added since last update
// (Since the Manager sends the whole cumulative curve of the current stroke)
int newPointsCount = fullCurve.Count - _lastPointsCount;
// If count dropped (new stroke started), we take the whole new list
if (newPointsCount < 0)
{
newPointsCount = fullCurve.Count;
AddPoints(fullCurve); // Add the whole new stroke start
}
else if (newPointsCount > 0)
{
// Just add the tail (the newest packet)
List<float> newSegment = fullCurve.GetRange(_lastPointsCount, newPointsCount);
AddPoints(newSegment);
}
_lastPointsCount = fullCurve.Count;
}
private void AddPoints(List<float> newPoints)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => {
_historyPoints.AddRange(newPoints);
// Trim history if too long
if (_historyPoints.Count > maxPoints)
{
_historyPoints.RemoveRange(0, _historyPoints.Count - maxPoints);
}
DrawGraph();
});
}
private void DrawGraph()
{
_lr.positionCount = _historyPoints.Count;
for (int i = 0; i < _historyPoints.Count; i++)
{
float x = i * xScale;
float y = _historyPoints[i] * yScale;
// Shift x so the graph scrolls to the left
// (Optional: Keep it static and let it grow to the right)
_lr.SetPosition(i, new Vector3(x, y, 0));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68892aba1c1c94fd7959380b4718584a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class ForceCurveVisualizer : MonoBehaviour
{
private LineRenderer _lr;
[Header("Visual Settings")]
public float xScale = 0.1f;
public float yScale = 0.05f; // Raw force is usually 0-200. 0.05 scales it to 0-10 height.
public float lineWidth = 0.1f;
public Color lineColor = Color.cyan;
private void Awake()
{
_lr = GetComponent<LineRenderer>();
// Setup LineRenderer programmatically if not set in Inspector
_lr.positionCount = 0;
_lr.startWidth = lineWidth;
_lr.endWidth = lineWidth;
_lr.useWorldSpace = false; // Important for UI/HUD
_lr.material = new Material(Shader.Find("Sprites/Default")); // Basic white material
_lr.startColor = lineColor;
_lr.endColor = lineColor;
}
private void Start()
{
if (PerformanceMonitorManager.Instance != null)
{
PerformanceMonitorManager.Instance.OnForceCurveUpdated += UpdateGraph;
}
}
private void UpdateGraph(List<float> points)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => {
if (points.Count < 2)
{
_lr.positionCount = 0;
return;
}
_lr.positionCount = points.Count;
// Draw
for (int i = 0; i < points.Count; i++)
{
float x = i * xScale;
float y = points[i] * yScale;
// Simple 3-point smoothing
if (i > 0 && i < points.Count - 1)
{
float prev = points[i-1] * yScale;
float next = points[i+1] * yScale;
y = (prev + y + next) / 3f;
}
_lr.SetPosition(i, new Vector3(x, y, 0));
}
});
}
// JUICE TIP: Use this to check for "Good Form"
// A good stroke is a smooth bell curve.
// A bad stroke has a "dip" in the middle (two peaks).
// You can analyze 'points' here to trigger the "sputtering engine" sound.
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6d1a8e151ddc4bae990fe2e86149e70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class PM5TestUI : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private TMP_Text wattsText;
[SerializeField] private TMP_Text spmText;
[SerializeField] private TMP_Text distanceText;
[SerializeField] private TMP_Text hrText; // NEW
[SerializeField] private TMP_Text caloriesText; // NEW
[SerializeField] private TMP_Text statusText;
[SerializeField] private Image statusIndicator;
[SerializeField] private Button connectButton;
[Header("Logging")]
[SerializeField] private Transform logContent;
[SerializeField] private GameObject logItemPrefab;
private void Start()
{
if (PerformanceMonitorManager.Instance != null)
{
PerformanceMonitorManager.Instance.OnLog += AddLog;
PerformanceMonitorManager.Instance.OnConnectionStateChanged += UpdateStatusUI;
PerformanceMonitorManager.Instance.OnStatsUpdated += UpdateDashboard;
}
connectButton.onClick.AddListener(() => {
PerformanceMonitorManager.Instance.StartScan();
});
UpdateStatusUI(false);
}
private void UpdateDashboard(PerformanceMonitorManager.RowingStats stats)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => {
if (wattsText) wattsText.text = $"{stats.Watts} W";
if (spmText) spmText.text = $"{stats.SPM} s/m";
if (distanceText) distanceText.text = $"{stats.Distance:F0} m";
if (hrText) hrText.text = stats.HeartRate > 0 ? $"{stats.HeartRate} bpm" : "--";
if (caloriesText) caloriesText.text = $"{stats.Calories} cal";
});
}
private void UpdateStatusUI(bool isConnected)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => {
if (statusText)
{
statusText.text = isConnected ? "CONNECTED" : "DISCONNECTED";
statusText.color = isConnected ? Color.green : Color.red;
}
if (statusIndicator)
statusIndicator.color = isConnected ? Color.green : Color.red;
if (connectButton)
connectButton.interactable = !isConnected;
});
}
private void AddLog(string message)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => {
if (logContent == null || logItemPrefab == null) return;
// Optional: Limit log size to prevent lag
if (logContent.childCount > 20)
Destroy(logContent.GetChild(0).gameObject);
GameObject newItem = Instantiate(logItemPrefab, logContent);
TMP_Text txt = newItem.GetComponent<TMP_Text>();
if (txt) txt.text = $"[{System.DateTime.Now:HH:mm:ss}] {message}";
Canvas.ForceUpdateCanvases();
ScrollRect sr = logContent.GetComponentInParent<ScrollRect>();
if (sr) sr.verticalNormalizedPosition = 0f;
});
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 039ab1d0f5dab403db97489b34b3aa0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: