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; public string PageTitle => "手动执行任务"; [ObservableProperty] private ObservableCollection _tasks = new(); [ObservableProperty] private LiveTaskQueue? _selectedTask; [ObservableProperty] private ObservableCollection _details = new(); [ObservableProperty] private LiveTaskDetail? _selectedDetail; [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 ObservableCollection HostOptions { get; } = new() { new() { Name = "15栋入库提升机", Code = "1#Host" }, new() { Name = "15栋出库提升机", Code = "2#Host" }, new() { Name = "14栋提升机", Code = "3#Host" }, new() { Name = "13栋提升机", Code = "4#Host" } }; [ObservableProperty] private HostOption _selectedHostOption; public ManualTaskViewModel( ILiveTaskQueueService taskQueueService, ILiveTaskDetailService taskDetailService, SerilogHelper logger) { _taskQueueService = taskQueueService; _taskDetailService = taskDetailService; _http = new HttpClient { Timeout = System.TimeSpan.FromSeconds(5) }; _logger = logger; _selectedHostOption = HostOptions[0]; } 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); for (int i = 0; i < list.Count; i++) list[i].RowIndex = i + 1; Tasks = new ObservableCollection(list); StatusText = list.Count > 0 ? $"查询到 {list.Count} 条待执行手动任务" : "暂无待执行的手动任务"; } partial void OnSelectedTaskChanged(LiveTaskQueue? value) { TaskExecuted = false; SelectedDetail = null; HoistInfo = "--"; DockingPoint = "--"; if (value is not null) LoadDetails(value.taskCode); } partial void OnSelectedDetailChanged(LiveTaskDetail? value) { if (value is not null && !string.IsNullOrWhiteSpace(value.execDevice)) { var parts = value.execDevice.Split('_'); if (parts.Length == 2) { HoistInfo = parts[0]; DockingPoint = parts[1]; return; } } HoistInfo = "--"; DockingPoint = "--"; } private void LoadDetails(string taskCode) { var detailList = _taskDetailService.Query(x => x.taskCode == taskCode) .OrderBy(d => d.objId) .ToList(); for (int i = 0; i < detailList.Count; i++) detailList[i].RowIndex = i + 1; 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; } if (SelectedDetail is null) { StatusText = "请先在明细列表中选择一条提升机明细"; return; } var hoistDetail = SelectedDetail; if (hoistDetail.deviceType != 2) { StatusText = "所选明细不是提升机步骤,请选择设备类型为提升机的明细"; return; } IsExecuting = true; StatusText = "正在获取空闲提升机..."; try { // 1. 获取空闲提升机 string hostCode = SelectedHostOption?.Code ?? "1#Host"; if (hoistDetail.taskType == 2 && hoistDetail.taskCategory == 2 && hoistDetail.startPoint.Contains("15#")) { hostCode = "2#Host"; } else { if (hoistDetail.startPoint.Contains("13#")) { hostCode = "4#Host"; }else if (hoistDetail.startPoint.Contains("14#")) { hostCode = "3#Host"; }else if (hoistDetail.startPoint.Contains("15#")) { hostCode = "1#Host"; } } var freeUrl = $"{HoistBaseUrl}/api/hoist/free?hostCode={Uri.EscapeDataString(hostCode)}"; FreeHoistResponse? freeRes; while (true) { freeRes = await _http.GetFromJsonAsync(freeUrl); if (freeRes is { found: true }) break; StatusText = "暂无空闲提升机,等待中..."; HoistInfo = "忙碌"; await System.Threading.Tasks.Task.Delay(1000); } StatusText = $"已获取空闲提升机 {freeRes.deviceName ?? freeRes.deviceCode},正在下发任务..."; // 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; hoistDetail.execDevice = $"{freeRes.hostCode}_{freeRes.deviceSerialNo}"; _taskDetailService.Update(hoistDetail); HoistInfo = freeRes.hostCode.ToString(); // HoistInfo = freeRes.hostCode; DockingPoint = freeRes.deviceSerialNo.ToString(); }); TaskExecuted = true; _logger.Info($"手动任务 {SelectedTask.taskCode} 已下发到提升机 {freeRes.deviceCode}"); StatusText = $"任务 {SelectedTask.taskCode} 已下发到提升机 {freeRes.deviceName ?? freeRes.deviceCode}"; LoadDetails(SelectedTask.taskCode); RefreshTaskList(); } catch (HttpRequestException ex) { TaskExecuted = false; HoistInfo = "--"; StatusText = $"与提升机调度中心通信失败: {ex.Message}"; _logger.Error($"HoistServer 通信失败: {ex.Message}"); } catch (Exception ex) { TaskExecuted = false; 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; } if (SelectedDetail is null) { StatusText = "请先在明细列表中选择一条明细"; return; } var hoistDetail = SelectedDetail; if (hoistDetail.deviceType != 2) { StatusText = "所选明细不是提升机步骤,请选择设备类型为提升机的明细"; return; } if (SelectedTask.taskStatus != 2) { StatusText = "当前任务未在执行中,请先执行任务"; return; } if (string.IsNullOrWhiteSpace(hoistDetail.execDevice)) { StatusText = "当前任务未分配执行设备,请先执行任务"; return; } IsMaterialReady = true; StatusText = "正在确认物料到位..."; try { var execParts = hoistDetail.execDevice!.Split('_'); var execHostCode = execParts[0]; var execSerialNo = int.Parse(execParts[1]); var body = new { hostCode = execHostCode, serialNo = execSerialNo, 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} 物料已到位,提升机已启动,等待完成..."; // 轮询等待提升机任务完成 var waitUrl = $"{HoistBaseUrl}/api/hoist/wait-complete?hostCode={Uri.EscapeDataString(execHostCode)}&deviceSerialNo={execSerialNo}"; while (true) { var waitRes = await _http.GetFromJsonAsync(waitUrl); if (waitRes is { isComplete: true }) break; await System.Threading.Tasks.Task.Delay(2000); } await System.Threading.Tasks.Task.Run(() => { hoistDetail.taskStatus = 3; _taskDetailService.Update(hoistDetail); }); StatusText = $"任务 {SelectedTask.taskCode} 提升机已完成"; _logger.Info($"手动任务 {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 WaitCompleteResponse { public bool isComplete { get; set; } } private class ApiResult { public bool success { get; set; } public string? msg { get; set; } } public class HostOption { public string Name { get; set; } = string.Empty; public string Code { get; set; } = string.Empty; } }