From 0264cea34ba024487cfa3128ed31035a0049ab3d Mon Sep 17 00:00:00 2001 From: WenJY Date: Sat, 20 Jun 2026 21:30:52 +0800 Subject: [PATCH] =?UTF-8?q?add=20-=20=E6=B7=BB=E5=8A=A0=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E6=9C=BA=E6=89=8B=E5=8A=A8=E4=BB=BB=E5=8A=A1=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModels/Task/ManualTaskViewModel.cs | 305 ++++++++++++++++++ Sln.Wcs.UI/Views/Task/ManualTaskView.axaml | 163 ++++++++++ Sln.Wcs.UI/Views/Task/ManualTaskView.axaml.cs | 12 + 3 files changed, 480 insertions(+) create mode 100644 Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs create mode 100644 Sln.Wcs.UI/Views/Task/ManualTaskView.axaml create mode 100644 Sln.Wcs.UI/Views/Task/ManualTaskView.axaml.cs diff --git a/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs b/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs new file mode 100644 index 0000000..d918995 --- /dev/null +++ b/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs @@ -0,0 +1,305 @@ +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; } + } +} diff --git a/Sln.Wcs.UI/Views/Task/ManualTaskView.axaml b/Sln.Wcs.UI/Views/Task/ManualTaskView.axaml new file mode 100644 index 0000000..2938a8b --- /dev/null +++ b/Sln.Wcs.UI/Views/Task/ManualTaskView.axaml @@ -0,0 +1,163 @@ + + + + + + + + + + + + +