using System.Collections.ObjectModel; using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json.Serialization; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Sln.Wcs.Model.Domain; using Sln.Wcs.Repository.service; using Sln.Wcs.Serilog; namespace Sln.Wcs.UI.ViewModels.Task; public partial class ManualTaskViewModel : ObservableObject { private const string HoistBaseUrl = "http://localhost:5100"; private readonly ILiveTaskQueueService _taskQueueService; private readonly ILiveTaskDetailService _taskDetailService; private readonly HttpClient _http; private readonly SerilogHelper _logger; private FreeHoistResponse? _allocatedHoist; private Dictionary hoistDic = new Dictionary(); public string PageTitle => "手动执行任务"; [ObservableProperty] private ObservableCollection _tasks = new(); [ObservableProperty] private LiveTaskQueue? _selectedTask; [ObservableProperty] private ObservableCollection _details = new(); [ObservableProperty] private string _statusText = string.Empty; [ObservableProperty] private bool _isExecuting; [ObservableProperty] private bool _isMaterialReady; [ObservableProperty] private bool _taskExecuted; [ObservableProperty] private string _hoistInfo = "--"; [ObservableProperty] private string _dockingPoint = "--"; public ManualTaskViewModel( ILiveTaskQueueService taskQueueService, ILiveTaskDetailService taskDetailService, SerilogHelper logger) { _taskQueueService = taskQueueService; _taskDetailService = taskDetailService; _http = new HttpClient { Timeout = System.TimeSpan.FromSeconds(5) }; _logger = logger; } public Avalonia.Controls.Control CreateView() => new Views.Task.ManualTaskView(); [RelayCommand] private void Load() { RefreshTaskList(); } private void RefreshTaskList() { List list = _taskQueueService.getLiveTaskQueues(x=>x.executionMode == 1 && x.isFlag == 1); Tasks = new ObservableCollection(list); StatusText = list.Count > 0 ? $"查询到 {list.Count} 条待执行手动任务" : "暂无待执行的手动任务"; } partial void OnSelectedTaskChanged(LiveTaskQueue? value) { TaskExecuted = false; _allocatedHoist = null; HoistInfo = "--"; DockingPoint = "--"; if (value is not null) LoadDetails(value.taskCode); } private void LoadDetails(string taskCode) { var detailList = _taskDetailService.Query(x => x.taskCode == taskCode) .OrderBy(d => d.objId) .ToList(); Details = new ObservableCollection(detailList); //StatusText = $"任务 {taskCode},共 {detailList.Count} 条明细"; } /// /// 任务执行:调用提升机调度中心 API 获取空闲提升机后下发任务 /// [RelayCommand] private async System.Threading.Tasks.Task ExecuteTaskAsync() { if (SelectedTask is null) { StatusText = "请先选择一条任务"; return; } if (SelectedTask.taskStatus != 1) { StatusText = "当前任务状态不允许执行"; return; } var hoistDetail = Details.FirstOrDefault(d => d.deviceType == 2); if (hoistDetail is null) { StatusText = "当前任务未包含提升机步骤,无需手动执行"; return; } IsExecuting = true; StatusText = "正在获取空闲提升机..."; try { // 1. 获取空闲提升机 string hostCode = "1#Host"; var freeUrl = $"{HoistBaseUrl}/api/hoist/free?hostCode={Uri.EscapeDataString(hostCode)}"; var freeRes = await _http.GetFromJsonAsync(freeUrl); if (freeRes is not { found: true }) { StatusText = "暂无空闲提升机,请稍后重新获取"; HoistInfo = "忙碌"; _logger.Info($"手动任务 {SelectedTask.taskCode} 获取空闲提升机失败:无空闲设备"); return; } _allocatedHoist = freeRes; HoistInfo = $"{freeRes.deviceName ?? freeRes.deviceCode} (空闲)"; DockingPoint = $"{freeRes.deviceSerialNo}" ?? "--"; StatusText = $"已获取空闲提升机 {freeRes.deviceName ?? freeRes.deviceCode},正在下发任务..."; hoistDic.Add(SelectedTask.taskCode,freeRes); // 2. 下发提升机调度任务 var dispatchBody = new { hostCode = freeRes.hostCode, serialNo = freeRes.deviceSerialNo, taskCode = hoistDetail.taskCode, startPoint = hoistDetail.startPoint, endPoint = hoistDetail.endPoint }; var dispatchRes = await _http.PostAsJsonAsync($"{HoistBaseUrl}/api/task/dispatch", dispatchBody); var dispatchResult = await dispatchRes.Content.ReadFromJsonAsync(); if (dispatchResult is not { success: true }) { StatusText = $"任务下发失败: {dispatchResult?.msg ?? "未知错误"}"; _logger.Error($"手动任务 {SelectedTask.taskCode} 下发失败"); return; } // 3. 更新任务状态为执行中 await System.Threading.Tasks.Task.Run(() => { SelectedTask.taskStatus = 2; _taskQueueService.Update(SelectedTask); hoistDetail.taskStatus = 2; _taskDetailService.Update(hoistDetail); }); TaskExecuted = true; _logger.Info($"手动任务 {SelectedTask.taskCode} 已下发到提升机 {freeRes.deviceCode}"); StatusText = $"任务 {SelectedTask.taskCode} 已下发到提升机 {freeRes.deviceName ?? freeRes.deviceCode}"; LoadDetails(SelectedTask.taskCode); RefreshTaskList(); } catch (HttpRequestException ex) { TaskExecuted = false; _allocatedHoist = null; HoistInfo = "--"; StatusText = $"与提升机调度中心通信失败: {ex.Message}"; _logger.Error($"HoistServer 通信失败: {ex.Message}"); } catch (Exception ex) { TaskExecuted = false; _allocatedHoist = null; HoistInfo = "--"; StatusText = $"任务执行失败: {ex.Message}"; _logger.Error($"手动任务执行失败: {ex.Message}"); } finally { IsExecuting = false; } } /// /// 物料到位确认:调用提升机调度中心 receive-pallet 接口 /// [RelayCommand] private async System.Threading.Tasks.Task MaterialReadyAsync() { if (SelectedTask is null) { StatusText = "请先选择一条任务"; return; } hoistDic.TryGetValue(SelectedTask.taskCode, out _allocatedHoist); if (_allocatedHoist is null) { StatusText = "请先执行任务获取空闲提升机,再确认物料到位"; return; } var hoistDetail = Details.FirstOrDefault(d => d.deviceType == 2); if (hoistDetail is null) { StatusText = "未找到提升机任务明细"; return; } IsMaterialReady = true; StatusText = "正在确认物料到位..."; try { var body = new { hostCode = _allocatedHoist.hostCode, serialNo = _allocatedHoist.deviceSerialNo, taskCode = hoistDetail.taskCode, palletBarcode = hoistDetail.palletBarcode ?? "", startPoint = hoistDetail.startPoint, endPoint = hoistDetail.endPoint }; var res = await _http.PostAsJsonAsync($"{HoistBaseUrl}/api/hoist/receive-pallet", body); var result = await res.Content.ReadFromJsonAsync(); if (result is not { success: true }) { StatusText = $"receive-pallet 调用失败: {result?.msg ?? "未知错误"}"; _logger.Error($"receive-pallet 失败: {result?.msg}"); return; } // 更新备注 await System.Threading.Tasks.Task.Run(() => { SelectedTask.remark = "物料已到位"; _taskQueueService.Update(SelectedTask); }); _logger.Info($"手动任务 {SelectedTask.taskCode} 物料到位确认,已调用 receive-pallet"); StatusText = $"任务 {SelectedTask.taskCode} 物料已到位,提升机已启动"; LoadDetails(SelectedTask.taskCode); } catch (HttpRequestException ex) { StatusText = $"与提升机调度中心通信失败: {ex.Message}"; _logger.Error($"HoistServer 通信失败: {ex.Message}"); } catch (Exception ex) { StatusText = $"物料到位确认失败: {ex.Message}"; _logger.Error($"物料到位确认失败: {ex.Message}"); } finally { IsMaterialReady = false; } } // ---- API 响应 DTO ---- private class FreeHoistResponse { public bool found { get; set; } public string? deviceCode { get; set; } public string? deviceName { get; set; } public string? hostCode { get; set; } public int deviceSerialNo { get; set; } } private class ApiResult { public bool success { get; set; } public string? msg { get; set; } } }