| | |
| | | using HslCommunication; |
| | | using Newtonsoft.Json; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; |
| | | using Quartz; |
| | | using System.Collections.Concurrent; |
| | | using System.Net; |
| | | using System.Net.Sockets; |
| | | using System.Text.Json; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common; |
| | | 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 |
| | |
| | | private const int MaxTaskTotalNum = 48; |
| | | |
| | | private readonly TcpSocketServer _TcpSocket; |
| | | private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new(); |
| | | |
| | | //private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new(); |
| | | private static int _eventSubscribedFlag; |
| | | |
| | | private readonly ITaskService _taskService; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | private readonly ICacheService _cache; |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | |
| | | private static IRobotTaskService _latestRobotTaskService = null!; |
| | | private static ITaskService _latestTaskService = null!; |
| | | |
| | | public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService) |
| | | public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService, ICacheService cache, HttpClientHelper httpClientHelper) |
| | | { |
| | | _TcpSocket = TcpSocket; |
| | | _robotTaskService = RobottaskService; |
| | | _taskService = TaskService; |
| | | _cache = cache; |
| | | _httpClientHelper = httpClientHelper; |
| | | |
| | | _latestRobotTaskService = RobottaskService; |
| | | _latestTaskService = TaskService; |
| | |
| | | string ipAddress = robotCrane.IPAddress; |
| | | |
| | | // 获取或创建状态 |
| | | RobotSocketState state = _socketStates.GetOrAdd(ipAddress, _ => new RobotSocketState |
| | | RobotSocketState state = _cache.GetOrAdd($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}", _ => new RobotSocketState |
| | | { |
| | | IPAddress = ipAddress, |
| | | RobotCrane = robotCrane |
| | |
| | | // 更新设备信息 |
| | | state.RobotCrane = robotCrane; |
| | | |
| | | // 检查是否有该客户端连接 |
| | | var clientIds = _TcpSocket.GetClientIds(); |
| | | if (!clientIds.Contains(ipAddress)) |
| | | try |
| | | { |
| | | 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)) |
| | | // 检查是否有该客户端连接 |
| | | var clientIds = _TcpSocket.GetClientIds(); |
| | | if (!clientIds.Contains(ipAddress)) |
| | | { |
| | | _ = _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; |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 获取任务并缓存到状态中 |
| | | 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) |
| | | // 订阅一次 message 事件(全局一次) |
| | | if (Interlocked.CompareExchange(ref _eventSubscribedFlag, 1, 0) == 0) |
| | | { |
| | | // 处理正在执行的任务 |
| | | if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") |
| | | _TcpSocket.MessageReceived += _TcpSocket_MessageReceived; |
| | | _TcpSocket.RobotReceived += _TcpSocket_RobotReceived; |
| | | } |
| | | |
| | | if (!state.IsEventSubscribed) |
| | | { |
| | | if (_TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client)) |
| | | { |
| | | await Task.Delay(1000); |
| | | if (state.CurrentAction == "PickFinished" && state.RobotArmObject == 1 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | _ = _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; |
| | | |
| | | // 更新缓存中的状态 |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}", state); |
| | | } |
| | | } |
| | | |
| | | // 获取任务并缓存到状态中 |
| | | Dt_RobotTask? task = GetTask(robotCrane); |
| | | if (task != null) |
| | | { |
| | | if (task.RobotTaskTotalNum <= MaxTaskTotalNum) |
| | | { |
| | | // 处理正在执行的任务 |
| | | if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") |
| | | { |
| | | string taskString = $"Putbattery,{task.RobotTargetAddress}"; |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | if (state.CurrentAction == "PickFinished" && state.RobotArmObject == 1 && task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.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.RobotPutFinish.GetHashCode()) |
| | | { |
| | | 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()) |
| | | else if (state.OperStatus == "Homed" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | | // 发送任务指令 |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | // 随机生成两天托盘条码存放到两个变量里面 |
| | | // 定义前缀(例如:TRAY代表托盘) |
| | | // 组盘读取线体条码 |
| | | if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode()) |
| | | { |
| | | // TODO 处理成功发送任务指令后的逻辑 |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | result = await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | string prefix = "TRAY"; |
| | | |
| | | // 生成两个托盘条码 |
| | | string trayBarcode1 = GenerateTrayBarcode(state, prefix); |
| | | string trayBarcode2 = GenerateTrayBarcode(state, prefix); |
| | | if (!trayBarcode1.IsNullOrEmpty() && !trayBarcode2.IsNullOrEmpty()) |
| | | { |
| | | |
| | | await SendSocketRobotPickAsync(task, state); |
| | | } |
| | | } |
| | | else // 换盘直接发送取货地址 |
| | | { |
| | | await SendSocketRobotPickAsync(task, state); |
| | | } |
| | | } |
| | | |
| | | if (state.CurrentTask.IsNullOrEmpty() && state.ToJson() != task.ToJson()) |
| | | { |
| | | state.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); |
| | | state.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | // 更新缓存中的状态 |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}", state); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception) |
| | | { |
| | | } |
| | | finally |
| | | { |
| | | // 可选:在这里处理任何需要在任务完成后执行的清理工作 |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // 组合:前缀 + 日期 + 时间 + 随机数 |
| | | var barCode = prefix + datePart + timePart + randomPart; |
| | | state.CellBarcode.Add(randomPart); |
| | | state.CellBarcode.Add(barCode); |
| | | |
| | | return barCode; |
| | | } |
| | |
| | | /// <returns></returns> |
| | | private Task<string?> _TcpSocket_RobotReceived(string clientId) |
| | | { |
| | | _socketStates.TryRemove(clientId, out _); |
| | | var robotSocketState = _cache.Get<RobotSocketState>($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{clientId}"); |
| | | robotSocketState.IsEventSubscribed = false; |
| | | robotSocketState.CurrentAction = ""; |
| | | robotSocketState.OperStatus = ""; |
| | | robotSocketState.RobotArmObject = 0; |
| | | robotSocketState.RobotControlMode = 0; |
| | | robotSocketState.RobotRunMode = 0; |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{clientId}", robotSocketState); |
| | | return Task.FromResult<string?>(null); |
| | | } |
| | | |
| | |
| | | /// <returns></returns> |
| | | private async Task<string?> _TcpSocket_MessageReceived(string message, bool isJson, TcpClient client, RobotSocketState state) |
| | | { |
| | | if (!(bool)(_cache?.TryGetValue($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}", out state))) |
| | | return null; |
| | | |
| | | string messageLower = message.ToLowerInvariant(); |
| | | |
| | | if (await IsSimpleCommandAsync(messageLower, state)) |
| | | { |
| | | await _TcpSocket.SendMessageAsync(client, message); |
| | | return null; |
| | | } |
| | | |
| | | if (IsPrefixCommand(messageLower)) |
| | | else if (IsPrefixCommand(messageLower)) |
| | | { |
| | | try |
| | | { |
| | |
| | | var stockDTO = BuildStockDTO(state, positions); |
| | | state.LastPickPositions = positions; |
| | | |
| | | var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), stockDTO.ToJsonString(), nameof(ConfigKey.SplitPalletAsync)); |
| | | var result = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.SplitPalletAsync), stockDTO.ToJson()); |
| | | |
| | | if (result.Status) |
| | | if (result.Data.Status && result.IsSuccess) |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | |
| | | state.LastPickPositions = positions; |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode(); |
| | | await _latestRobotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | |
| | | 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; |
| | | |
| | | var result = _httpClientHelper.Post<WebResponseContent>(configKey, stockDTO.ToJson()); |
| | | putSuccess = result.Data.Status && result.IsSuccess; |
| | | } |
| | | |
| | | if (putSuccess) |
| | |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode(); |
| | | await _latestRobotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | |
| | | await _TcpSocket.SendMessageAsync(client, message); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}"); |
| | | } |
| | | |
| | | await _TcpSocket.SendMessageAsync(client, message); |
| | | |
| | | return null; |
| | | } |
| | | |
| | | // 更新缓存中的状态 |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{state.IPAddress}", state); |
| | | |
| | | return null; |
| | | } |
| | |
| | | /// <param name="message"></param> |
| | | /// <param name="state"></param> |
| | | /// <returns></returns> |
| | | private static async Task<bool> IsSimpleCommandAsync(string message, RobotSocketState state) |
| | | private async Task<bool> IsSimpleCommandAsync(string message, RobotSocketState state) |
| | | { |
| | | switch (message) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | private static async Task HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress) |
| | | private async Task HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress) |
| | | { |
| | | var currentTask = state.CurrentTask; |
| | | if (currentTask == null) |
| | |
| | | PalletType = 1, |
| | | TaskType = 4 |
| | | }; |
| | | |
| | | var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), taskDto.ToJsonString(), nameof(ConfigKey.CreateTaskInboundAsync)); |
| | | if (!result.Status) |
| | | var result = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.CreateTaskInboundAsync), taskDto.ToJson()); |
| | | if (!result.Data.Status && result.IsSuccess) |
| | | { |
| | | return; |
| | | } |
| | |
| | | |
| | | private static StockDTO BuildStockDTO(RobotSocketState state, int[] positions) |
| | | { |
| | | string sss = state.ToJson(); |
| | | return new StockDTO |
| | | { |
| | | SourceLineNo = state.CurrentTask?.RobotSourceAddressLineCode, |
| | | SourcePalletNo = state.CurrentTask?.RobotSourceAddressPalletCode, |
| | | TargetPalletNo = state.CurrentTask?.RobotTargetAddressPalletCode, |
| | | TargetLineNo = state.CurrentTask?.RobotTargetAddressLineCode, |
| | | 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, |
| | | Quantity = state.RobotTaskTotalNum > 0 ? state.RobotTaskTotalNum + positions.Length : positions.Length, |
| | | Channel = x, |
| | | CellBarcode = !state.CellBarcode.IsNullOrEmpty() ? state.CellBarcode[idx] : "" |
| | | CellBarcode = state.CellBarcode?.Count > 0 ? state.CellBarcode[x - 1] : "" |
| | | }) |
| | | .ToList() |
| | | }; |
| | |
| | | private Dt_RobotTask? GetTask(RobotCraneDevice robotCrane) |
| | | { |
| | | return _robotTaskService.QueryRobotCraneTask(robotCrane.DeviceCode); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 发送机械手取货命令 |
| | | /// </summary> |
| | | /// <param name="task"></param> |
| | | /// <param name="state"></param> |
| | | /// <returns></returns> |
| | | private async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state) |
| | | { |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | | // 发送任务指令 |
| | | bool result = await _TcpSocket.SendToClientAsync(state.IPAddress, taskString); |
| | | if (result) |
| | | { |
| | | // TODO 处理成功发送任务指令后的逻辑 |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | result = await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | // 更新缓存中的状态 |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{state.IPAddress}", state); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | /// <summary> |
| | | /// 抓取位置条码 |
| | | /// </summary> |
| | | public List<string> CellBarcode { get; set; } = new(); |
| | | public List<string> CellBarcode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 当前抓取任务 |