using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Routing; using Quartz; using WIDESEAWCS_Common.TaskEnum; using WIDESEAWCS_Communicator; using WIDESEAWCS_Core.Enums; using WIDESEAWCS_Core.Helper; using WIDESEAWCS_ITaskInfoRepository; using WIDESEAWCS_ITaskInfoService; using WIDESEAWCS_Model.Models; using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.DeviceBase; using WIDESEAWCS_QuartzJob.Models; using WIDESEAWCS_QuartzJob.Service; using WIDESEAWCS_QuartzJob.StackerCrane.Enum; using WIDESEAWCS_TaskInfoService; using WIDESEAWCS_Tasks.StackerCraneJob; namespace WIDESEAWCS_Tasks { [DisallowConcurrentExecution] public class CommonStackerCraneJob : IJob { private readonly ITaskService _taskService; private readonly ITaskExecuteDetailService _taskExecuteDetailService; private readonly ITaskRepository _taskRepository; private readonly IRouterService _routerService; private readonly StackerCraneCommandConfig _config; public CommonStackerCraneJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, ITaskRepository taskRepository, IRouterService routerService) { _taskService = taskService; _taskExecuteDetailService = taskExecuteDetailService; _taskRepository = taskRepository; _routerService = routerService; _config = LoadConfig(); } /// /// 加载配置(优先级:配置文件 > 默认配置) /// private static StackerCraneCommandConfig LoadConfig() { try { string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "StackerCraneJob", "stackercrane-command-config.json"); if (File.Exists(configPath)) { string json = File.ReadAllText(configPath); return System.Text.Json.JsonSerializer.Deserialize(json) ?? new StackerCraneCommandConfig(); } } catch (Exception ex) { Console.WriteLine($"配置加载失败: {ex.Message},使用默认配置"); } return new StackerCraneCommandConfig(); } public Task Execute(IJobExecutionContext context) { try { Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " CommonStackerCraneJob Start"); bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value); if (!flag || value is not IStackerCrane commonStackerCrane) { return Task.CompletedTask; } if (!commonStackerCrane.IsEventSubscribed) { commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler; } if (commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs)) { commonStackerCrane.CheckStackerCraneTaskCompleted(); Dt_Task? task = GetTask(commonStackerCrane); if (task != null) { object? stackerCraneTaskCommand = ConvertToStackerCraneTaskCommand(task); if (stackerCraneTaskCommand != null) { bool sendFlag = SendStackerCraneCommand(commonStackerCrane, stackerCraneTaskCommand); if (sendFlag) { commonStackerCrane.LastTaskType = task.TaskType; _taskService.UpdateTaskStatusToNext(task.TaskNum); } } } } } catch (Exception ex) { Console.WriteLine($"CommonStackerCraneJob Error: {ex.Message}"); } return Task.CompletedTask; } /// /// 任务完成事件订阅的方法 /// /// /// private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, WIDESEAWCS_QuartzJob.StackerCrane.StackerCraneTaskCompletedEventArgs e) { CommonStackerCrane? commonStackerCrane = sender as CommonStackerCrane; if (commonStackerCrane != null) { if (commonStackerCrane.GetValue(StackerCraneDBName.WorkType) != 5) { Console.Out.WriteLine("TaskCompleted" + e.TaskNum); _taskService.StackCraneTaskCompleted(e.TaskNum); commonStackerCrane.SetValue(StackerCraneDBName.WorkType, 5); } } } /// /// 获取任务 /// /// 堆垛机对象 /// private Dt_Task? GetTask(IStackerCrane commonStackerCrane) { Dt_Task? task = null; if (commonStackerCrane.LastTaskType == null) { task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode); } else { if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) { task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); } else { task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); } } if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) { //if (IsOutTaskStationAvailable(task)) //{ return task; //} List otherOutStationCodes = _routerService.QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType) .Select(x => x.ChildPosi).ToList(); List tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes); foreach (var alternativeTask in tasks) { if (IsOutTaskStationAvailable(alternativeTask)) { return alternativeTask; } } task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); } return task; } /// /// 出库任务判断出库站台是否可用 /// /// 任务实体 /// 如果站台可用返回true,否则返回false private bool IsOutTaskStationAvailable([NotNull] Dt_Task task) { Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType); if (router == null) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.NextAddress}】信息,无法校验站台"); return false; } IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode); if (device == null) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到出库站台【{router.ChildPosiDeviceCode}】对应的通讯对象,无法判断出库站台是否被占用"); return false; } CommonConveyorLine conveyorLine = (CommonConveyorLine)device; return conveyorLine.IsOccupied(router.ChildPosi); } /// /// 任务实体转换成命令Model /// public object? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task) { // 根据配置判断命令类型 string commandType = GetCommandType(task.Roadway); // 创建并构建命令 return commandType switch { "Formation" => BuildCommand(task, CreateFormationCommand(task)), _ => BuildCommand(task, CreateStandardCommand(task)) }; } private static bool SendStackerCraneCommand(IStackerCrane commonStackerCrane, object command) { return command switch { FormationStackerCraneTaskCommand formationCommand => commonStackerCrane.SendCommand(formationCommand), StackerCraneTaskCommand standardCommand => commonStackerCrane.SendCommand(standardCommand), _ => false }; } /// /// 根据 Roadway 获取命令类型 /// private string GetCommandType(string roadway) { foreach (var mapping in _config.RoadwayCommandMapping) { if (roadway.Contains(mapping.Key)) { return mapping.Value; } } return _config.DefaultCommandType; } /// /// 创建标准堆垛机命令 /// private static StackerCraneTaskCommand CreateStandardCommand(Dt_Task task) { return new StackerCraneTaskCommand { //Barcode = task.PalletCode, TaskNum = task.TaskNum, WorkType = 1, WorkAction = 1 }; } /// /// 创建分容堆垛机命令 /// private static FormationStackerCraneTaskCommand CreateFormationCommand(Dt_Task task) { return new FormationStackerCraneTaskCommand { Barcode = task.PalletCode, TaskNum = task.TaskNum, WorkType = 1, WorkAction = 1, FireAlarm = 0, HeartBeat = 0, FieldName = string.Empty }; } /// /// 通用命令构建方法 /// private T? BuildCommand(Dt_Task task, T command) where T : class { TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup(); return taskTypeGroup switch { TaskTypeGroup.InboundGroup => BuildInboundCommand(task, command), TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, command), TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, command), _ => command }; } /// /// 通用入库命令构建 /// private T? BuildInboundCommand(Dt_Task task, T command) where T : class { Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType); if (router == null) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.CurrentAddress}】信息,无法获取对应的堆垛机取货站台信息"); return null; } SetCommandProperty(command, "StartRow", Convert.ToInt16(router.SrmRow)); SetCommandProperty(command, "StartColumn", Convert.ToInt16(router.SrmColumn)); SetCommandProperty(command, "StartLayer", Convert.ToInt16(router.SrmLayer)); if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"入库任务终点错误,终点:【{task.NextAddress}】"); return null; } SetCommandProperty(command, "EndRow", endRow); SetCommandProperty(command, "EndColumn", endColumn); SetCommandProperty(command, "EndLayer", endLayer); return command; } /// /// 通用出库命令构建 /// private T? BuildOutboundCommand(Dt_Task task, T command) where T : class { Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType); if (router == null) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.TargetAddress}】信息,无法获取对应的堆垛机放货站台信息"); return null; } SetCommandProperty(command, "EndRow", Convert.ToInt16(router.SrmRow)); SetCommandProperty(command, "EndColumn", Convert.ToInt16(router.SrmColumn)); SetCommandProperty(command, "EndLayer", Convert.ToInt16(router.SrmLayer)); if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"出库任务起点错误,起点:【{task.CurrentAddress}】"); return null; } SetCommandProperty(command, "StartRow", startRow); SetCommandProperty(command, "StartColumn", startColumn); SetCommandProperty(command, "StartLayer", startLayer); return command; } /// /// 通用移库命令构建 /// private T? BuildRelocationCommand(Dt_Task task, T command) where T : class { if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务终点错误,终点:【{task.NextAddress}】"); return null; } SetCommandProperty(command, "EndRow", endRow); SetCommandProperty(command, "EndColumn", endColumn); SetCommandProperty(command, "EndLayer", endLayer); if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) { _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务起点错误,起点:【{task.CurrentAddress}】"); return null; } SetCommandProperty(command, "StartRow", startRow); SetCommandProperty(command, "StartColumn", startColumn); SetCommandProperty(command, "StartLayer", startLayer); return command; } /// /// 使用反射设置命令属性 /// private static void SetCommandProperty(T command, string propertyName, object value) where T : class { var property = typeof(T).GetProperty(propertyName); property?.SetValue(command, value); } /// /// 解析地址字符串(格式:行-列-层) /// private bool TryParseAddress(string address, out short row, out short column, out short layer) { row = column = layer = 0; string[] parts = address.Split("-"); if (parts.Length != 3) { return false; } return short.TryParse(parts[0], out row) && short.TryParse(parts[1], out column) && short.TryParse(parts[2], out layer); } } }