Refactoring UI
This commit is contained in:
191
MainPage.xaml
191
MainPage.xaml
@ -2,34 +2,84 @@
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="HeartRateMonitorAndroid.MainPage"
|
||||
BackgroundColor="#F8F9FA">
|
||||
BackgroundColor="#F8F9FA"
|
||||
Title="心率监测">
|
||||
|
||||
<Grid Padding="20" RowSpacing="20" ColumnSpacing="20">
|
||||
<Grid Padding="20" RowSpacing="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 状态和心率标签 -->
|
||||
<VerticalStackLayout Grid.Row="0" Spacing="10">
|
||||
<Label x:Name="statusLabel"
|
||||
Text="正在初始化..."
|
||||
FontSize="18"
|
||||
TextColor="#6C757D"
|
||||
HorizontalOptions="Center" />
|
||||
<Label x:Name="heartRateLabel"
|
||||
Text="心率: -- bpm"
|
||||
FontSize="36"
|
||||
FontAttributes="Bold"
|
||||
TextColor="#FF4757"
|
||||
Margin="0,5,0,5"
|
||||
HorizontalOptions="Center" />
|
||||
</VerticalStackLayout>
|
||||
<!-- 服务状态指示器 -->
|
||||
<Frame Grid.Row="0" BorderColor="#E9ECEF" CornerRadius="15" Padding="15"
|
||||
BackgroundColor="White" HasShadow="True">
|
||||
<Grid ColumnSpacing="15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 状态指示灯 -->
|
||||
<Ellipse Grid.Column="0"
|
||||
x:Name="statusIndicator"
|
||||
Fill="#28A745"
|
||||
WidthRequest="16"
|
||||
HeightRequest="16"
|
||||
VerticalOptions="Center"/>
|
||||
|
||||
<!-- 状态文本 -->
|
||||
<VerticalStackLayout Grid.Column="1" Spacing="2">
|
||||
<Label x:Name="serviceStatusLabel"
|
||||
Text="后台服务运行中"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="#495057"/>
|
||||
<Label x:Name="connectionStatusLabel"
|
||||
Text="正在连接心率设备..."
|
||||
FontSize="14"
|
||||
TextColor="#6C757D"/>
|
||||
</VerticalStackLayout>
|
||||
|
||||
<!-- 数据上传状态 -->
|
||||
<Label Grid.Column="2"
|
||||
x:Name="uploadStatusLabel"
|
||||
Text="已连接"
|
||||
FontSize="12"
|
||||
TextColor="#28A745"
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="End"/>
|
||||
</Grid>
|
||||
</Frame>
|
||||
|
||||
<!-- 当前心率显示 -->
|
||||
<Frame Grid.Row="1" BorderColor="#E9ECEF" CornerRadius="15" Padding="20"
|
||||
BackgroundColor="White" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center" Spacing="5">
|
||||
<Label Text="当前心率"
|
||||
FontSize="16"
|
||||
TextColor="#6C757D"
|
||||
HorizontalOptions="Center"/>
|
||||
<Label x:Name="heartRateLabel"
|
||||
Text="-- bpm"
|
||||
FontSize="48"
|
||||
FontAttributes="Bold"
|
||||
TextColor="#FF4757"
|
||||
HorizontalOptions="Center"/>
|
||||
<Label x:Name="lastUpdateLabel"
|
||||
Text="等待数据..."
|
||||
FontSize="12"
|
||||
TextColor="#ADB5BD"
|
||||
HorizontalOptions="Center"/>
|
||||
</VerticalStackLayout>
|
||||
</Frame>
|
||||
|
||||
<!-- 心率统计信息 -->
|
||||
<Grid Grid.Row="1" ColumnSpacing="15">
|
||||
<Grid Grid.Row="2" ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@ -37,75 +87,76 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 最低心率 -->
|
||||
<Frame Grid.Column="0" BorderColor="#E9ECEF" BackgroundColor="#E8F5FE" CornerRadius="12" Padding="10" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center">
|
||||
<Label Text="最低心率" FontSize="14" TextColor="#2E86DE" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label x:Name="minHeartRateLabel" Text="--" FontSize="24" TextColor="#0056B3" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label Text="bpm" FontSize="12" TextColor="#2E86DE" HorizontalOptions="Center"/>
|
||||
<Frame Grid.Column="0" BorderColor="#E9ECEF" BackgroundColor="#E8F5FE" CornerRadius="12" Padding="15" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center" Spacing="3">
|
||||
<Label Text="最低" FontSize="12" TextColor="#2E86DE" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label x:Name="minHeartRateLabel" Text="--" FontSize="20" TextColor="#0056B3" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label Text="bpm" FontSize="10" TextColor="#2E86DE" HorizontalOptions="Center"/>
|
||||
</VerticalStackLayout>
|
||||
</Frame>
|
||||
|
||||
<!-- 平均心率 -->
|
||||
<Frame Grid.Column="1" BorderColor="#E9ECEF" BackgroundColor="#FFF5F5" CornerRadius="12" Padding="10" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center">
|
||||
<Label Text="平均心率" FontSize="14" TextColor="#FF4757" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label x:Name="avgHeartRateLabel" Text="--" FontSize="24" TextColor="#E01B30" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label Text="bpm" FontSize="12" TextColor="#FF4757" HorizontalOptions="Center"/>
|
||||
<Frame Grid.Column="1" BorderColor="#E9ECEF" BackgroundColor="#FFF5F5" CornerRadius="12" Padding="15" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center" Spacing="3">
|
||||
<Label Text="平均" FontSize="12" TextColor="#FF4757" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label x:Name="avgHeartRateLabel" Text="--" FontSize="20" TextColor="#E01B30" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label Text="bpm" FontSize="10" TextColor="#FF4757" HorizontalOptions="Center"/>
|
||||
</VerticalStackLayout>
|
||||
</Frame>
|
||||
|
||||
<!-- 最高心率 -->
|
||||
<Frame Grid.Column="2" BorderColor="#E9ECEF" BackgroundColor="#FFF0E5" CornerRadius="12" Padding="10" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center">
|
||||
<Label Text="最高心率" FontSize="14" TextColor="#FF8832" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label x:Name="maxHeartRateLabel" Text="--" FontSize="24" TextColor="#E65C00" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label Text="bpm" FontSize="12" TextColor="#FF8832" HorizontalOptions="Center"/>
|
||||
<Frame Grid.Column="2" BorderColor="#E9ECEF" BackgroundColor="#FFF0E5" CornerRadius="12" Padding="15" HasShadow="True">
|
||||
<VerticalStackLayout HorizontalOptions="Center" Spacing="3">
|
||||
<Label Text="最高" FontSize="12" TextColor="#FF8832" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label x:Name="maxHeartRateLabel" Text="--" FontSize="20" TextColor="#E65C00" FontAttributes="Bold" HorizontalOptions="Center"/>
|
||||
<Label Text="bpm" FontSize="10" TextColor="#FF8832" HorizontalOptions="Center"/>
|
||||
</VerticalStackLayout>
|
||||
</Frame>
|
||||
</Grid>
|
||||
|
||||
<!-- 心率图表 -->
|
||||
<Frame Grid.Row="2" BorderColor="#E9ECEF" CornerRadius="15" Padding="10" Margin="0,5,0,10"
|
||||
BackgroundColor="#F8F9FA" HasShadow="True">
|
||||
<Frame Grid.Row="3" BorderColor="#E9ECEF" CornerRadius="15" Padding="15"
|
||||
BackgroundColor="White" HasShadow="True">
|
||||
<Grid>
|
||||
<GraphicsView x:Name="heartRateGraphicsView" HeightRequest="250" WidthRequest="300" BackgroundColor="#F8F9FA"/>
|
||||
<Label x:Name="noDataLabel" Text="暂无数据" HorizontalOptions="Center" VerticalOptions="Center"
|
||||
IsVisible="True" TextColor="#6C757D" FontSize="16" FontAttributes="Italic"/>
|
||||
<GraphicsView x:Name="heartRateGraphicsView" BackgroundColor="White"/>
|
||||
<VerticalStackLayout x:Name="noDataLayout"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
IsVisible="True"
|
||||
Spacing="10">
|
||||
<Label Text="📊" FontSize="40" HorizontalOptions="Center"/>
|
||||
<Label Text="等待心率数据"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="#6C757D"
|
||||
FontSize="16"/>
|
||||
<Label Text="后台服务将自动连接设备并获取数据"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="#ADB5BD"
|
||||
FontSize="12"
|
||||
HorizontalTextAlignment="Center"/>
|
||||
</VerticalStackLayout>
|
||||
</Grid>
|
||||
</Frame>
|
||||
|
||||
<!-- 按钮区域 -->
|
||||
<Grid Grid.Row="3" ColumnSpacing="15" RowSpacing="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- 会话信息 -->
|
||||
<Frame Grid.Row="4" BorderColor="#E9ECEF" CornerRadius="15" Padding="15"
|
||||
BackgroundColor="White" HasShadow="True">
|
||||
<Grid ColumnSpacing="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Row="0" Grid.Column="0" Text="开始扫描" Clicked="OnScanClicked"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="#2E86DE"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="10"
|
||||
HeightRequest="50"/>
|
||||
<Button Grid.Row="0" Grid.Column="1" x:Name="backgroundButton" Text="后台运行" Clicked="OnBackgroundClicked"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="#FF4757"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="10"
|
||||
HeightRequest="50"/>
|
||||
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="webSocketButton" Text="配置数据上传" Clicked="OnWebSocketSettingsClicked"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
BackgroundColor="#6C757D"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="10"
|
||||
HeightRequest="50"/>
|
||||
</Grid>
|
||||
<VerticalStackLayout Grid.Column="0" Spacing="3">
|
||||
<Label Text="监测时长" FontSize="12" TextColor="#6C757D" FontAttributes="Bold"/>
|
||||
<Label x:Name="durationLabel" Text="00:00:00" FontSize="16" TextColor="#495057" FontAttributes="Bold"/>
|
||||
</VerticalStackLayout>
|
||||
|
||||
<VerticalStackLayout Grid.Column="1" Spacing="3">
|
||||
<Label Text="数据点数" FontSize="12" TextColor="#6C757D" FontAttributes="Bold"/>
|
||||
<Label x:Name="dataPointsLabel" Text="0" FontSize="16" TextColor="#495057" FontAttributes="Bold"/>
|
||||
</VerticalStackLayout>
|
||||
</Grid>
|
||||
</Frame>
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
388
MainPage.xaml.cs
388
MainPage.xaml.cs
@ -8,318 +8,224 @@ namespace HeartRateMonitorAndroid;
|
||||
public partial class MainPage : ContentPage
|
||||
{
|
||||
private const string Tag = "HeartRateMonitor";
|
||||
private const int GraphUpdateIntervalMs = 1000; // 每秒更新一次图表
|
||||
private const string DefaultWebsocketUrl = "wss://ws.nuanr-mxi.com/ws"; // 默认WebSocket服务器地址
|
||||
private const int UIUpdateIntervalMs = 1000; // 每秒更新一次UI
|
||||
|
||||
// 图表更新定时器
|
||||
private IDispatcherTimer _graphUpdateTimer;
|
||||
// UI更新定时器
|
||||
private IDispatcherTimer _uiUpdateTimer;
|
||||
|
||||
// WebSocket相关
|
||||
private WebSocketService.HeartRateWebSocketClient _webSocketClient;
|
||||
private bool _isWebSocketEnabled = false;
|
||||
private string _webSocketUrl = DefaultWebsocketUrl;
|
||||
// 数据<EFBFBD><EFBFBD>务
|
||||
private readonly IHeartRateDataService _dataService;
|
||||
|
||||
// 后台模式标志
|
||||
private bool _isRunningInBackground = false;
|
||||
|
||||
// 服务
|
||||
private readonly BluetoothService _bluetoothService;
|
||||
|
||||
// 心率数据模型
|
||||
private readonly HeartRateSessionData _sessionData = new();
|
||||
// 心率数据图表
|
||||
private readonly HeartRateGraphDrawable _heartRateGraph = new();
|
||||
|
||||
// 会话开始时间
|
||||
private DateTime _sessionStartTime = DateTime.Now;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 初始化蓝牙服务
|
||||
_bluetoothService = new BluetoothService();
|
||||
_bluetoothService.StatusUpdated += UpdateStatus;
|
||||
_bluetoothService.HeartRateUpdated += UpdateHeartRate;
|
||||
_bluetoothService.DeviceDiscovered += OnHeartRateDeviceDiscovered;
|
||||
// 获取数据服务实例
|
||||
_dataService = HeartRateDataService.Instance;
|
||||
|
||||
// 初始化图表
|
||||
heartRateGraphicsView.Drawable = _heartRateGraph;
|
||||
|
||||
// 初始化图表更新定时器
|
||||
InitializeGraphUpdateTimer();
|
||||
// 订阅服务事件
|
||||
SubscribeToServiceEvents();
|
||||
|
||||
// 检查蓝牙状态
|
||||
_bluetoothService.CheckBluetoothState();
|
||||
// 初始化UI更新定时器
|
||||
InitializeUITimer();
|
||||
|
||||
// 启动后台服务
|
||||
_ = StartBackgroundServiceAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化图表更新定时器
|
||||
/// 订阅服务事件
|
||||
/// </summary>
|
||||
private void InitializeGraphUpdateTimer()
|
||||
private void SubscribeToServiceEvents()
|
||||
{
|
||||
_graphUpdateTimer = Dispatcher.CreateTimer();
|
||||
_graphUpdateTimer.Interval = TimeSpan.FromMilliseconds(GraphUpdateIntervalMs);
|
||||
_graphUpdateTimer.Tick += async (s, e) => await UpdateGraph();
|
||||
_graphUpdateTimer.Start();
|
||||
_dataService.HeartRateDataReceived += OnHeartRateDataReceived;
|
||||
_dataService.ServiceStatusChanged += OnServiceStatusChanged;
|
||||
_dataService.DeviceStatusChanged += OnDeviceStatusChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新图表和通知
|
||||
/// 初始化UI更新定时器
|
||||
/// </summary>
|
||||
private async Task UpdateGraph()
|
||||
private void InitializeUITimer()
|
||||
{
|
||||
// 发送心率数据到服务器
|
||||
await SendHeartRateToServerAsync(_sessionData.LatestHeartRate);
|
||||
_uiUpdateTimer = Dispatcher.CreateTimer();
|
||||
_uiUpdateTimer.Interval = TimeSpan.FromMilliseconds(UIUpdateIntervalMs);
|
||||
_uiUpdateTimer.Tick += async (s, e) => await UpdateUI();
|
||||
_uiUpdateTimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动后台服务
|
||||
/// </summary>
|
||||
private async Task StartBackgroundServiceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateServiceStatusUI("正在启动后台服务...", false);
|
||||
await _dataService.StartBackgroundServiceAsync();
|
||||
Debug.WriteLine($"{Tag}: 后台服务启动请求已发送");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 启动后台服务失败: {ex.Message}");
|
||||
UpdateServiceStatusUI("服务启动失败", false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新UI界面
|
||||
/// </summary>
|
||||
private async Task UpdateUI()
|
||||
{
|
||||
var sessionData = _dataService.GetCurrentSessionData();
|
||||
|
||||
// 更新图表
|
||||
_heartRateGraph.UpdateData(_sessionData.HeartRateData);
|
||||
heartRateGraphicsView.Invalidate();
|
||||
|
||||
// 重置新数据标记
|
||||
_sessionData.ResetNewDataFlag();
|
||||
|
||||
// 如果在后台运行且有数据,更新通知
|
||||
if (_isRunningInBackground && _sessionData.HeartRateData.Count > 0)
|
||||
if (sessionData.HasNewHeartRateData)
|
||||
{
|
||||
var duration = _sessionData.GetSessionDuration();
|
||||
NotificationService.ShowHeartRateNotification(
|
||||
_sessionData.LatestHeartRate,
|
||||
_sessionData.AverageHeartRate,
|
||||
_sessionData.MinHeartRate,
|
||||
_sessionData.MaxHeartRate,
|
||||
duration);
|
||||
_heartRateGraph.UpdateData(sessionData.HeartRateData);
|
||||
heartRateGraphicsView.Invalidate();
|
||||
sessionData.ResetNewDataFlag();
|
||||
}
|
||||
|
||||
// 更新会话时长
|
||||
var duration = DateTime.Now - _sessionStartTime;
|
||||
durationLabel.Text = $"{duration:hh\\:mm\\:ss}";
|
||||
|
||||
// 更新数据点数
|
||||
dataPointsLabel.Text = sessionData.HeartRateData.Count.ToString();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新状态标签
|
||||
/// 心率数据接收事件处理
|
||||
/// </summary>
|
||||
private void UpdateStatus(string status)
|
||||
private void OnHeartRateDataReceived(int heartRate)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
statusLabel.Text = status;
|
||||
try
|
||||
{
|
||||
var sessionData = _dataService.GetCurrentSessionData();
|
||||
|
||||
// 更新当前心率显示
|
||||
heartRateLabel.Text = $"{heartRate} bpm";
|
||||
lastUpdateLabel.Text = $"更新时间: {DateTime.Now:HH:mm:ss}";
|
||||
|
||||
// 隐藏无数据提示
|
||||
if (sessionData.HeartRateData.Count >= 1)
|
||||
{
|
||||
noDataLayout.IsVisible = false;
|
||||
}
|
||||
|
||||
// 更新统计信息
|
||||
minHeartRateLabel.Text = sessionData.MinHeartRate.ToString();
|
||||
maxHeartRateLabel.Text = sessionData.MaxHeartRate.ToString();
|
||||
avgHeartRateLabel.Text = sessionData.AverageHeartRate.ToString("0");
|
||||
|
||||
//Debug.WriteLine($"{Tag}: UI已更新心率数据: {heartRate} bpm");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 更新心率UI失败: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新心率数据
|
||||
/// 服务状态变化事件处理
|
||||
/// </summary>
|
||||
private void UpdateHeartRate(int heartRate)
|
||||
private void OnServiceStatusChanged(ServiceStatus status)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
// 更新UI上的心率显示
|
||||
heartRateLabel.Text = $"心率: {heartRate} BPM";
|
||||
|
||||
// 添加新的心率数据点
|
||||
_sessionData.AddHeartRate(heartRate);
|
||||
|
||||
// 如果是第一个数据点,隐藏提示标签
|
||||
if (_sessionData.HeartRateData.Count == 1)
|
||||
try
|
||||
{
|
||||
noDataLabel.IsVisible = false;
|
||||
UpdateServiceStatusUI(status.StatusMessage, status.IsRunning);
|
||||
UpdateUploadStatusUI(status.IsWebSocketConnected ? "已连接" : "未连接", status.IsWebSocketConnected);
|
||||
|
||||
Debug.WriteLine($"{Tag}: 服务状态已更新: {status.StatusMessage}");
|
||||
}
|
||||
|
||||
// 更新统计信息显示
|
||||
minHeartRateLabel.Text = _sessionData.MinHeartRate.ToString();
|
||||
maxHeartRateLabel.Text = _sessionData.MaxHeartRate.ToString();
|
||||
avgHeartRateLabel.Text = _sessionData.AverageHeartRate.ToString("0");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扫描按钮点击事件
|
||||
/// </summary>
|
||||
private async void OnScanClicked(object sender, EventArgs e)
|
||||
{
|
||||
await _bluetoothService.StartScanAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 心率设备发现事件处理
|
||||
/// </summary>
|
||||
private async void OnHeartRateDeviceDiscovered(Plugin.BLE.Abstractions.Contracts.IDevice device)
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(async () => {
|
||||
statusLabel.Text = $"检测到心率设备: {device.Name ?? "未知设备"}";
|
||||
await _bluetoothService.ConnectToDeviceAsync(device);
|
||||
|
||||
// 连接成功后重置心率数据显示
|
||||
if (_bluetoothService.ConnectedDevice != null)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_sessionData.ResetData();
|
||||
minHeartRateLabel.Text = "--";
|
||||
maxHeartRateLabel.Text = "--";
|
||||
avgHeartRateLabel.Text = "--";
|
||||
noDataLabel.IsVisible = true;
|
||||
heartRateGraphicsView.Invalidate();
|
||||
Debug.WriteLine($"{Tag}: 更新服务状态UI失败: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后台运行按钮点击事件
|
||||
/// 设备状态变化事件处理
|
||||
/// </summary>
|
||||
private async void OnBackgroundClicked(object sender, EventArgs e)
|
||||
private void OnDeviceStatusChanged(DeviceConnectionStatus status)
|
||||
{
|
||||
if (_bluetoothService.ConnectedDevice == null)
|
||||
{
|
||||
await DisplayAlert("提示", "请先连接心率设备", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunningInBackground = !_isRunningInBackground;
|
||||
|
||||
if (_isRunningInBackground)
|
||||
{
|
||||
backgroundButton.Text = "停止后台运行";
|
||||
|
||||
// 显示通知
|
||||
if (_sessionData.HeartRateData.Count > 0)
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
try
|
||||
{
|
||||
TimeSpan duration = _sessionData.GetSessionDuration();
|
||||
NotificationService.ShowHeartRateNotification(
|
||||
_sessionData.LatestHeartRate,
|
||||
_sessionData.AverageHeartRate,
|
||||
_sessionData.MinHeartRate,
|
||||
_sessionData.MaxHeartRate,
|
||||
duration);
|
||||
connectionStatusLabel.Text = status.ConnectionMessage;
|
||||
|
||||
// 如果设备重新连接,重置会话开始时间
|
||||
if (status.IsConnected && !string.IsNullOrEmpty(status.DeviceName))
|
||||
{
|
||||
_sessionStartTime = DateTime.Now;
|
||||
|
||||
// 重置UI<55><49>示
|
||||
minHeartRateLabel.Text = "--";
|
||||
maxHeartRateLabel.Text = "--";
|
||||
avgHeartRateLabel.Text = "--";
|
||||
noDataLayout.IsVisible = true;
|
||||
heartRateGraphicsView.Invalidate();
|
||||
}
|
||||
|
||||
Debug.WriteLine($"{Tag}: 设备状态已更新: {status.ConnectionMessage}");
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
NotificationService.ShowHeartRateNotification(0, 0, 0, 0, TimeSpan.Zero);
|
||||
Debug.WriteLine($"{Tag}: 更新设<E696B0><E8AEBE>状态UI失败: {ex.Message}");
|
||||
}
|
||||
|
||||
// 通知用户应用将在后台运行
|
||||
await DisplayAlert("后台运行", "应用将在后台继续监测心率。可以通过通知栏查看实时数据。", "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundButton.Text = "后台运行";
|
||||
|
||||
// 取消通知
|
||||
Services.NotificationService.CancelNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket设置按钮点击事件
|
||||
/// 更新服务状态UI
|
||||
/// </summary>
|
||||
private async void OnWebSocketSettingsClicked(object sender, EventArgs e)
|
||||
private void UpdateServiceStatusUI(string status, bool isRunning)
|
||||
{
|
||||
string result = await DisplayPromptAsync(
|
||||
"配置数据上传",
|
||||
"请输入WebSocket服务器地址:\n格式:wss://example.com/ws",
|
||||
"确定",
|
||||
"取消",
|
||||
_webSocketUrl,
|
||||
maxLength: 100,
|
||||
keyboard: Keyboard.Url);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(result)) return;
|
||||
|
||||
if (!result.StartsWith("ws://") && !result.StartsWith("wss://"))
|
||||
{
|
||||
// fallback到默认websocket服务器
|
||||
result = _webSocketUrl;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 更新按钮状态,显示正在连接
|
||||
webSocketButton.Text = "正在连接...";
|
||||
webSocketButton.IsEnabled = false;
|
||||
|
||||
// 初始化WebSocket客户端
|
||||
await InitializeWebSocketClientAsync(result);
|
||||
|
||||
if (_isWebSocketEnabled)
|
||||
{
|
||||
webSocketButton.Text = "数据上传已启用";
|
||||
webSocketButton.BackgroundColor = Color.FromArgb("#28A745"); // 绿色
|
||||
await DisplayAlert("连接成功", "心率数据将会实时上传到服务器", "确定");
|
||||
// 禁用按钮
|
||||
webSocketButton.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
webSocketButton.Text = "配置数据上传";
|
||||
webSocketButton.BackgroundColor = Color.FromArgb("#6C757D"); // 灰色
|
||||
await DisplayAlert("连接失败", "无法连接到指定的WebSocket服务器,请检查地址或网络连接", "确定");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
webSocketButton.Text = "配置数据上传";
|
||||
webSocketButton.BackgroundColor = Color.FromArgb("#6C757D"); // 灰色
|
||||
await DisplayAlert("错误", $"配置WebSocket时出错: {ex.Message}", "确定");
|
||||
}
|
||||
finally
|
||||
{
|
||||
webSocketButton.IsEnabled = true;
|
||||
}
|
||||
serviceStatusLabel.Text = status;
|
||||
statusIndicator.Fill = isRunning ? Color.FromArgb("#28A745") : Color.FromArgb("#DC3545");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化WebSocket客户端
|
||||
/// 更新数据上传状态UI
|
||||
/// </summary>
|
||||
private async Task InitializeWebSocketClientAsync(string url = null)
|
||||
private void UpdateUploadStatusUI(string status, bool isConnected)
|
||||
{
|
||||
// 释放现有的WebSocket客户端
|
||||
if (_webSocketClient != null)
|
||||
{
|
||||
_webSocketClient.Dispose();
|
||||
_webSocketClient = null;
|
||||
}
|
||||
|
||||
// 使用提供的URL或默认URL
|
||||
_webSocketUrl = string.IsNullOrEmpty(url) ? DefaultWebsocketUrl : url;
|
||||
|
||||
try
|
||||
{
|
||||
_webSocketClient = new Services.WebSocketService.HeartRateWebSocketClient(_webSocketUrl);
|
||||
await _webSocketClient.ConnectAsync();
|
||||
_isWebSocketEnabled = true;
|
||||
Debug.WriteLine($"{Tag}: WebSocket客户端已初始化,连接到 {_webSocketUrl}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 初始化WebSocket客户端失败: {ex.Message}");
|
||||
_isWebSocketEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送心率数据到WebSocket服务器
|
||||
/// </summary>
|
||||
private async Task SendHeartRateToServerAsync(int heartRate)
|
||||
{
|
||||
if (!_isWebSocketEnabled || _webSocketClient == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var data = new WebSocketService.HeartRateData
|
||||
{
|
||||
HeartRate = heartRate,
|
||||
Timestamp = DateTime.Now,
|
||||
DeviceName = _bluetoothService.ConnectedDevice?.Name ?? "未知设备"
|
||||
};
|
||||
|
||||
await _webSocketClient.SendHeartRateDataAsync(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"发送心率数据失败: {ex.Message}");
|
||||
}
|
||||
uploadStatusLabel.Text = status;
|
||||
uploadStatusLabel.TextColor = isConnected ? Color.FromArgb("#28A745") : Color.FromArgb("#DC3545");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 页面卸载时清理资源
|
||||
/// </summary>
|
||||
~MainPage()
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
if (_webSocketClient != null)
|
||||
{
|
||||
_webSocketClient.Dispose();
|
||||
_webSocketClient = null;
|
||||
}
|
||||
base.OnDisappearing();
|
||||
|
||||
_bluetoothService.Dispose();
|
||||
// 停止定时器
|
||||
_uiUpdateTimer?.Stop();
|
||||
|
||||
// 取消订阅事件
|
||||
if (_dataService != null)
|
||||
{
|
||||
_dataService.HeartRateDataReceived -= OnHeartRateDataReceived;
|
||||
_dataService.ServiceStatusChanged -= OnServiceStatusChanged;
|
||||
_dataService.DeviceStatusChanged -= OnDeviceStatusChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Platforms/Android/AndroidBackgroundServiceStarter.cs
Normal file
56
Platforms/Android/AndroidBackgroundServiceStarter.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using Android.Content;
|
||||
using HeartRateMonitorAndroid.Services;
|
||||
using Application = Android.App.Application;
|
||||
namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
{
|
||||
/// <summary>
|
||||
/// Android平台的后台服务启动器实现
|
||||
/// </summary>
|
||||
public class AndroidBackgroundServiceStarter : IBackgroundServiceStarter
|
||||
{
|
||||
public async Task StartServiceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = Platform.CurrentActivity?.ApplicationContext ?? Application.Context;
|
||||
var intent = new Intent(context, typeof(HeartRateKeepAliveService));
|
||||
context.StartForegroundService(intent);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"AndroidBackgroundServiceStarter: 启动服务失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopServiceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = Platform.CurrentActivity?.ApplicationContext ?? Application.Context;
|
||||
var intent = new Intent(context, typeof(HeartRateKeepAliveService));
|
||||
context.StopService(intent);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"AndroidBackgroundServiceStarter: 停止服务失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Android平台的服务助手实现
|
||||
/// </summary>
|
||||
public class AndroidServiceHelper : IServiceHelper
|
||||
{
|
||||
public IBackgroundServiceStarter BackgroundServiceStarter { get; }
|
||||
|
||||
public AndroidServiceHelper()
|
||||
{
|
||||
BackgroundServiceStarter = new AndroidBackgroundServiceStarter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,13 +21,19 @@ namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
private const int NOTIFICATION_ID = 1001;
|
||||
private const string CHANNEL_ID = "HeartRateMonitorChannel";
|
||||
private const string CHANNEL_NAME = "心率监测服务";
|
||||
private const int REPORT_INTERVAL_MS = 1000; // 1秒汇报间隔
|
||||
|
||||
private PowerManager.WakeLock _wakeLock;
|
||||
private System.Timers.Timer _heartRateTimer;
|
||||
private WebSocketService.HeartRateWebSocketClient _webSocketClient;
|
||||
private BluetoothService _bluetoothService;
|
||||
private HeartRateDataService _dataService;
|
||||
private bool _isServiceRunning = false;
|
||||
|
||||
// 定时汇报相关
|
||||
private System.Threading.Timer _reportTimer;
|
||||
private int _latestHeartRate = 0;
|
||||
private readonly object _heartRateLock = new object();
|
||||
|
||||
public override void OnCreate()
|
||||
{
|
||||
base.OnCreate();
|
||||
@ -41,8 +47,6 @@ namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
if (!_isServiceRunning)
|
||||
{
|
||||
StartForegroundService();
|
||||
StartHeartRateMonitoring();
|
||||
ScheduleJobService();
|
||||
_isServiceRunning = true;
|
||||
}
|
||||
|
||||
@ -83,7 +87,7 @@ namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
var notification = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.SetContentTitle("心率监测服务")
|
||||
.SetContentText("正在后台监测心率数据")
|
||||
.SetSmallIcon(Resource.Drawable.abc_dialog_material_background) // 使用系统图标
|
||||
.SetSmallIcon(Resource.Drawable.abc_dialog_material_background)
|
||||
.SetContentIntent(pendingIntent)
|
||||
.SetOngoing(true)
|
||||
.SetPriority(NotificationCompat.PriorityLow)
|
||||
@ -106,276 +110,242 @@ namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 开始初始化服务");
|
||||
|
||||
// 获取服务实例
|
||||
var serviceProvider = MauiApplication.Current?.Services;
|
||||
_bluetoothService = serviceProvider?.GetService<BluetoothService>();
|
||||
// 获取数据服务实例
|
||||
_dataService = HeartRateDataService.Instance;
|
||||
_dataService.UpdateServiceStatus(false, "正在初始化服务...", false);
|
||||
|
||||
if (_bluetoothService == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 无法获取BluetoothService实例,创建新实例");
|
||||
_bluetoothService = new BluetoothService();
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 成功获取BluetoothService实例");
|
||||
}
|
||||
// 初始化蓝牙服务
|
||||
_bluetoothService = new BluetoothService();
|
||||
_bluetoothService.StatusUpdated += OnBluetoothStatusUpdated;
|
||||
_bluetoothService.HeartRateUpdated += OnHeartRateDataReceived;
|
||||
_bluetoothService.DeviceDiscovered += OnDeviceDiscovered;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 蓝牙服务已初始化");
|
||||
|
||||
// 初始化WebSocket客户端
|
||||
var serverUrl = GetServerUrl();
|
||||
if (!string.IsNullOrEmpty(serverUrl))
|
||||
{
|
||||
_webSocketClient = new WebSocketService.HeartRateWebSocketClient(serverUrl);
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: WebSocket客户端已初始化,服务器: {serverUrl}");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 无法获取服务器URL");
|
||||
}
|
||||
Task.Run(async () => await InitializeWebSocketAsync());
|
||||
|
||||
// 如果蓝牙服务可用,尝试重新连接之前连接的设备
|
||||
if (_bluetoothService != null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 启动蓝牙重连任务");
|
||||
Task.Run(async () => await ReconnectBluetoothDevice());
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 蓝牙服务不可用,跳过蓝牙重连");
|
||||
}
|
||||
// 启动蓝牙连接
|
||||
Task.Run(async () => await StartBluetoothMonitoringAsync());
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"InitializeServices Error: {ex.Message}");
|
||||
_dataService?.UpdateServiceStatus(false, "服务初始化失败", false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReconnectBluetoothDevice()
|
||||
private async Task InitializeWebSocketAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 尝试重新连接蓝牙设备");
|
||||
|
||||
// 检查蓝牙状态
|
||||
var bluetoothState = _bluetoothService.CheckBluetoothState();
|
||||
if (!bluetoothState.Contains("准备就绪"))
|
||||
var serverUrl = GetServerUrl();
|
||||
if (!string.IsNullOrEmpty(serverUrl))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 蓝牙状态不可用: {bluetoothState}");
|
||||
return;
|
||||
}
|
||||
_webSocketClient = new WebSocketService.HeartRateWebSocketClient(serverUrl);
|
||||
await _webSocketClient.ConnectAsync();
|
||||
|
||||
// 尝试获取上次连接的设备地址
|
||||
var lastConnectedDeviceAddress = _bluetoothService.GetLastConnectedDeviceAddress();
|
||||
if (!string.IsNullOrEmpty(lastConnectedDeviceAddress))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 尝试连接上次的设备: {lastConnectedDeviceAddress}");
|
||||
|
||||
// 首先尝试直接连接到已知设备
|
||||
var directConnectSuccess = await _bluetoothService.ConnectToDeviceByAddressAsync(lastConnectedDeviceAddress);
|
||||
|
||||
if (!directConnectSuccess)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 直接连接失败,开始扫描寻找设备");
|
||||
|
||||
// 注册临时设备发现事件处理器
|
||||
bool deviceFound = false;
|
||||
Action<Plugin.BLE.Abstractions.Contracts.IDevice> tempDeviceHandler = (device) =>
|
||||
{
|
||||
if (device.Id.ToString() == lastConnectedDeviceAddress)
|
||||
{
|
||||
deviceFound = true;
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 在扫描中找到目标设备: {device.Name}");
|
||||
Task.Run(async () => await _bluetoothService.ConnectToDeviceAsync(device));
|
||||
}
|
||||
};
|
||||
|
||||
_bluetoothService.DeviceDiscovered += tempDeviceHandler;
|
||||
|
||||
try
|
||||
{
|
||||
// 开始扫描
|
||||
await _bluetoothService.StartScanAsync();
|
||||
|
||||
// 等待10秒寻找目标设备
|
||||
for (int i = 0; i < 100 && !deviceFound; i++)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
// 停止扫描
|
||||
await _bluetoothService.StopScanAsync();
|
||||
|
||||
if (deviceFound)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 成功找到并连接目标设备");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 未找到目标设备");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_bluetoothService.DeviceDiscovered -= tempDeviceHandler;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 直接连接成功");
|
||||
}
|
||||
_dataService?.UpdateServiceStatus(true, "后台服务运行中", true, serverUrl);
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: WebSocket连接成功: {serverUrl}");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 没有找到上次连接的设备信息,开始新的扫描");
|
||||
|
||||
// 注册临时设备发现事件处理器
|
||||
bool anyDeviceFound = false;
|
||||
Action<Plugin.BLE.Abstractions.Contracts.IDevice> tempDeviceHandler = (device) =>
|
||||
{
|
||||
if (!anyDeviceFound)
|
||||
{
|
||||
anyDeviceFound = true;
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 发现心率设备: {device.Name}");
|
||||
Task.Run(async () => await _bluetoothService.ConnectToDeviceAsync(device));
|
||||
}
|
||||
};
|
||||
|
||||
_bluetoothService.DeviceDiscovered += tempDeviceHandler;
|
||||
|
||||
try
|
||||
{
|
||||
// 如果没有上次连接的设备信息,开始扫描
|
||||
await _bluetoothService.StartScanAsync();
|
||||
|
||||
// 扫描15秒后停止
|
||||
await Task.Delay(15000);
|
||||
await _bluetoothService.StopScanAsync();
|
||||
|
||||
if (anyDeviceFound)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 找到并连接了新设备");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 未找到任何心率设备");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_bluetoothService.DeviceDiscovered -= tempDeviceHandler;
|
||||
}
|
||||
_dataService?.UpdateServiceStatus(true, "后台服务运行中", false);
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 无法获取服务器URL,WebSocket未连接");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"ReconnectBluetoothDevice Error: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: WebSocket连接失败: {ex.Message}");
|
||||
_dataService?.UpdateServiceStatus(true, "后台服务运行中", false);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLastConnectedDeviceAddress()
|
||||
private async Task StartBluetoothMonitoringAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用MAUI的Preferences API获取设备地址
|
||||
return Microsoft.Maui.Storage.Preferences.Get("LastConnectedDevice", null);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"GetLastConnectedDeviceAddress Error: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 开始蓝牙监测");
|
||||
|
||||
private void SaveLastConnectedDeviceAddress(string deviceAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sharedPreferences = GetSharedPreferences("HeartRateMonitor", FileCreationMode.Private);
|
||||
var editor = sharedPreferences.Edit();
|
||||
editor.PutString("LastConnectedDevice", deviceAddress);
|
||||
editor.Apply();
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 保存设备地址: {deviceAddress}");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"SaveLastConnectedDeviceAddress Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
// 检查蓝牙状态
|
||||
_bluetoothService.CheckBluetoothState();
|
||||
|
||||
private string GetServerUrl()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从Resources/Raw/token.txt读取服务器URL
|
||||
using var stream = FileSystem.OpenAppPackageFileAsync("server.txt").Result;
|
||||
using var reader = new StreamReader(stream);
|
||||
|
||||
var contents = reader.ReadToEnd();
|
||||
return contents;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "wss:///ws.nuanr-mxi.com/ws"; // 默认URL
|
||||
}
|
||||
}
|
||||
|
||||
private void StartHeartRateMonitoring()
|
||||
{
|
||||
_heartRateTimer = new System.Timers.Timer(1000); // 30秒间隔
|
||||
_heartRateTimer.Elapsed += async (sender, e) =>
|
||||
{
|
||||
try
|
||||
if (_bluetoothService.IsBluetoothAvailable)
|
||||
{
|
||||
await MonitorHeartRate();
|
||||
_dataService?.UpdateDeviceStatus(false, "", "正在扫描心率设备...");
|
||||
|
||||
// 开始扫描心率设备
|
||||
await _bluetoothService.StartScanAsync();
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 蓝牙扫描已启动");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Heart rate monitoring error: {ex.Message}");
|
||||
_dataService?.UpdateDeviceStatus(false, "", "蓝牙不可用");
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 蓝牙不可用");
|
||||
}
|
||||
};
|
||||
_heartRateTimer.Start();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 蓝牙监测启动失败: {ex.Message}");
|
||||
_dataService?.UpdateDeviceStatus(false, "", "蓝牙监测启动失败");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MonitorHeartRate()
|
||||
private void OnBluetoothStatusUpdated(string status)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 蓝牙状态更新: {status}");
|
||||
_dataService?.UpdateDeviceStatus(false, "", status);
|
||||
}
|
||||
|
||||
private void OnHeartRateDataReceived(int heartRate)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保WebSocket连接
|
||||
//System.Diagnostics.Debug.WriteLine($"KeepAliveService: 收到心率数据: {heartRate} bpm");
|
||||
|
||||
// 只更新最新心率值,不立即发送数据
|
||||
lock (_heartRateLock)
|
||||
{
|
||||
_latestHeartRate = heartRate;
|
||||
}
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine($"KeepAliveService: 心率数据已更新: {heartRate} bpm");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 处理心率数据失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDeviceDiscovered(Plugin.BLE.Abstractions.Contracts.IDevice device)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 发现设备: {device.Name ?? "未知设备"}");
|
||||
|
||||
_dataService?.UpdateDeviceStatus(false, device.Name ?? "未知设备", $"发现设备: {device.Name ?? "未知设备"}");
|
||||
|
||||
// 尝试连接设备
|
||||
await _bluetoothService.ConnectToDeviceAsync(device);
|
||||
|
||||
if (_bluetoothService.ConnectedDevice != null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 设备连接成功: {device.Name}");
|
||||
|
||||
_dataService?.UpdateDeviceStatus(true, device.Name ?? "未知设备", $"已连接: {device.Name ?? "未知设备"}");
|
||||
_dataService?.ResetSessionData(); // 重置会话数据
|
||||
|
||||
// 启动定时汇报
|
||||
StartPeriodicReporting();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 设备连接失败: {ex.Message}");
|
||||
_dataService?.UpdateDeviceStatus(false, "", "设备连接失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动定时汇报(每秒一次)
|
||||
/// </summary>
|
||||
private void StartPeriodicReporting()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 停止现有定时器
|
||||
StopPeriodicReporting();
|
||||
|
||||
// 启动新的定时器
|
||||
_reportTimer = new System.Threading.Timer(OnPeriodicReport, null,
|
||||
TimeSpan.FromMilliseconds(REPORT_INTERVAL_MS),
|
||||
TimeSpan.FromMilliseconds(REPORT_INTERVAL_MS));
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 定时汇报已启动,间隔1秒");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 启动定时汇报失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止定时汇报
|
||||
/// </summary>
|
||||
private void StopPeriodicReporting()
|
||||
{
|
||||
try
|
||||
{
|
||||
_reportTimer?.Dispose();
|
||||
_reportTimer = null;
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 定时汇报已停止");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 停止定时汇报失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定时汇报回调方法
|
||||
/// </summary>
|
||||
private async void OnPeriodicReport(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_isServiceRunning) return;
|
||||
|
||||
int currentHeartRate;
|
||||
lock (_heartRateLock)
|
||||
{
|
||||
currentHeartRate = _latestHeartRate;
|
||||
}
|
||||
|
||||
// 无论心率数据是否更新,都进行汇报
|
||||
//System.Diagnostics.Debug.WriteLine($"KeepAliveService: 定时汇报 - 心率: {currentHeartRate} bpm");
|
||||
|
||||
// 更新数据服务(触发UI更新)
|
||||
if (currentHeartRate > 0)
|
||||
{
|
||||
_dataService?.UpdateHeartRateData(currentHeartRate);
|
||||
}
|
||||
|
||||
// 发送到WebSocket服务器
|
||||
if (_webSocketClient != null)
|
||||
{
|
||||
await _webSocketClient.ConnectAsync();
|
||||
|
||||
// 从蓝牙设备获取心率数据
|
||||
var heartRate = await GetCurrentHeartRate();
|
||||
if (heartRate > 0)
|
||||
{
|
||||
await _webSocketClient.SendHeartRateAsync(heartRate);
|
||||
UpdateNotification($"最新心率: {heartRate} BPM");
|
||||
}
|
||||
await SendHeartRateToServerAsync(currentHeartRate);
|
||||
}
|
||||
|
||||
// 更新通知
|
||||
var message = currentHeartRate > 0 ? $"最新心率: {currentHeartRate} BPM" : "等待心率数据...";
|
||||
UpdateNotification(message);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"MonitorHeartRate Error: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 定时汇报失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> GetCurrentHeartRate()
|
||||
private async Task SendHeartRateToServerAsync(int heartRate)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 只从蓝牙设备获取真实心率数据
|
||||
if (_bluetoothService?.ConnectedDevice != null)
|
||||
{
|
||||
// 从BluetoothService获取最新的心率值
|
||||
return _bluetoothService.LastHeartRate;
|
||||
}
|
||||
if (_webSocketClient == null) return;
|
||||
|
||||
// 如果没有连接设备,返回0表示无数据
|
||||
return 0;
|
||||
var data = new WebSocketService.HeartRateData
|
||||
{
|
||||
HeartRate = heartRate,
|
||||
Timestamp = DateTime.Now,
|
||||
DeviceName = _bluetoothService?.ConnectedDevice?.Name ?? "未知设备"
|
||||
};
|
||||
|
||||
await _webSocketClient.SendHeartRateDataAsync(data);
|
||||
//System.Diagnostics.Debug.WriteLine($"KeepAliveService: 心率数据已发送到服务器: {heartRate} bpm");
|
||||
}
|
||||
catch
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return 0;
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 发送心率数据失败: {ex.Message}");
|
||||
_dataService?.UpdateServiceStatus(true, "后台服务运行中", false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +360,7 @@ namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
var notification = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.SetContentTitle("心率监测服务")
|
||||
.SetContentText(message)
|
||||
.SetSmallIcon(Resource.Drawable.abc_dialog_material_background) // 使用系统图标
|
||||
.SetSmallIcon(Resource.Drawable.abc_dialog_material_background)
|
||||
.SetContentIntent(pendingIntent)
|
||||
.SetOngoing(true)
|
||||
.Build();
|
||||
@ -404,65 +374,52 @@ namespace HeartRateMonitorAndroid.Platforms.Android
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleJobService()
|
||||
private string GetServerUrl()
|
||||
{
|
||||
try
|
||||
{
|
||||
var jobScheduler = GetSystemService(JobSchedulerService) as JobScheduler;
|
||||
var jobInfo = new JobInfo.Builder(1002, new ComponentName(this, Java.Lang.Class.FromType(typeof(HeartRateJobService))))
|
||||
.SetRequiredNetworkType(NetworkType.Any)
|
||||
.SetPersisted(true)
|
||||
.SetPeriodic(15 * 60 * 1000) // 15分钟
|
||||
.SetRequiresCharging(false)
|
||||
.SetRequiresDeviceIdle(false)
|
||||
.Build();
|
||||
|
||||
jobScheduler?.Schedule(jobInfo);
|
||||
// 从资源文件读取服<E58F96><E69C8D><EFBFBD>器地址
|
||||
using var stream = Assets.Open("server.txt");
|
||||
using var reader = new System.IO.StreamReader(stream);
|
||||
return reader.ReadToEnd().Trim();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"ScheduleJobService Error: {ex.Message}");
|
||||
return "wss://ws.nuanr-mxi.com/ws"; // 默认服务器地址
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_isServiceRunning = false;
|
||||
_heartRateTimer?.Stop();
|
||||
_heartRateTimer?.Dispose();
|
||||
_webSocketClient?.Dispose();
|
||||
_wakeLock?.Release();
|
||||
|
||||
// 服务被销毁时,立即重启
|
||||
RestartService();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void RestartService()
|
||||
{
|
||||
try
|
||||
{
|
||||
var intent = new Intent(this, typeof(HeartRateKeepAliveService));
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||
{
|
||||
StartForegroundService(intent);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartService(intent);
|
||||
}
|
||||
_isServiceRunning = false;
|
||||
|
||||
// 停止定时汇报
|
||||
StopPeriodicReporting();
|
||||
|
||||
// 清理资源
|
||||
_bluetoothService?.Dispose();
|
||||
_webSocketClient?.Dispose();
|
||||
_wakeLock?.Release();
|
||||
|
||||
_dataService?.UpdateServiceStatus(false, "服务已停止", false);
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("KeepAliveService: 服务已停止");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"RestartService Error: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"KeepAliveService: 服务停止时发生错误: {ex.Message}");
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
public override void OnTaskRemoved(Intent rootIntent)
|
||||
{
|
||||
// 当任务被移除时重启服务
|
||||
RestartService();
|
||||
var intent = new Intent(this, typeof(HeartRateKeepAliveService));
|
||||
StartForegroundService(intent);
|
||||
base.OnTaskRemoved(rootIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
using HeartRateMonitorAndroid.Services;
|
||||
|
||||
namespace HeartRateMonitorAndroid;
|
||||
|
||||
@ -12,4 +13,12 @@ public class MainApplication : MauiApplication
|
||||
}
|
||||
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
|
||||
public override void OnCreate()
|
||||
{
|
||||
base.OnCreate();
|
||||
|
||||
// 设置Android平台特定的服务助手
|
||||
ServiceHelper.Current = new Platforms.Android.AndroidServiceHelper();
|
||||
}
|
||||
}
|
||||
232
Services/HeartRateDataService.cs
Normal file
232
Services/HeartRateDataService.cs
Normal file
@ -0,0 +1,232 @@
|
||||
using HeartRateMonitorAndroid.Models;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace HeartRateMonitorAndroid.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 心率数据服务实现,作为前端与后台服务的桥梁
|
||||
/// </summary>
|
||||
public class HeartRateDataService : IHeartRateDataService
|
||||
{
|
||||
private const string Tag = "HeartRateDataService";
|
||||
|
||||
// 静态实例,用于在后台服务和前端之间共享数据
|
||||
private static HeartRateDataService _instance;
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
// 事件
|
||||
public event Action<int> HeartRateDataReceived;
|
||||
public event Action<ServiceStatus> ServiceStatusChanged;
|
||||
public event Action<DeviceConnectionStatus> DeviceStatusChanged;
|
||||
|
||||
// 数据存储
|
||||
private readonly HeartRateSessionData _sessionData = new();
|
||||
private ServiceStatus _serviceStatus = new();
|
||||
private DeviceConnectionStatus _deviceStatus = new();
|
||||
|
||||
private HeartRateDataService()
|
||||
{
|
||||
// 私有构造函数,确保单例模式
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单例实例
|
||||
/// </summary>
|
||||
public static HeartRateDataService Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new HeartRateDataService();
|
||||
}
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前心率数据
|
||||
/// </summary>
|
||||
public HeartRateSessionData GetCurrentSessionData()
|
||||
{
|
||||
return _sessionData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务状态
|
||||
/// </summary>
|
||||
public ServiceStatus GetServiceStatus()
|
||||
{
|
||||
return _serviceStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备连接状态
|
||||
/// </summary>
|
||||
public DeviceConnectionStatus GetDeviceStatus()
|
||||
{
|
||||
return _deviceStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动后台服务(平台特定实现将在调用时提供)
|
||||
/// </summary>
|
||||
public async Task StartBackgroundServiceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 通过依赖注入获取平台特定的服务启动器
|
||||
var serviceStarter = ServiceHelper.Current?.BackgroundServiceStarter;
|
||||
if (serviceStarter != null)
|
||||
{
|
||||
await serviceStarter.StartServiceAsync();
|
||||
Debug.WriteLine($"{Tag}: 后台服务启动请求已发送");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 无法获取平台特定的服务启动器");
|
||||
throw new PlatformNotSupportedException("当前平台不支持后台服务");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 启动后台服务失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止后台服务
|
||||
/// </summary>
|
||||
public async Task StopBackgroundServiceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var serviceStarter = ServiceHelper.Current?.BackgroundServiceStarter;
|
||||
if (serviceStarter != null)
|
||||
{
|
||||
await serviceStarter.StopServiceAsync();
|
||||
Debug.WriteLine($"{Tag}: 后台服务停止请求已发送");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 无法获取平台特定的服务启动器");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 停止后台服务失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// 以下方法供后台服务调用,用于更新数据和状态
|
||||
|
||||
/// <summary>
|
||||
/// 更新心率数据(由后台服务调用)
|
||||
/// </summary>
|
||||
public void UpdateHeartRateData(int heartRate)
|
||||
{
|
||||
try
|
||||
{
|
||||
_sessionData.AddHeartRate(heartRate);
|
||||
HeartRateDataReceived?.Invoke(heartRate);
|
||||
//Debug.WriteLine($"{Tag}: 心率数据已更新: {heartRate} bpm");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 更新心率数据失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新服务状态(由后台服务调用)
|
||||
/// </summary>
|
||||
public void UpdateServiceStatus(bool isRunning, string statusMessage, bool isWebSocketConnected, string webSocketUrl = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
_serviceStatus.IsRunning = isRunning;
|
||||
_serviceStatus.StatusMessage = statusMessage;
|
||||
_serviceStatus.IsWebSocketConnected = isWebSocketConnected;
|
||||
_serviceStatus.WebSocketUrl = webSocketUrl;
|
||||
_serviceStatus.LastUpdateTime = DateTime.Now;
|
||||
|
||||
ServiceStatusChanged?.Invoke(_serviceStatus);
|
||||
Debug.WriteLine($"{Tag}: 服务状态已更新: {statusMessage}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 更新服务状态失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新设备连接状态(由后台服务调用)
|
||||
/// </summary>
|
||||
public void UpdateDeviceStatus(bool isConnected, string deviceName, string connectionMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
_deviceStatus.IsConnected = isConnected;
|
||||
_deviceStatus.DeviceName = deviceName;
|
||||
_deviceStatus.ConnectionMessage = connectionMessage;
|
||||
_deviceStatus.LastConnectionTime = DateTime.Now;
|
||||
|
||||
DeviceStatusChanged?.Invoke(_deviceStatus);
|
||||
Debug.WriteLine($"{Tag}: 设备状态已更新: {connectionMessage}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 更新设备状态失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置会话数据(由后台服务调用)
|
||||
/// </summary>
|
||||
public void ResetSessionData()
|
||||
{
|
||||
try
|
||||
{
|
||||
_sessionData.ResetData();
|
||||
Debug.WriteLine($"{Tag}: 会话数据已重置");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 重置会话数据失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 平台特定服务启动器接口
|
||||
/// </summary>
|
||||
public interface IBackgroundServiceStarter
|
||||
{
|
||||
Task StartServiceAsync();
|
||||
Task StopServiceAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务助手类,用于获取平台特定的服务
|
||||
/// </summary>
|
||||
public static class ServiceHelper
|
||||
{
|
||||
public static IServiceHelper Current { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务助手接口
|
||||
/// </summary>
|
||||
public interface IServiceHelper
|
||||
{
|
||||
IBackgroundServiceStarter BackgroundServiceStarter { get; }
|
||||
}
|
||||
}
|
||||
74
Services/IHeartRateDataService.cs
Normal file
74
Services/IHeartRateDataService.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using HeartRateMonitorAndroid.Models;
|
||||
|
||||
namespace HeartRateMonitorAndroid.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 心率数据服务接口,用于前端与后台服务通信
|
||||
/// </summary>
|
||||
public interface IHeartRateDataService
|
||||
{
|
||||
/// <summary>
|
||||
/// 心率数据更新事件
|
||||
/// </summary>
|
||||
event Action<int> HeartRateDataReceived;
|
||||
|
||||
/// <summary>
|
||||
/// 服务状态更新事件
|
||||
/// </summary>
|
||||
event Action<ServiceStatus> ServiceStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 设备连接状态更新事件
|
||||
/// </summary>
|
||||
event Action<DeviceConnectionStatus> DeviceStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前心率数据
|
||||
/// </summary>
|
||||
HeartRateSessionData GetCurrentSessionData();
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务状态
|
||||
/// </summary>
|
||||
ServiceStatus GetServiceStatus();
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备连接状态
|
||||
/// </summary>
|
||||
DeviceConnectionStatus GetDeviceStatus();
|
||||
|
||||
/// <summary>
|
||||
/// 启动后台服务
|
||||
/// </summary>
|
||||
Task StartBackgroundServiceAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 停止后台服务
|
||||
/// </summary>
|
||||
Task StopBackgroundServiceAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务状态
|
||||
/// </summary>
|
||||
public class ServiceStatus
|
||||
{
|
||||
public bool IsRunning { get; set; }
|
||||
public string StatusMessage { get; set; } = "";
|
||||
public bool IsWebSocketConnected { get; set; }
|
||||
public string WebSocketUrl { get; set; } = "";
|
||||
public DateTime LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备连接状态
|
||||
/// </summary>
|
||||
public class DeviceConnectionStatus
|
||||
{
|
||||
public bool IsConnected { get; set; }
|
||||
public string DeviceName { get; set; } = "";
|
||||
public string ConnectionMessage { get; set; } = "";
|
||||
public DateTime LastConnectionTime { get; set; }
|
||||
public int SignalStrength { get; set; } // 信号强度,如果支持的话
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user