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.Caches; 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; using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.Service; using WIDESEAWCS_Tasks.SocketServer; namespace WIDESEAWCS_Tasks { [DisallowConcurrentExecution] public class RobotJob : IJob { private const int MaxTaskTotalNum = 48; private readonly TcpSocketServer _TcpSocket; //private static readonly ConcurrentDictionary _socketStates = new(); private static int _eventSubscribedFlag; private readonly ITaskService _taskService; private readonly IRobotTaskService _robotTaskService; private readonly ICacheService _cache; private static IRobotTaskService _latestRobotTaskService = null!; private static ITaskService _latestTaskService = null!; public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService, ICacheService cache) { _TcpSocket = TcpSocket; _robotTaskService = RobottaskService; _taskService = TaskService; _cache = cache; _latestRobotTaskService = RobottaskService; _latestTaskService = TaskService; } public async Task Execute(IJobExecutionContext context) { bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value); RobotCraneDevice robotCrane = (RobotCraneDevice?)value ?? new RobotCraneDevice(); if (!flag || robotCrane.IsNullOrEmpty()) { return; } string ipAddress = robotCrane.IPAddress; // 获取或创建状态 RobotSocketState state = _cache.GetOrAdd(ipAddress, _ => new RobotSocketState { IPAddress = ipAddress, RobotCrane = robotCrane }); // 更新设备信息 state.RobotCrane = robotCrane; try { // 检查是否有该客户端连接 var clientIds = _TcpSocket.GetClientIds(); if (!clientIds.Contains(ipAddress)) { return; } // 订阅一次 message 事件(全局一次) if (Interlocked.CompareExchange(ref _eventSubscribedFlag, 1, 0) == 0) { _TcpSocket.MessageReceived += _TcpSocket_MessageReceived; _TcpSocket.RobotReceived += _TcpSocket_RobotReceived; } if (!state.IsEventSubscribed) { 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 <= MaxTaskTotalNum) { // 处理正在执行的任务 if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") { await Task.Delay(1000); 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 读取线体电池条码,发送取电池指令 // 随机生成两天托盘条码存放到两个变量里面 // 定义前缀(例如: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); } } } } } } } catch (Exception) { } finally { // 可选:在这里处理任何需要在任务完成后执行的清理工作 // 更新缓存中的状态 _cache.AddOrUpdate(ipAddress, state); } } //临时测试用 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; } /// /// 事件:客户端断开连接时触发 /// /// /// private Task _TcpSocket_RobotReceived(string clientId) { _cache.TryRemove(clientId, out _); return Task.FromResult(null); } /// /// 事件:收到消息时触发 /// /// /// /// /// /// private async Task _TcpSocket_MessageReceived(string message, bool isJson, TcpClient client, RobotSocketState state) { string messageLower = message.ToLowerInvariant(); if (await IsSimpleCommandAsync(messageLower, state)) { await _TcpSocket.SendMessageAsync(client, message); return null; } if (IsPrefixCommand(messageLower)) { try { var parts = message.Split(','); if (parts.Length >= 1 && state.CurrentTask != null) { var cmd = parts[0].ToLowerInvariant(); 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")) { 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")) { bool putSuccess = true; if (state.IsGroupPallet) { 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 (Exception ex) { Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}"); } await _TcpSocket.SendMessageAsync(client, message); return null; } return null; } /// /// 机械手简单命令处理 /// /// /// /// private static async Task IsSimpleCommandAsync(string message, RobotSocketState state) { switch (message) { case "homing": state.OperStatus = "Homing"; return true; case "homed": state.OperStatus = "Homed"; return true; case "picking": state.CurrentAction = "Picking"; return true; case "puting": state.CurrentAction = "Putting"; return true; case "allpickfinished": // 取货完成 state.CurrentAction = "AllPickFinished"; if (state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode() || state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()) { await HandleInboundTaskAsync(state, useSourceAddress: true); } return true; case "allputfinished": // 放货完成 state.CurrentAction = "AllPutFinished"; if (state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()) { await HandleInboundTaskAsync(state, useSourceAddress: false); } return true; case "running": state.OperStatus = "Running"; return true; case "pausing": state.OperStatus = "Pausing"; return true; case "warming": state.OperStatus = "Warming"; return true; case "emstoping": state.OperStatus = "Emstoping"; return true; case "runmode,1": state.RobotRunMode = 1; return true; case "runmode,2": state.RobotRunMode = 2; return true; case "controlmode,1": state.RobotControlMode = 1; return true; case "controlmode,2": state.RobotControlMode = 2; return true; case "armobject,1": state.RobotArmObject = 1; return true; case "armobject,0": state.RobotArmObject = 0; return true; default: return false; } } 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(result.Data.ToString() ?? string.Empty) ?? new WMSTaskDTO(); var content = _latestTaskService.ReceiveWMSTask(new List { 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); } /// /// 机械手前缀命令处理 /// /// /// private static bool IsPrefixCommand(string message) { 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 _robotTaskService.QueryRobotCraneTask(robotCrane.DeviceCode); } } public class RobotSocketState { public string IPAddress { get; set; } = string.Empty; /// /// 是否已订阅消息事件 /// public bool IsEventSubscribed { get; set; } /// /// 机械手运行模式 /// public int? RobotRunMode { get; set; } /// /// 机械手控制模式 /// public int? RobotControlMode { get; set; } /// /// 机械手是否抓取物料,0-无物料,1-有物料 /// public int? RobotArmObject { get; set; } /// /// 机械手设备信息 /// public RobotCraneDevice? RobotCrane { get; set; } /// /// 当前动作 /// public string? CurrentAction { get; set; } /// /// 当前状态 /// public string? OperStatus { get; set; } /// /// 取货完成位置 /// public int[]? LastPickPositions { get; set; } /// /// 放货完成位置 /// public int[]? LastPutPositions { get; set; } /// /// 抓取位置条码 /// public List CellBarcode { get; set; } = new(); /// /// 当前抓取任务 /// public Dt_RobotTask? CurrentTask { get; set; } /// /// 是否需要拆盘 /// public bool IsSplitPallet { get; set; } /// /// 是否需要组盘 /// public bool IsGroupPallet { get; set; } /// /// 任务总数 /// public int RobotTaskTotalNum { get; set; } } }