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_Core;
|
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 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 ITaskExecuteDetailService _taskExecuteDetailService;
|
private readonly ITaskRepository _taskRepository;
|
private readonly IRouterService _routerService;
|
|
public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService)
|
{
|
_TcpSocket = TcpSocket;
|
_robottaskService = RobottaskService;
|
this._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)
|
{
|
WebResponseContent content = new WebResponseContent();
|
string messageLower = message.ToLowerInvariant();
|
|
if (await IsSimpleCommandAsync(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"))
|
{
|
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.LastPickPositions = positions;
|
|
var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), stockDTO.ToJsonString(), state.CurrentTask?.RobotTaskType == 2 ? nameof(ConfigKey.ChangePalletAsync) : nameof(ConfigKey.SplitPalletAsync));
|
WebResponseContent? contentNullable = JsonConvert.DeserializeObject<WebResponseContent>(result);
|
content = contentNullable ?? new WebResponseContent();
|
|
if (content.Status)
|
{
|
state.CurrentAction = "PickFinished";
|
}
|
}
|
else if (cmd.StartsWith("putfinished"))
|
{
|
state.LastPutPositions = positions;
|
if (state.CurrentTask?.RobotTaskType == 1)
|
{
|
// 发送数据给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()
|
};
|
var result = await HttpRequestHelper.HTTPPostAsync(nameof(Category.WMS), stockDTO.ToJsonString(), nameof(ConfigKey.GroupPalletAsync));
|
WebResponseContent? contentNullable = JsonConvert.DeserializeObject<WebResponseContent>(result);
|
content = contentNullable ?? new WebResponseContent();
|
|
if (content.Status)
|
{
|
state.CurrentAction = "PutFinished";
|
}
|
}
|
}
|
}
|
}
|
catch { }
|
|
return null;
|
}
|
|
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.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)
|
{
|
await HandleInboundTaskAsync(state, useSourceAddress: true);
|
}
|
return true;
|
|
case "allputfinished":
|
state.CurrentAction = "AllPutFinished";
|
if (state.CurrentTask?.RobotTaskType == 1)
|
{
|
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.RobotTargetAddress ?? 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));
|
WebResponseContent content = JsonConvert.DeserializeObject<WebResponseContent>(result) ?? new WebResponseContent();
|
if (!content.Status)
|
{
|
return;
|
}
|
|
WMSTaskDTO taskDTO = JsonConvert.DeserializeObject<WMSTaskDTO>(content.Data.ToString() ?? string.Empty) ?? new WMSTaskDTO();
|
content = _taskService.ReceiveWMSTask(new List<WMSTaskDTO> { taskDTO });
|
if (!content.Status)
|
{
|
return;
|
}
|
|
var taskInfo = _taskService.QueryByTaskNum(taskDTO.TaskNum);
|
|
|
string targetAddress = useSourceAddress ? taskDTO.SourceAddress : taskDTO.TargetAddress;
|
|
IDevice? device = Storage.Devices.Where(x => x.DeviceProDTOs.Select(x => x.DeviceChildCode == taskDTO.SourceAddress).FirstOrDefault()).FirstOrDefault() ?? null;
|
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 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>
|
/// 机械手抓取对象
|
/// </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; }
|
}
|
}
|