377 lines
10 KiB
C#
377 lines
10 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// BFI测试记录数据模型
|
|||
|
|
/// 包含测试的基本信息、数据点和统计信息
|
|||
|
|
/// </summary>
|
|||
|
|
[System.Serializable]
|
|||
|
|
public class BFITestRecord
|
|||
|
|
{
|
|||
|
|
[Header("基本信息")]
|
|||
|
|
public string TestId; // 测试ID
|
|||
|
|
public string TestName; // 测试名称
|
|||
|
|
public DateTime StartTime; // 开始时间
|
|||
|
|
public DateTime EndTime; // 结束时间
|
|||
|
|
public long StartTimeTicks; // 开始时间Ticks(用于JSON持久化)
|
|||
|
|
public long EndTimeTicks; // 结束时间Ticks(用于JSON持久化)
|
|||
|
|
public string PatientId; // 患者ID
|
|||
|
|
public string PatientName; // 患者姓名
|
|||
|
|
public string PatientGender; // 患者性别
|
|||
|
|
public int PatientAge; // 患者年龄
|
|||
|
|
public float PatientHeight; // 患者身高(cm)
|
|||
|
|
public float PatientWeight; // 患者体重(kg)
|
|||
|
|
|
|||
|
|
[Header("测试配置")]
|
|||
|
|
public float LowThreshold; // 低阈值
|
|||
|
|
public float HighThreshold; // 高阈值
|
|||
|
|
public int AlarmPriority; // 报警优先级
|
|||
|
|
public bool AlarmEnabled; // 是否启用报警
|
|||
|
|
|
|||
|
|
[Header("测试数据")]
|
|||
|
|
public List<BFIDataPoint> DataPoints; // BFI数据点集合
|
|||
|
|
|
|||
|
|
[Header("统计信息")]
|
|||
|
|
public BFIStatistics Statistics; // 统计数据
|
|||
|
|
|
|||
|
|
[Header("导出信息")]
|
|||
|
|
public DateTime ExportTime; // 导出时间
|
|||
|
|
public string ExportFormat; // 导出格式
|
|||
|
|
public string FilePath; // 文件路径
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 构造函数
|
|||
|
|
/// </summary>
|
|||
|
|
public BFITestRecord()
|
|||
|
|
{
|
|||
|
|
TestId = System.Guid.NewGuid().ToString();
|
|||
|
|
TestName = $"BFI测试_{DateTime.Now:yyyyMMdd_HHmmss}";
|
|||
|
|
StartTime = DateTime.Now;
|
|||
|
|
StartTimeTicks = StartTime.Ticks;
|
|||
|
|
EndTime = DateTime.MinValue;
|
|||
|
|
EndTimeTicks = 0;
|
|||
|
|
PatientGender = string.Empty;
|
|||
|
|
PatientAge = 0;
|
|||
|
|
PatientHeight = 0f;
|
|||
|
|
PatientWeight = 0f;
|
|||
|
|
DataPoints = new List<BFIDataPoint>();
|
|||
|
|
Statistics = new BFIStatistics();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 构造函数(带参数)
|
|||
|
|
/// </summary>
|
|||
|
|
public BFITestRecord(string testName, string patientId = null)
|
|||
|
|
{
|
|||
|
|
TestId = System.Guid.NewGuid().ToString();
|
|||
|
|
TestName = testName;
|
|||
|
|
PatientId = patientId;
|
|||
|
|
StartTime = DateTime.Now;
|
|||
|
|
StartTimeTicks = StartTime.Ticks;
|
|||
|
|
EndTime = DateTime.MinValue;
|
|||
|
|
EndTimeTicks = 0;
|
|||
|
|
PatientGender = string.Empty;
|
|||
|
|
PatientAge = 0;
|
|||
|
|
PatientHeight = 0f;
|
|||
|
|
PatientWeight = 0f;
|
|||
|
|
DataPoints = new List<BFIDataPoint>();
|
|||
|
|
Statistics = new BFIStatistics();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 添加数据点
|
|||
|
|
/// </summary>
|
|||
|
|
public void AddDataPoint(float bfiValue, string status = "正常", string notes = "")
|
|||
|
|
{
|
|||
|
|
var dataPoint = new BFIDataPoint
|
|||
|
|
{
|
|||
|
|
Timestamp = DateTime.Now,
|
|||
|
|
TimestampTicks = DateTime.Now.Ticks,
|
|||
|
|
BFI = bfiValue,
|
|||
|
|
Status = status,
|
|||
|
|
Notes = notes
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
DataPoints.Add(dataPoint);
|
|||
|
|
UpdateStatistics();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 结束测试
|
|||
|
|
/// </summary>
|
|||
|
|
public void EndTest()
|
|||
|
|
{
|
|||
|
|
EndTest(DateTime.Now);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 使用指定时间结束测试
|
|||
|
|
/// </summary>
|
|||
|
|
public void EndTest(DateTime endTime)
|
|||
|
|
{
|
|||
|
|
EndTime = endTime;
|
|||
|
|
EndTimeTicks = EndTime.Ticks;
|
|||
|
|
UpdateStatistics();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 重新计算统计信息
|
|||
|
|
/// </summary>
|
|||
|
|
public void RecalculateStatistics()
|
|||
|
|
{
|
|||
|
|
UpdateStatistics();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 序列化前同步时间字段
|
|||
|
|
/// </summary>
|
|||
|
|
public void PrepareForSerialization()
|
|||
|
|
{
|
|||
|
|
if (StartTime > DateTime.MinValue)
|
|||
|
|
{
|
|||
|
|
StartTimeTicks = StartTime.Ticks;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
EndTimeTicks = EndTime > DateTime.MinValue ? EndTime.Ticks : 0;
|
|||
|
|
|
|||
|
|
if (DataPoints != null)
|
|||
|
|
{
|
|||
|
|
foreach (var point in DataPoints)
|
|||
|
|
{
|
|||
|
|
point?.PrepareForSerialization();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 反序列化后恢复时间字段
|
|||
|
|
/// </summary>
|
|||
|
|
public void RestoreAfterDeserialization()
|
|||
|
|
{
|
|||
|
|
if (StartTimeTicks > 0)
|
|||
|
|
{
|
|||
|
|
StartTime = new DateTime(StartTimeTicks, DateTimeKind.Local);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (EndTimeTicks > 0)
|
|||
|
|
{
|
|||
|
|
EndTime = new DateTime(EndTimeTicks, DateTimeKind.Local);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (DataPoints != null)
|
|||
|
|
{
|
|||
|
|
foreach (var point in DataPoints)
|
|||
|
|
{
|
|||
|
|
point?.RestoreAfterDeserialization();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 兼容旧数据:没有有效开始时间时,使用当前时间避免出现公元1年
|
|||
|
|
if (StartTime <= DateTime.MinValue)
|
|||
|
|
{
|
|||
|
|
StartTime = DateTime.Now;
|
|||
|
|
StartTimeTicks = StartTime.Ticks;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 避免结束时间早于开始时间导致负时长
|
|||
|
|
if (EndTime > DateTime.MinValue && EndTime < StartTime)
|
|||
|
|
{
|
|||
|
|
EndTime = DateTime.MinValue;
|
|||
|
|
EndTimeTicks = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UpdateStatistics();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新统计信息
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdateStatistics()
|
|||
|
|
{
|
|||
|
|
if (DataPoints == null || DataPoints.Count == 0)
|
|||
|
|
{
|
|||
|
|
Statistics = new BFIStatistics();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var bfiValues = new List<float>();
|
|||
|
|
foreach (var point in DataPoints)
|
|||
|
|
{
|
|||
|
|
bfiValues.Add(point.BFI);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Statistics.Count = DataPoints.Count;
|
|||
|
|
Statistics.MinValue = bfiValues.Count > 0 ? Mathf.Min(bfiValues.ToArray()) : 0f;
|
|||
|
|
Statistics.MaxValue = bfiValues.Count > 0 ? Mathf.Max(bfiValues.ToArray()) : 0f;
|
|||
|
|
Statistics.Average = bfiValues.Count > 0 ? bfiValues.Average() : 0f;
|
|||
|
|
|
|||
|
|
// 计算标准差
|
|||
|
|
if (bfiValues.Count > 1)
|
|||
|
|
{
|
|||
|
|
float variance = bfiValues.Sum(x => Mathf.Pow(x - Statistics.Average, 2)) / bfiValues.Count;
|
|||
|
|
Statistics.StandardDeviation = Mathf.Sqrt(variance);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Statistics.Duration = EndTime > StartTime ? (EndTime - StartTime).TotalMinutes : 0;
|
|||
|
|
Statistics.LastUpdated = DateTime.Now;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取测试时长(分钟)
|
|||
|
|
/// </summary>
|
|||
|
|
public double GetDurationMinutes()
|
|||
|
|
{
|
|||
|
|
if (EndTime > StartTime)
|
|||
|
|
{
|
|||
|
|
return (EndTime - StartTime).TotalMinutes;
|
|||
|
|
}
|
|||
|
|
return (DateTime.Now - StartTime).TotalMinutes;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 检查是否有异常数据
|
|||
|
|
/// </summary>
|
|||
|
|
public bool HasAbnormalData()
|
|||
|
|
{
|
|||
|
|
if (DataPoints == null) return false;
|
|||
|
|
|
|||
|
|
foreach (var point in DataPoints)
|
|||
|
|
{
|
|||
|
|
if (point.BFI < LowThreshold || point.BFI > HighThreshold)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取异常数据点数量
|
|||
|
|
/// </summary>
|
|||
|
|
public int GetAbnormalDataCount()
|
|||
|
|
{
|
|||
|
|
if (DataPoints == null) return 0;
|
|||
|
|
|
|||
|
|
int count = 0;
|
|||
|
|
foreach (var point in DataPoints)
|
|||
|
|
{
|
|||
|
|
if (point.BFI < LowThreshold || point.BFI > HighThreshold)
|
|||
|
|
{
|
|||
|
|
count++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return count;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// BFI数据点
|
|||
|
|
/// </summary>
|
|||
|
|
[System.Serializable]
|
|||
|
|
public class BFIDataPoint
|
|||
|
|
{
|
|||
|
|
public DateTime Timestamp; // 时间戳
|
|||
|
|
public long TimestampTicks; // 时间戳Ticks(用于JSON持久化)
|
|||
|
|
public float BFI; // BFI值
|
|||
|
|
public string Status; // 状态(正常、异常、报警等)
|
|||
|
|
public string Notes; // 备注
|
|||
|
|
public int AlarmLevel; // 报警级别 (0=正常, 1=低, 2=中, 3=高)
|
|||
|
|
|
|||
|
|
public BFIDataPoint()
|
|||
|
|
{
|
|||
|
|
Timestamp = DateTime.Now;
|
|||
|
|
TimestampTicks = Timestamp.Ticks;
|
|||
|
|
Status = "正常";
|
|||
|
|
Notes = "";
|
|||
|
|
AlarmLevel = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 序列化前同步时间字段
|
|||
|
|
/// </summary>
|
|||
|
|
public void PrepareForSerialization()
|
|||
|
|
{
|
|||
|
|
if (Timestamp > DateTime.MinValue)
|
|||
|
|
{
|
|||
|
|
TimestampTicks = Timestamp.Ticks;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 反序列化后恢复时间字段
|
|||
|
|
/// </summary>
|
|||
|
|
public void RestoreAfterDeserialization()
|
|||
|
|
{
|
|||
|
|
if (TimestampTicks > 0)
|
|||
|
|
{
|
|||
|
|
Timestamp = new DateTime(TimestampTicks, DateTimeKind.Local);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Timestamp <= DateTime.MinValue)
|
|||
|
|
{
|
|||
|
|
Timestamp = DateTime.Now;
|
|||
|
|
TimestampTicks = Timestamp.Ticks;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// BFI统计信息
|
|||
|
|
/// </summary>
|
|||
|
|
[System.Serializable]
|
|||
|
|
public class BFIStatistics
|
|||
|
|
{
|
|||
|
|
public int Count; // 数据点总数
|
|||
|
|
public float MinValue; // 最小值
|
|||
|
|
public float MaxValue; // 最大值
|
|||
|
|
public float Average; // 平均值
|
|||
|
|
public float StandardDeviation; // 标准差
|
|||
|
|
public double Duration; // 测试时长(分钟)
|
|||
|
|
public DateTime LastUpdated; // 最后更新时间
|
|||
|
|
|
|||
|
|
public BFIStatistics()
|
|||
|
|
{
|
|||
|
|
Count = 0;
|
|||
|
|
MinValue = 0f;
|
|||
|
|
MaxValue = 0f;
|
|||
|
|
Average = 0f;
|
|||
|
|
StandardDeviation = 0f;
|
|||
|
|
Duration = 0;
|
|||
|
|
LastUpdated = DateTime.Now;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 扩展方法
|
|||
|
|
/// </summary>
|
|||
|
|
public static class ListExtensions
|
|||
|
|
{
|
|||
|
|
public static float Average(this List<float> source)
|
|||
|
|
{
|
|||
|
|
if (source == null || source.Count == 0)
|
|||
|
|
return 0f;
|
|||
|
|
|
|||
|
|
float sum = 0f;
|
|||
|
|
foreach (float value in source)
|
|||
|
|
{
|
|||
|
|
sum += value;
|
|||
|
|
}
|
|||
|
|
return sum / source.Count;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static float Sum(this List<float> source, System.Func<float, float> selector)
|
|||
|
|
{
|
|||
|
|
if (source == null || source.Count == 0)
|
|||
|
|
return 0f;
|
|||
|
|
|
|||
|
|
float sum = 0f;
|
|||
|
|
foreach (float value in source)
|
|||
|
|
{
|
|||
|
|
sum += selector(value);
|
|||
|
|
}
|
|||
|
|
return sum;
|
|||
|
|
}
|
|||
|
|
}
|