using Quartz;
|
using System.Collections.Concurrent;
|
using System.Net.Sockets;
|
using WIDESEAWCS_Core.Helper;
|
using WIDESEAWCS_DTO.Stock;
|
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 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)
|
{
|
_TcpSocket = TcpSocket;
|
_taskService = 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 = _socketStates.GetOrAdd(ipAddress, _ => new RobotSocketState
|
{
|
IPAddress = ipAddress,
|
RobotCrane = robotCrane
|
});
|
|
// 更新设备信息
|
state.RobotCrane = robotCrane;
|
|
// 检查是否有该客户端连接
|
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)
|
{
|
_TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client);
|
Task clientTask = _TcpSocket.HandleClientAsync(client, robotCrane.IPAddress, _TcpSocket._cts.Token, state);
|
state.IsEventSubscribed = true;
|
}
|
|
// 获取任务并缓存到状态中
|
Dt_RobotTask? task = GetTask(robotCrane);
|
if (task != null)
|
{
|
state.CurrentTask = task;
|
if (task.RobotTaskTotalNum != 48)
|
{
|
// 处理正在执行的任务
|
if (state.RobotRunMode == 1 && state.RobotControlMode == 1)
|
{
|
await Task.Delay(1000);
|
if ((state.CurrentAction == "Homed" || state.CurrentAction == "PickFinished" || state.CurrentAction == "PutFinished") && state.OperStatus == "Running")
|
{
|
// TODO 读取线体电池条码,发送取电池指令
|
if (true)
|
{
|
// 模拟读取条码
|
state.CellBarcode = new string[] { "CellBarcode1", "CellBarcode2", "CellBarcode3", "CellBarcode4" };
|
|
string taskString = $"Pickbattery,{task.RobotSourceAddress}";
|
// 发送任务指令
|
bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString);
|
}
|
}
|
}
|
}
|
}
|
|
return;
|
}
|
|
/// <summary>
|
/// 事件:客户端断开连接时触发
|
/// </summary>
|
/// <param name="clientId"></param>
|
/// <returns></returns>
|
private Task<string?> _TcpSocket_RobotReceived(string clientId)
|
{
|
_socketStates.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 (IsSimpleCommand(messageLower, state))
|
{
|
return null;
|
}
|
|
if (IsPrefixCommand(messageLower))
|
{
|
try
|
{
|
var parts = message.Split(',');
|
if (parts.Length >= 1)
|
{
|
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]);
|
}
|
|
if (cmd.StartsWith("pickfinished"))
|
{
|
state.LastPickPositions = positions;
|
state.CurrentAction = "PickFinished";
|
}
|
else if (cmd.StartsWith("putfinished"))
|
{
|
state.LastPutPositions = positions;
|
// 发送数据给WMS组盘/换盘
|
StockDTO stockDTO = 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 > 0 ? x : throw new ArgumentOutOfRangeException(nameof(x), "Channel must be positive"),
|
CellBarcode = state.CellBarcode[idx]
|
})
|
.ToList()
|
};
|
state.CurrentAction = "PutFinished";
|
}
|
}
|
}
|
catch { }
|
|
return null;
|
}
|
|
return null;
|
}
|
|
/// <summary>
|
/// 机械手简单命令处理
|
/// </summary>
|
/// <param name="message"></param>
|
/// <param name="state"></param>
|
/// <returns></returns>
|
private bool IsSimpleCommand(string message, RobotSocketState state)
|
{
|
switch (message)
|
{
|
case "homing":
|
state.CurrentAction = "Homing";
|
return true;
|
|
case "homed":
|
state.CurrentAction = "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 == 2|| state.CurrentTask?.RobotTaskType == 3)
|
{
|
// TODO 机械手取货完成,判断是否换盘、拆盘任务,创建空托盘回库任务
|
}
|
return true;
|
|
case "allputfinished":
|
state.CurrentAction = "AllPutFinished";
|
if (state.CurrentTask?.RobotTaskType == 1 )
|
{
|
// TODO 机械手取货完成,判断是否组盘任务,创建组盘入库任务
|
}
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 机械手前缀命令处理
|
/// </summary>
|
/// <param name="message"></param>
|
/// <returns></returns>
|
private static bool IsPrefixCommand(string message)
|
{
|
return message.StartsWith("pickfinished") || message.StartsWith("putfinished");
|
}
|
|
private Dt_RobotTask? GetTask(RobotCraneDevice robotCrane)
|
{
|
return _taskService.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>
|
/// 机械手抓取对象
|
/// </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 string[] CellBarcode { get; set; }
|
|
/// <summary>
|
/// 当前抓取任务
|
/// </summary>
|
public Dt_RobotTask? CurrentTask { get; set; }
|
}
|
}
|