| | |
| | | using HslCommunication; |
| | | using Newtonsoft.Json; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; |
| | | using Quartz; |
| | | using System.Net; |
| | | using System.Net.Sockets; |
| | | using System.Threading.Tasks; |
| | | using Quartz; |
| | | 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_DTO.Stock; |
| | | using WIDESEAWCS_DTO.TaskInfo; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | using WIDESEAWCS_Tasks.Workflow; |
| | | using WIDESEAWCS_Tasks.SocketServer; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// 机器人任务作业:负责调度与生命周期管理,具体状态机流程交给编排器。 |
| | | /// </summary> |
| | | [DisallowConcurrentExecution] |
| | | public class RobotJob : IJob |
| | | { |
| | | private const int MaxTaskTotalNum = 48; |
| | | |
| | | private readonly TcpSocketServer _TcpSocket; |
| | | private static int _messageSubscribedFlag; |
| | | |
| | | //private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new(); |
| | | private static int _eventSubscribedFlag; |
| | | private readonly RobotClientManager _clientManager; |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly IRobotMessageRouter _messageRouter; |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | private readonly IRobotWorkflowOrchestrator _workflowOrchestrator; |
| | | |
| | | 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, ICacheService cache, HttpClientHelper httpClientHelper) |
| | | public RobotJob( |
| | | TcpSocketServer tcpSocket, |
| | | IRobotTaskService robotTaskService, |
| | | ITaskService taskService, |
| | | ICacheService cache, |
| | | HttpClientHelper httpClientHelper) |
| | | { |
| | | _TcpSocket = TcpSocket; |
| | | _robotTaskService = RobottaskService; |
| | | _taskService = TaskService; |
| | | _cache = cache; |
| | | _httpClientHelper = httpClientHelper; |
| | | _stateManager = new RobotStateManager(cache); |
| | | |
| | | _latestRobotTaskService = RobottaskService; |
| | | _latestTaskService = TaskService; |
| | | // 收口 Socket 访问,后续若替换通信实现只需替换网关层。 |
| | | ISocketClientGateway socketGateway = new SocketClientGateway(tcpSocket); |
| | | |
| | | _taskProcessor = new RobotTaskProcessor(socketGateway, _stateManager, robotTaskService, taskService, httpClientHelper); |
| | | _clientManager = new RobotClientManager(tcpSocket, _stateManager); |
| | | |
| | | var simpleCommandHandler = new RobotSimpleCommandHandler(_taskProcessor); |
| | | var prefixCommandHandler = new RobotPrefixCommandHandler(robotTaskService, _taskProcessor, _stateManager, socketGateway); |
| | | _messageRouter = new RobotMessageHandler(socketGateway, _stateManager, cache, simpleCommandHandler, prefixCommandHandler); |
| | | |
| | | _workflowOrchestrator = new RobotWorkflowOrchestrator(_stateManager, _clientManager, _taskProcessor, robotTaskService); |
| | | |
| | | _clientManager.OnClientDisconnected += OnClientDisconnected; |
| | | |
| | | // 全局只订阅一次消息事件,保持原有行为。 |
| | | if (System.Threading.Interlocked.CompareExchange(ref _messageSubscribedFlag, 1, 0) == 0) |
| | | { |
| | | tcpSocket.MessageReceived += _messageRouter.HandleMessageReceivedAsync; |
| | | Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 机器手TCP消息事件已订阅"); |
| | | } |
| | | } |
| | | |
| | | private void OnClientDisconnected(object? sender, RobotSocketState state) |
| | | { |
| | | Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 客户端已断开连接: {state.IPAddress}"); |
| | | } |
| | | |
| | | public async Task Execute(IJobExecutionContext context) |
| | |
| | | |
| | | string ipAddress = robotCrane.IPAddress; |
| | | |
| | | // 获取或创建状态 |
| | | RobotSocketState state = _cache.GetOrAdd($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}", _ => new RobotSocketState |
| | | { |
| | | IPAddress = ipAddress, |
| | | RobotCrane = robotCrane |
| | | }); |
| | | |
| | | // 更新设备信息 |
| | | RobotSocketState state = _stateManager.GetOrCreateState(ipAddress, robotCrane); |
| | | state.RobotCrane = robotCrane; |
| | | |
| | | try |
| | | { |
| | | // 检查是否有该客户端连接 |
| | | var clientIds = _TcpSocket.GetClientIds(); |
| | | if (!clientIds.Contains(ipAddress)) |
| | | if (!_clientManager.EnsureClientSubscribed(ipAddress, robotCrane)) |
| | | { |
| | | 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; |
| | | |
| | | // 更新缓存中的状态 |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}", state); |
| | | } |
| | | } |
| | | |
| | | // 获取任务并缓存到状态中 |
| | | Dt_RobotTask? task = GetTask(robotCrane); |
| | | var task = _taskProcessor.GetTask(robotCrane); |
| | | if (task != null) |
| | | { |
| | | if (task.RobotTaskTotalNum <= MaxTaskTotalNum) |
| | | var latestState = _stateManager.GetState(ipAddress); |
| | | if (latestState == null) |
| | | { |
| | | // 处理正在执行的任务 |
| | | if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") |
| | | { |
| | | 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.OperStatus == "Homed" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | // 随机生成两天托盘条码存放到两个变量里面 |
| | | // 定义前缀(例如:TRAY代表托盘) |
| | | // 组盘读取线体条码 |
| | | if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode()) |
| | | { |
| | | string prefix = "TRAY"; |
| | | return; |
| | | } |
| | | |
| | | // 生成两个托盘条码 |
| | | 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); |
| | | } |
| | | } |
| | | if (latestState.RobotTaskTotalNum < MaxTaskTotalNum) |
| | | { |
| | | await _workflowOrchestrator.ExecuteAsync(latestState, task, ipAddress); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception) |
| | | { |
| | | } |
| | | finally |
| | | { |
| | | // 可选:在这里处理任何需要在任务完成后执行的清理工作 |
| | | } |
| | | } |
| | | |
| | | //临时测试用 |
| | | 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(barCode); |
| | | |
| | | return barCode; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 事件:客户端断开连接时触发 |
| | | /// </summary> |
| | | /// <param name="clientId"></param> |
| | | /// <returns></returns> |
| | | private Task<string?> _TcpSocket_RobotReceived(string clientId) |
| | | { |
| | | 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); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 事件:收到消息时触发 |
| | | /// </summary> |
| | | /// <param name="message"></param> |
| | | /// <param name="isJson"></param> |
| | | /// <param name="client"></param> |
| | | /// <param name="state"></param> |
| | | /// <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); |
| | | } |
| | | else 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 = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.SplitPalletAsync), stockDTO.ToJson()); |
| | | |
| | | if (result.Data.Status && result.IsSuccess) |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | |
| | | state.LastPickPositions = positions; |
| | | 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 = _httpClientHelper.Post<WebResponseContent>(configKey, stockDTO.ToJson()); |
| | | putSuccess = result.Data.Status && result.IsSuccess; |
| | | } |
| | | |
| | | if (putSuccess) |
| | | { |
| | | state.CurrentAction = "PutFinished"; |
| | | state.RobotTaskTotalNum += positions.Length; |
| | | task.RobotTaskTotalNum += positions.Length; |
| | | } |
| | | |
| | | 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}"); |
| | | } |
| | | } |
| | | |
| | | // 更新缓存中的状态 |
| | | _cache.TryUpdateIfChanged($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{state.IPAddress}", state); |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 机械手简单命令处理 |
| | | /// </summary> |
| | | /// <param name="message"></param> |
| | | /// <param name="state"></param> |
| | | /// <returns></returns> |
| | | private async Task<bool> 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 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 = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.CreateTaskInboundAsync), taskDto.ToJson()); |
| | | if (!result.Data.Status && result.IsSuccess) |
| | | { |
| | | 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> |
| | | /// <param name="message"></param> |
| | | /// <returns></returns> |
| | | private static bool IsPrefixCommand(string message) |
| | | { |
| | | return message.StartsWith("pickfinished") || message.StartsWith("putfinished"); |
| | | } |
| | | |
| | | 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, |
| | | Details = positions |
| | | .Where(x => x > 0) |
| | | .OrderBy(x => x) |
| | | .Select((x, idx) => new StockDetailDTO |
| | | { |
| | | Quantity = state.RobotTaskTotalNum > 0 ? state.RobotTaskTotalNum + positions.Length : positions.Length, |
| | | Channel = x, |
| | | 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); |
| | | // 异常处理已在组件内部进行,Job 层保持兜底吞吐语义。 |
| | | } |
| | | } |
| | | } |
| | | |
| | | public class RobotSocketState |
| | | { |
| | | public string IPAddress { get; set; } = string.Empty; |
| | | |
| | | /// <summary> |
| | | /// 是否已订阅消息事件 |
| | | /// </summary> |
| | | public bool IsEventSubscribed { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 机械手运行模式 |
| | | /// </summary> |
| | | public int? RobotRunMode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 机械手控制模式 |
| | | /// </summary> |
| | | public int? RobotControlMode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 机械手是否抓取物料,0-无物料,1-有物料 |
| | | /// </summary> |
| | | public int? RobotArmObject { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 机械手设备信息 |
| | | /// </summary> |
| | | public RobotCraneDevice? RobotCrane { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 当前动作 |
| | | /// </summary> |
| | | public string? CurrentAction { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 当前状态 |
| | | /// </summary> |
| | | public string? OperStatus { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 取货完成位置 |
| | | /// </summary> |
| | | public int[]? LastPickPositions { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 放货完成位置 |
| | | /// </summary> |
| | | public int[]? LastPutPositions { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 抓取位置条码 |
| | | /// </summary> |
| | | public List<string> CellBarcode { get; set; } |
| | | |
| | | /// <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; } |
| | | } |
| | | } |
| | | } |