478 lines
16 KiB
C#
478 lines
16 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using CommunityToolkit.Mvvm.Input;
|
||
using PetWashControl.Models;
|
||
using PetWashControl.Services;
|
||
using System.Collections.ObjectModel;
|
||
using System.Text.Json;
|
||
using System.Windows;
|
||
|
||
namespace PetWashControl.ViewModels;
|
||
|
||
public partial class WashStep : ObservableObject
|
||
{
|
||
[ObservableProperty]
|
||
private string _name = "";
|
||
|
||
[ObservableProperty]
|
||
private string _status = "等待中";
|
||
|
||
[ObservableProperty]
|
||
private bool _isActive;
|
||
}
|
||
|
||
public partial class MainViewModel : ObservableObject
|
||
{
|
||
private readonly ApiService _apiService;
|
||
private readonly MqttClientService _mqttService;
|
||
private readonly ConfigurationService _config;
|
||
private readonly LogService _logger;
|
||
|
||
public event Action<string>? ViewChanged;
|
||
|
||
[ObservableProperty]
|
||
private ObservableCollection<Package> _packages = new();
|
||
|
||
[ObservableProperty]
|
||
private Package? _selectedPackage;
|
||
|
||
[ObservableProperty]
|
||
private Order? _currentOrder;
|
||
|
||
[ObservableProperty]
|
||
private string _statusMessage = "系统就绪";
|
||
|
||
[ObservableProperty]
|
||
private bool _isDoorOpen;
|
||
|
||
[ObservableProperty]
|
||
private bool _isWashing;
|
||
|
||
[ObservableProperty]
|
||
private bool _isConnected;
|
||
|
||
[ObservableProperty]
|
||
private string _currentView = "Idle";
|
||
|
||
[ObservableProperty]
|
||
private int _washProgress;
|
||
|
||
[ObservableProperty]
|
||
private string _currentStep = "";
|
||
|
||
[ObservableProperty]
|
||
private string _remainingTime = "00:00";
|
||
|
||
[ObservableProperty]
|
||
private ObservableCollection<WashStep> _washSteps = new();
|
||
|
||
public MainViewModel()
|
||
{
|
||
_config = new ConfigurationService();
|
||
_logger = new LogService();
|
||
_apiService = new ApiService(_config);
|
||
_mqttService = new MqttClientService(_config);
|
||
_mqttService.MessageReceived += OnMqttMessageReceived;
|
||
|
||
// 初始化洗护步骤
|
||
InitializeWashSteps();
|
||
}
|
||
|
||
private void InitializeWashSteps()
|
||
{
|
||
WashSteps.Add(new WashStep { Name = "第一次冲水", Status = "等待中" });
|
||
WashSteps.Add(new WashStep { Name = "沐浴露喷洒", Status = "等待中" });
|
||
WashSteps.Add(new WashStep { Name = "第二次冲水", Status = "等待中" });
|
||
WashSteps.Add(new WashStep { Name = "香波喷洒", Status = "等待中" });
|
||
WashSteps.Add(new WashStep { Name = "第三次冲水", Status = "等待中" });
|
||
WashSteps.Add(new WashStep { Name = "热风吹毛", Status = "等待中" });
|
||
WashSteps.Add(new WashStep { Name = "冷热风混合", Status = "等待中" });
|
||
}
|
||
|
||
public async Task InitializeAsync()
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInfo("开始初始化系统...");
|
||
|
||
await _mqttService.ConnectAsync();
|
||
IsConnected = _mqttService.IsConnected;
|
||
_logger.LogInfo("MQTT连接成功");
|
||
|
||
await LoadPackagesAsync();
|
||
StatusMessage = "系统就绪,请点击开始";
|
||
_logger.LogInfo("系统初始化完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("初始化失败", ex);
|
||
StatusMessage = $"初始化失败: {ex.Message}";
|
||
MessageBox.Show($"系统初始化失败,请检查后端服务是否启动。\n\n错误: {ex.Message}",
|
||
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task LoadPackagesAsync()
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInfo("加载套餐列表...");
|
||
var packages = await _apiService.GetPackagesAsync();
|
||
Packages.Clear();
|
||
foreach (var package in packages)
|
||
{
|
||
Packages.Add(package);
|
||
}
|
||
_logger.LogInfo($"成功加载 {packages.Count} 个套餐");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("加载套餐失败", ex);
|
||
StatusMessage = $"加载套餐失败: {ex.Message}";
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void ShowPayment()
|
||
{
|
||
CurrentView = "Payment";
|
||
ViewChanged?.Invoke("Payment");
|
||
StatusMessage = "请选择套餐";
|
||
_logger.LogInfo("切换到支付界面");
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void BackToIdle()
|
||
{
|
||
CurrentView = "Idle";
|
||
ViewChanged?.Invoke("Idle");
|
||
StatusMessage = "系统就绪,请点击开始";
|
||
SelectedPackage = null;
|
||
CurrentOrder = null;
|
||
IsDoorOpen = false;
|
||
|
||
// 重置洗护步骤状态
|
||
foreach (var step in WashSteps)
|
||
{
|
||
step.Status = "等待中";
|
||
step.IsActive = false;
|
||
}
|
||
|
||
_logger.LogInfo("返回待机界面");
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task SelectPackage(Package package)
|
||
{
|
||
if (package == null) return;
|
||
|
||
SelectedPackage = package;
|
||
_logger.LogInfo($"选择套餐: {package.Name}");
|
||
|
||
try
|
||
{
|
||
// 创建订单
|
||
CurrentOrder = await _apiService.CreateOrderAsync(package.Id);
|
||
StatusMessage = $"订单创建成功,请扫码支付 ¥{package.Price}";
|
||
_logger.LogInfo($"订单创建成功,订单ID: {CurrentOrder?.Id}");
|
||
|
||
// 切换到二维码支付界面
|
||
CurrentView = "QRCode";
|
||
ViewChanged?.Invoke("QRCode");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("创建订单失败", ex);
|
||
StatusMessage = $"创建订单失败: {ex.Message}";
|
||
MessageBox.Show($"创建订单失败\n\n{ex.Message}", "错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void CancelPayment()
|
||
{
|
||
_logger.LogInfo("取消支付");
|
||
CurrentOrder = null;
|
||
SelectedPackage = null;
|
||
CurrentView = "Payment";
|
||
ViewChanged?.Invoke("Payment");
|
||
StatusMessage = "已取消支付,请重新选择套餐";
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task CreateOrderAsync()
|
||
{
|
||
if (SelectedPackage == null)
|
||
{
|
||
MessageBox.Show("请先选择套餐", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_logger.LogInfo($"创建订单,套餐ID: {SelectedPackage.Id}");
|
||
CurrentOrder = await _apiService.CreateOrderAsync(SelectedPackage.Id);
|
||
StatusMessage = $"订单创建成功,订单号: {CurrentOrder?.Id},请支付 ¥{SelectedPackage.Price}";
|
||
_logger.LogInfo($"订单创建成功,订单ID: {CurrentOrder?.Id}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("创建订单失败", ex);
|
||
StatusMessage = $"创建订单失败: {ex.Message}";
|
||
MessageBox.Show($"创建订单失败\n\n{ex.Message}", "错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task SimulatePaymentAsync()
|
||
{
|
||
if (CurrentOrder == null)
|
||
{
|
||
MessageBox.Show("请先创建订单", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_logger.LogInfo($"模拟支付,订单ID: {CurrentOrder.Id}");
|
||
|
||
// 模拟支付处理延迟
|
||
StatusMessage = "正在处理支付...";
|
||
await Task.Delay(1500);
|
||
|
||
// 确认支付
|
||
CurrentOrder = await _apiService.ConfirmPaymentAsync(CurrentOrder.Id);
|
||
StatusMessage = "支付成功!设备门已打开,请将宠物放入";
|
||
IsDoorOpen = true;
|
||
_logger.LogInfo("支付成功,门已打开");
|
||
|
||
// 显示支付成功提示
|
||
MessageBox.Show("支付成功!\n\n设备门已自动打开\n请将宠物放入设备后关闭门开始洗护",
|
||
"支付成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
|
||
// 返回待机界面,显示关门按钮
|
||
CurrentView = "Idle";
|
||
ViewChanged?.Invoke("Idle");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("支付失败", ex);
|
||
StatusMessage = $"支付失败: {ex.Message}";
|
||
MessageBox.Show($"支付失败\n\n{ex.Message}", "错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
private async Task CloseDoorAsync()
|
||
{
|
||
if (CurrentOrder == null || !IsDoorOpen)
|
||
{
|
||
MessageBox.Show("门未打开", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_logger.LogInfo($"关门,订单ID: {CurrentOrder.Id}");
|
||
|
||
// 通过MQTT发送关门状态
|
||
await _mqttService.PublishAsync("device/status", new
|
||
{
|
||
status = "door_closed",
|
||
orderId = CurrentOrder.Id,
|
||
timestamp = DateTime.Now
|
||
});
|
||
|
||
// 更新订单状态
|
||
CurrentOrder = await _apiService.UpdateOrderStatusAsync(CurrentOrder.Id, OrderStatus.DoorClosed);
|
||
|
||
IsDoorOpen = false;
|
||
StatusMessage = "门已关闭,清洗即将开始...";
|
||
_logger.LogInfo("门已关闭,等待清洗开始");
|
||
|
||
// 切换到洗护界面
|
||
CurrentView = "Washing";
|
||
ViewChanged?.Invoke("Washing");
|
||
IsWashing = true;
|
||
CurrentStep = "第一次冲水";
|
||
WashProgress = 0;
|
||
|
||
// 开始模拟洗护流程
|
||
_ = SimulateWashingProcessAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("关门失败", ex);
|
||
StatusMessage = $"关门失败: {ex.Message}";
|
||
MessageBox.Show($"关门失败\n\n{ex.Message}", "错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
private async Task SimulateWashingProcessAsync()
|
||
{
|
||
try
|
||
{
|
||
var steps = new[]
|
||
{
|
||
("第一次冲水", 120),
|
||
("沐浴露喷洒", 30),
|
||
("第二次冲水", 180),
|
||
("香波喷洒", 30),
|
||
("第三次冲水", 180),
|
||
("热风吹毛", 300),
|
||
("冷热风混合", 120)
|
||
};
|
||
|
||
int totalDuration = steps.Sum(s => s.Item2);
|
||
int elapsed = 0;
|
||
|
||
for (int stepIndex = 0; stepIndex < steps.Length; stepIndex++)
|
||
{
|
||
var (stepName, duration) = steps[stepIndex];
|
||
CurrentStep = stepName;
|
||
_logger.LogInfo($"开始步骤: {stepName}");
|
||
|
||
// 更新步骤状态
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
// 标记之前的步骤为已完成
|
||
for (int i = 0; i < stepIndex; i++)
|
||
{
|
||
WashSteps[i].Status = "已完成";
|
||
WashSteps[i].IsActive = false;
|
||
}
|
||
|
||
// 标记当前步骤为进行中
|
||
WashSteps[stepIndex].Status = "进行中";
|
||
WashSteps[stepIndex].IsActive = true;
|
||
|
||
// 标记后续步骤为等待中
|
||
for (int i = stepIndex + 1; i < WashSteps.Count; i++)
|
||
{
|
||
WashSteps[i].Status = "等待中";
|
||
WashSteps[i].IsActive = false;
|
||
}
|
||
});
|
||
|
||
for (int i = 0; i <= duration; i++)
|
||
{
|
||
if (!IsWashing) return; // 如果被停止,退出
|
||
|
||
elapsed++;
|
||
WashProgress = (int)((double)elapsed / totalDuration * 100);
|
||
|
||
int remaining = totalDuration - elapsed;
|
||
int minutes = remaining / 60;
|
||
int seconds = remaining % 60;
|
||
RemainingTime = $"{minutes:D2}:{seconds:D2}";
|
||
|
||
await Task.Delay(100); // 加速模拟,实际应为1000ms
|
||
}
|
||
}
|
||
|
||
// 标记所有步骤为已完成
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
foreach (var step in WashSteps)
|
||
{
|
||
step.Status = "已完成";
|
||
step.IsActive = false;
|
||
}
|
||
});
|
||
|
||
// 洗护完成
|
||
await CompleteWashingAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("洗护流程失败", ex);
|
||
StatusMessage = $"洗护流程失败: {ex.Message}";
|
||
}
|
||
}
|
||
|
||
private async Task CompleteWashingAsync()
|
||
{
|
||
if (CurrentOrder == null) return;
|
||
|
||
try
|
||
{
|
||
_logger.LogInfo($"清洗完成,订单ID: {CurrentOrder.Id}");
|
||
|
||
// 发送清洗完成状态
|
||
await _mqttService.PublishAsync("device/status", new
|
||
{
|
||
status = "completed",
|
||
orderId = CurrentOrder.Id,
|
||
timestamp = DateTime.Now
|
||
});
|
||
|
||
CurrentOrder = await _apiService.UpdateOrderStatusAsync(CurrentOrder.Id, OrderStatus.Completed);
|
||
|
||
IsWashing = false;
|
||
IsDoorOpen = true;
|
||
StatusMessage = "清洗完成!请取出宠物";
|
||
|
||
MessageBox.Show("清洗完成!\n\n感谢使用无人自动洗宠机", "完成",
|
||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||
|
||
_logger.LogInfo("订单流程完成");
|
||
|
||
// 返回待机界面
|
||
CurrentOrder = null;
|
||
SelectedPackage = null;
|
||
BackToIdle();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("完成流程失败", ex);
|
||
StatusMessage = $"完成流程失败: {ex.Message}";
|
||
}
|
||
}
|
||
|
||
private void OnMqttMessageReceived(string topic, string payload)
|
||
{
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInfo($"收到MQTT消息 - Topic: {topic}, Payload: {payload}");
|
||
var message = JsonSerializer.Deserialize<JsonElement>(payload);
|
||
|
||
if (topic == "device/command")
|
||
{
|
||
var command = message.GetProperty("command").GetString();
|
||
|
||
if (command == "open_door")
|
||
{
|
||
IsDoorOpen = true;
|
||
StatusMessage = "设备门已打开,请将宠物放入后点击关门";
|
||
_logger.LogInfo("收到开门指令");
|
||
}
|
||
else if (command == "start_wash")
|
||
{
|
||
IsWashing = true;
|
||
var duration = message.GetProperty("durationMinutes").GetInt32();
|
||
StatusMessage = $"清洗已开始,预计 {duration} 分钟完成";
|
||
_logger.LogInfo($"收到开始清洗指令,时长: {duration}分钟");
|
||
}
|
||
}
|
||
else if (topic == "device/status")
|
||
{
|
||
var status = message.GetProperty("status").GetString();
|
||
StatusMessage = $"设备状态: {status}";
|
||
_logger.LogInfo($"设备状态更新: {status}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError("处理MQTT消息失败", ex);
|
||
StatusMessage = $"处理消息失败: {ex.Message}";
|
||
}
|
||
});
|
||
}
|
||
}
|