You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
10 KiB
C#

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<string,FreeHoistResponse> hoistDic = new Dictionary<string,FreeHoistResponse>();
public string PageTitle => "手动执行任务";
[ObservableProperty]
private ObservableCollection<LiveTaskQueue> _tasks = new();
[ObservableProperty]
private LiveTaskQueue? _selectedTask;
[ObservableProperty]
private ObservableCollection<LiveTaskDetail> _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<LiveTaskQueue> list = _taskQueueService.getLiveTaskQueues(x=>x.executionMode == 1 && x.isFlag == 1);
Tasks = new ObservableCollection<LiveTaskQueue>(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<LiveTaskDetail>(detailList);
//StatusText = $"任务 {taskCode},共 {detailList.Count} 条明细";
}
/// <summary>
/// 任务执行:调用提升机调度中心 API 获取空闲提升机后下发任务
/// </summary>
[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<FreeHoistResponse>(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<ApiResult>();
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;
}
}
/// <summary>
/// 物料到位确认:调用提升机调度中心 receive-pallet 接口
/// </summary>
[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<ApiResult>();
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; }
}
}