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<string, RobotSocketState> _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;
|
}
|
|
/// <summary>
|
/// 事件:客户端断开连接时触发
|
/// </summary>
|
/// <param name="clientId"></param>
|
/// <returns></returns>
|
private Task<string?> _TcpSocket_RobotReceived(string clientId)
|
{
|
_cache.TryRemove(clientId, out _);
|
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)
|
{
|
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;
|
}
|
|
/// <summary>
|
/// 机械手简单命令处理
|
/// </summary>
|
/// <param name="message"></param>
|
/// <param name="state"></param>
|
/// <returns></returns>
|
private static 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 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>
|
/// <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)
|
{
|
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;
|
|
/// <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; } = 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; }
|
}
|
}
|