| | |
| | | using Quartz; |
| | | using HslCommunication; |
| | | using Newtonsoft.Json; |
| | | using Quartz; |
| | | using System.Collections.Concurrent; |
| | | using System.Net.Sockets; |
| | | using System.Text.Json; |
| | | using WIDESEAWCS_Common.HttpEnum; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core; |
| | | using WIDESEAWCS_Core.Helper; |
| | | using WIDESEAWCS_Core.Http; |
| | | using WIDESEAWCS_DTO.Stock; |
| | | using WIDESEAWCS_DTO.TaskInfo; |
| | | using WIDESEAWCS_ITaskInfoRepository; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | |
| | | [DisallowConcurrentExecution] |
| | | public class RobotJob : IJob |
| | | { |
| | | private const int MaxTaskTotalNum = 48; |
| | | |
| | | private readonly TcpSocketServer _TcpSocket; |
| | | private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new(); |
| | | private static int _eventSubscribedFlag; |
| | | private readonly IRobotTaskService _taskService; |
| | | private readonly ITaskExecuteDetailService _taskExecuteDetailService; |
| | | private readonly ITaskRepository _taskRepository; |
| | | private readonly IRouterService _routerService; |
| | | |
| | | public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService taskService) |
| | | private readonly ITaskService _taskService; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | |
| | | private static IRobotTaskService _latestRobotTaskService = null!; |
| | | private static ITaskService _latestTaskService = null!; |
| | | |
| | | public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService) |
| | | { |
| | | _TcpSocket = TcpSocket; |
| | | _taskService = taskService; |
| | | _robotTaskService = RobottaskService; |
| | | _taskService = TaskService; |
| | | |
| | | _latestRobotTaskService = RobottaskService; |
| | | _latestTaskService = TaskService; |
| | | } |
| | | |
| | | public async Task Execute(IJobExecutionContext context) |
| | |
| | | |
| | | if (!state.IsEventSubscribed) |
| | | { |
| | | _TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client); |
| | | Task clientTask = _TcpSocket.HandleClientAsync(client, robotCrane.IPAddress, _TcpSocket._cts.Token, state); |
| | | state.IsEventSubscribed = true; |
| | | if (_TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client)) |
| | | { |
| | | _ = _TcpSocket.HandleClientAsync(client, robotCrane.IPAddress, _TcpSocket._cts.Token, state) |
| | | .ContinueWith(t => |
| | | { |
| | | if (t.IsFaulted) |
| | | Console.WriteLine($"HandleClientAsync error: {t.Exception?.GetBaseException().Message}"); |
| | | }, TaskContinuationOptions.OnlyOnFaulted); |
| | | state.IsEventSubscribed = true; |
| | | } |
| | | } |
| | | |
| | | // 获取任务并缓存到状态中 |
| | | Dt_RobotTask? task = GetTask(robotCrane); |
| | | if (task != null) |
| | | { |
| | | state.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); |
| | | state.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | if (task.RobotTaskTotalNum != 48) |
| | | if (task.RobotTaskTotalNum <= MaxTaskTotalNum) |
| | | { |
| | | // 处理正在执行的任务 |
| | | if (state.RobotRunMode == 1 && state.RobotControlMode == 1) |
| | | if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") |
| | | { |
| | | await Task.Delay(1000); |
| | | if ((state.CurrentAction == "Homed" || state.CurrentAction == "PickFinished" || state.CurrentAction == "PutFinished") && state.OperStatus == "Running") |
| | | if (state.CurrentAction == "PickFinished" && state.RobotArmObject == 1 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | string taskString = $"Putbattery,{task.RobotTargetAddress}"; |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else if (state.CurrentAction == "PutFinished" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | else if (state.OperStatus == "Homed" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | // TODO 读取线体电池条码,发送取电池指令 |
| | | if (true) |
| | | { |
| | | // 模拟读取条码 |
| | | state.CellBarcode = new string[] { "CellBarcode1", "CellBarcode2", "CellBarcode3", "CellBarcode4" }; |
| | | // 随机生成两天托盘条码存放到两个变量里面 |
| | | // 定义前缀(例如:TRAY代表托盘) |
| | | string prefix = "TRAY"; |
| | | |
| | | // 生成两个托盘条码 |
| | | string trayBarcode1 = GenerateTrayBarcode(state, prefix); |
| | | string trayBarcode2 = GenerateTrayBarcode(state, prefix); |
| | | if (!trayBarcode1.IsNullOrEmpty() && !trayBarcode2.IsNullOrEmpty()) |
| | | { |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | | // 发送任务指令 |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | { |
| | | // TODO 处理成功发送任务指令后的逻辑 |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | result = await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return; |
| | | //临时测试用 |
| | | private static string GenerateTrayBarcode(RobotSocketState state, string prefix = "") |
| | | { |
| | | // 当前日期 |
| | | string datePart = DateTime.Now.ToString("yyyyMMdd"); |
| | | |
| | | // 时间戳(时分秒) |
| | | string timePart = DateTime.Now.ToString("HHmmss"); |
| | | |
| | | // 随机数 |
| | | string randomPart = Random.Shared.Next(100, 1000).ToString(); |
| | | |
| | | // 组合:前缀 + 日期 + 时间 + 随机数 |
| | | var barCode = prefix + datePart + timePart + randomPart; |
| | | state.CellBarcode.Add(randomPart); |
| | | |
| | | return barCode; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | string messageLower = message.ToLowerInvariant(); |
| | | |
| | | if (IsSimpleCommand(messageLower, state)) |
| | | if (await IsSimpleCommandAsync(messageLower, state)) |
| | | { |
| | | await _TcpSocket.SendMessageAsync(client, message); |
| | | return null; |
| | | } |
| | | |
| | |
| | | try |
| | | { |
| | | var parts = message.Split(','); |
| | | if (parts.Length >= 1) |
| | | if (parts.Length >= 1 && state.CurrentTask != null) |
| | | { |
| | | var cmd = parts[0].ToLowerInvariant(); |
| | | int[] positions = new int[4]; |
| | | for (int i = 1; i <= 4 && i < parts.Length; i++) |
| | | { |
| | | int.TryParse(parts[i], out positions[i - 1]); |
| | | } |
| | | int[] positions = parts.Skip(1) |
| | | .Select(p => int.TryParse(p, out int value) ? value : (int?)null) |
| | | .Where(v => v.HasValue && v.Value != 0) |
| | | .Select(v => v!.Value) |
| | | .ToArray(); |
| | | |
| | | var task = await _latestRobotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskId == state.CurrentTask.RobotTaskId); |
| | | |
| | | if (cmd.StartsWith("pickfinished")) |
| | | { |
| | | state.LastPickPositions = positions; |
| | | state.CurrentAction = "PickFinished"; |
| | | if (state.IsSplitPallet) |
| | | { |
| | | var stockDTO = BuildStockDTO(state, positions); |
| | | state.LastPickPositions = positions; |
| | | |
| | | var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), stockDTO.ToJsonString(), nameof(ConfigKey.SplitPalletAsync)); |
| | | |
| | | if (result.Status) |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode(); |
| | | await _latestRobotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | else if (cmd.StartsWith("putfinished")) |
| | | { |
| | | state.LastPutPositions = positions; |
| | | // 发送数据给WMS组盘/换盘 |
| | | StockDTO stockDTO = new StockDTO |
| | | bool putSuccess = true; |
| | | if (state.IsGroupPallet) |
| | | { |
| | | SourceLineNo = state.CurrentTask?.RobotSourceAddressLineCode, |
| | | SourcePalletNo = state.CurrentTask?.RobotSourceAddressPalletCode, |
| | | TargetPalletNo = state.CurrentTask?.RobotTargetAddressPalletCode, |
| | | TargetLineNo = state.CurrentTask?.RobotTargetAddressLineCode, |
| | | Details = positions |
| | | .Where(x => x > 0) |
| | | .OrderBy(x => x) |
| | | .Select((x, idx) => new StockDetailDTO |
| | | { |
| | | Quantity = state.CurrentTask?.RobotTaskTotalNum ?? 1, |
| | | Channel = x > 0 ? x : throw new ArgumentOutOfRangeException(nameof(x), "Channel must be positive"), |
| | | CellBarcode = state.CellBarcode[idx] |
| | | }) |
| | | .ToList() |
| | | }; |
| | | state.CurrentAction = "PutFinished"; |
| | | state.LastPutPositions = positions; |
| | | var stockDTO = BuildStockDTO(state, positions); |
| | | var configKey = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode() |
| | | ? nameof(ConfigKey.ChangePalletAsync) : nameof(ConfigKey.GroupPalletAsync); |
| | | var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), stockDTO.ToJsonString(), configKey); |
| | | putSuccess = result.Status; |
| | | } |
| | | |
| | | if (putSuccess) |
| | | { |
| | | state.CurrentAction = "PutFinished"; |
| | | state.RobotTaskTotalNum += positions.Length; |
| | | task.RobotTaskTotalNum += positions.Length; |
| | | } |
| | | |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode(); |
| | | await _latestRobotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | } |
| | | catch { } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}"); |
| | | } |
| | | |
| | | await _TcpSocket.SendMessageAsync(client, message); |
| | | |
| | | return null; |
| | | } |
| | |
| | | /// <param name="message"></param> |
| | | /// <param name="state"></param> |
| | | /// <returns></returns> |
| | | private bool IsSimpleCommand(string message, RobotSocketState state) |
| | | private static async Task<bool> IsSimpleCommandAsync(string message, RobotSocketState state) |
| | | { |
| | | switch (message) |
| | | { |
| | | case "homing": |
| | | state.CurrentAction = "Homing"; |
| | | state.OperStatus = "Homing"; |
| | | return true; |
| | | |
| | | case "homed": |
| | | state.CurrentAction = "Homed"; |
| | | state.OperStatus = "Homed"; |
| | | return true; |
| | | |
| | | case "picking": |
| | |
| | | state.CurrentAction = "Putting"; |
| | | return true; |
| | | |
| | | case "allpickfinished": |
| | | case "allpickfinished": // 取货完成 |
| | | state.CurrentAction = "AllPickFinished"; |
| | | if(state.CurrentTask?.RobotTaskType == 2|| state.CurrentTask?.RobotTaskType == 3) |
| | | if (state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode() || state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()) |
| | | { |
| | | // TODO 机械手取货完成,判断是否换盘、拆盘任务,创建空托盘回库任务 |
| | | await HandleInboundTaskAsync(state, useSourceAddress: true); |
| | | } |
| | | return true; |
| | | |
| | | case "allputfinished": |
| | | case "allputfinished": // 放货完成 |
| | | state.CurrentAction = "AllPutFinished"; |
| | | if (state.CurrentTask?.RobotTaskType == 1 ) |
| | | if (state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()) |
| | | { |
| | | // TODO 机械手取货完成,判断是否组盘任务,创建组盘入库任务 |
| | | await HandleInboundTaskAsync(state, useSourceAddress: false); |
| | | } |
| | | return true; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private static async Task HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress) |
| | | { |
| | | var currentTask = state.CurrentTask; |
| | | if (currentTask == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | string roadway = currentTask.RobotRoadway == "1" ? "GWSC001" : currentTask.RobotRoadway == "2" ? "HCSC001" : "SC001"; |
| | | int warehouseId = currentTask.RobotRoadway == "1" ? 1 : currentTask.RobotRoadway == "2" ? 2 : 3; |
| | | |
| | | CreateTaskDto taskDto = new CreateTaskDto |
| | | { |
| | | PalletCode = currentTask.RobotTargetAddressPalletCode ?? string.Empty, |
| | | SourceAddress = currentTask.RobotSourceAddress ?? string.Empty, |
| | | TargetAddress = currentTask.RobotTargetAddress ?? string.Empty, |
| | | Roadway = roadway, |
| | | WarehouseId = warehouseId, |
| | | PalletType = 1, |
| | | TaskType = 4 |
| | | }; |
| | | |
| | | var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), taskDto.ToJsonString(), nameof(ConfigKey.CreateTaskInboundAsync)); |
| | | if (!result.Status) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | WMSTaskDTO taskDTO = JsonConvert.DeserializeObject<WMSTaskDTO>(result.Data.ToString() ?? string.Empty) ?? new WMSTaskDTO(); |
| | | var content = _latestTaskService.ReceiveWMSTask(new List<WMSTaskDTO> { taskDTO }); |
| | | if (!content.Status) return; |
| | | |
| | | var taskInfo = _latestTaskService.QueryByTaskNum(taskDTO.TaskNum); |
| | | if (taskInfo == null) return; |
| | | |
| | | string targetAddress = useSourceAddress ? taskDTO.SourceAddress : taskDTO.TargetAddress; |
| | | |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == targetAddress)); |
| | | device?.Communicator.Write(nameof(ConveyorLineDBNameNew.Target), taskInfo.NextAddress); |
| | | device?.Communicator.Write(nameof(ConveyorLineDBNameNew.TaskNo), taskDTO.TaskNum); |
| | | device?.Communicator.Write(nameof(ConveyorLineDBNameNew.WCS_STB), 1); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 机械手前缀命令处理 |
| | | /// </summary> |
| | |
| | | return message.StartsWith("pickfinished") || message.StartsWith("putfinished"); |
| | | } |
| | | |
| | | private static StockDTO BuildStockDTO(RobotSocketState state, int[] positions) |
| | | { |
| | | return new StockDTO |
| | | { |
| | | SourceLineNo = state.CurrentTask?.RobotSourceAddressLineCode, |
| | | SourcePalletNo = state.CurrentTask?.RobotSourceAddressPalletCode, |
| | | TargetPalletNo = state.CurrentTask?.RobotTargetAddressPalletCode, |
| | | TargetLineNo = state.CurrentTask?.RobotTargetAddressLineCode, |
| | | Details = positions |
| | | .Where(x => x > 0) |
| | | .OrderBy(x => x) |
| | | .Select((x, idx) => new StockDetailDTO |
| | | { |
| | | Quantity = state.CurrentTask?.RobotTaskTotalNum ?? 1, |
| | | Channel = x, |
| | | CellBarcode = !state.CellBarcode.IsNullOrEmpty() ? state.CellBarcode[idx] : "" |
| | | }) |
| | | .ToList() |
| | | }; |
| | | } |
| | | |
| | | private Dt_RobotTask? GetTask(RobotCraneDevice robotCrane) |
| | | { |
| | | return _taskService.QueryRobotCraneTask(robotCrane.DeviceCode); |
| | | return _robotTaskService.QueryRobotCraneTask(robotCrane.DeviceCode); |
| | | } |
| | | } |
| | | |
| | |
| | | public int? RobotControlMode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 机械手抓取对象 |
| | | /// 机械手是否抓取物料,0-无物料,1-有物料 |
| | | /// </summary> |
| | | public int? RobotArmObject { get; set; } |
| | | |
| | |
| | | /// <summary> |
| | | /// 抓取位置条码 |
| | | /// </summary> |
| | | public string[] CellBarcode { get; set; } |
| | | public List<string> CellBarcode { get; set; } = new(); |
| | | |
| | | /// <summary> |
| | | /// 当前抓取任务 |
| | | /// </summary> |
| | | public Dt_RobotTask? CurrentTask { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 是否需要拆盘 |
| | | /// </summary> |
| | | public bool IsSplitPallet { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 是否需要组盘 |
| | | /// </summary> |
| | | public bool IsGroupPallet { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 任务总数 |
| | | /// </summary> |
| | | public int RobotTaskTotalNum { get; set; } |
| | | } |
| | | } |