CustomProtocolService.cs 11.2 KB
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Rcs.Application.Common;
using Rcs.Application.Services;
using Rcs.Application.Services.Protocol;
using Rcs.Cyaninetech.Services;
using Rcs.Domain.Entities;
using Rcs.Domain.Extensions;
using Rcs.Domain.Models.VDA5050;
using Rcs.Domain.Settings;
using TaskStatus = Rcs.Domain.Entities.TaskStatus;

namespace Rcs.Infrastructure.Services.Protocol;

/// <summary>
/// 自定义协议服务实现
/// 通过LanYin服务与机器人通信
/// @author zzy
/// </summary>
public class CustomProtocolService : IProtocolService
{
    private readonly ILogger<CustomProtocolService> _logger;
    private readonly ILanYinService _lanYinService;
    private readonly IUnifiedTrafficControlService _unifiedTrafficControlService;
    private readonly LanYinSettings _lanYinSettings;

    public CustomProtocolService(
        ILogger<CustomProtocolService> logger,
        ILanYinService lanYinService,
        IUnifiedTrafficControlService unifiedTrafficControlService,
        IOptions<AppSettings> settings)
    {
        _logger = logger;
        _lanYinService = lanYinService;
        _unifiedTrafficControlService = unifiedTrafficControlService;
        _lanYinSettings = settings.Value.LanYinSettings;
    }

    /// <summary>
    /// 支持的协议类型:自定义协议
    /// </summary>
    public ProtocolType ProtocolType => ProtocolType.Custom;

    /// <summary>
    /// 发送订单指令
    /// </summary>
    public Task<ApiResponse> PrepareSendOrderAsync(Robot robot, RobotTask task, CancellationToken ct = default)
    {
        return SendOrderByTaskAsync(robot, task, ct);
    }

    /// <summary>
    /// 发送订单指令
    /// </summary>
    public Task<ApiResponse> SendOrderAsync(
        Robot robot,
        int priority,
        Guid endNodeId,
        string? containerId,
        CancellationToken ct = default)
    {
        return Task.FromResult(ApiResponse.Failed("CustomProtocol - direct node order is not supported"));
    }

    public Task<ApiResponse> SendOrderWithTaskContextAsync(
        Robot robot,
        int priority,
        Guid endNodeId,
        string? containerId,
        Guid? taskId = null,
        Guid? subTaskId = null,
        Guid? taskTemplateId = null,
        int? subTaskSequence = null,
        string? taskCode = null,
        Guid? mapId = null,
        CancellationToken ct = default)
    {
        return SendOrderAsync(robot, priority, endNodeId, containerId, ct);
    }

    private async Task<ApiResponse> SendOrderByTaskAsync(
        Robot robot,
        RobotTask task,
        CancellationToken ct = default)
    {
        var areaCode = task.EndLocation?.StorageArea?.AreaCode;
        if (string.IsNullOrEmpty(areaCode))
        {
            return ApiResponse.Failed($"CustomProtocol - 未找到库区编码,任务: {task.TaskCode}");
        }

        var locationCode = task.BeginLocation?.LocationCode;
        if (string.IsNullOrEmpty(locationCode))
        {
            return ApiResponse.Failed($"CustomProtocol - 未找到起始库位编码,任务: {task.TaskCode}");
        }
        var endLocationCode = task.EndLocation?.LocationCode;
        if (string.IsNullOrEmpty(endLocationCode))
        {
            return ApiResponse.Failed($"CustomProtocol - 未找到目标库位编码,任务: {task.TaskCode}");
        }
        var request = new Rcs.Cyaninetech.Models.LanYinDispatchTaskRequest
        {
            location_id = locationCode,
            area = areaCode,
            user_specified_robot_id = robot.RobotSerialNumber
        };
        if (_lanYinSettings.TargetType == 1)
        {
            request.store_location_id = endLocationCode;
        }
        else if (_lanYinSettings.TargetType != 2)
        {
            _logger.LogWarning(
                "CustomProtocol - unsupported LanYin target type: {TargetType}, defaulting to no store_location_id. Task={TaskCode}",
                _lanYinSettings.TargetType,
                task.TaskCode);
        }

        var res = await _lanYinService.DispatchTaskAsync(request, ct);
        if (res.Success)
        {
            if (!string.IsNullOrEmpty(res.Data.RunningId))
            {
                task.Relation = res.Data.RunningId;
            }
            return ApiResponse.Successful();
        }
        return ApiResponse.Failed($"Dispatch task failed: {res.Message}");

    }

    /// <summary>
    /// 取消指定订单
    /// </summary>
    public async Task CancelOrderAsync(Robot robot, string? orderId, CancellationToken ct = default)
    {
        _logger.LogInformation("CustomProtocol - 取消订单,机器人: {SerialNumber}, 订单: {OrderId}",
            robot.RobotSerialNumber, orderId);

        var result = await _lanYinService.CancelTaskByRobotAsync(robot.RobotSerialNumber, ct);
        if (!result.Success)
        {
            _logger.LogError($"取消订单失败: {result.Message}");
        }
    }

    /// <summary>
    /// 取消机器人所有任务
    /// @author zzy
    /// </summary>
    public async Task CancelRobotTasksAsync(Robot robot, CancellationToken ct = default)
    {
        _logger.LogInformation("CustomProtocol - 取消所有任务,机器人: {SerialNumber}", robot.RobotSerialNumber);

        var result = await _lanYinService.CancelTaskByRobotAsync(robot.RobotSerialNumber, ct);
        await ConfirmExceptionAsync(robot, ct);
        if (!result.Success)
        {
            _logger.LogError($"取消任务失败: {result.Message}");
        }
    }

    /// <summary>
    /// 发送即时动作指令(自定义协议暂不支持)
    /// </summary>
    public Task SendInstantActionAsync(Robot robot, InstantAction actions, CancellationToken ct = default)
    {
        _logger.LogWarning("自定义协议暂不支持发送InstantAction指令,机器人: {SerialNumber}", robot.RobotSerialNumber);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 复位机器人
    /// </summary>
    public async Task ResetRobotAsync(Robot robot, CancellationToken ct = default)
    {
        _logger.LogInformation("CustomProtocol - 复位机器人: {SerialNumber}", robot.RobotSerialNumber);

        var result = await _lanYinService.ResetRobotAsync(robot.RobotSerialNumber, ct);
        await ConfirmExceptionAsync(robot, ct);
        if (!result.Success)
        {
            _logger.LogError($"复位机器人失败: {result.Message}");
        }
    }

    /// <summary>
    /// 确认异常
    /// </summary>
    public async Task ConfirmExceptionAsync(Robot robot, CancellationToken ct = default)
    {
        _logger.LogInformation("CustomProtocol - 确认异常,机器人: {SerialNumber}", robot.RobotSerialNumber);

        var result = await _lanYinService.ConfirmExceptionAsync(robot.RobotSerialNumber, ct);
        if (!result.Success)
        {
            _logger.LogError($"确认异常失败: {result.Message}");
        }
    }

    public Task<ApiResponse> StartChargingAsync(Robot robot, string actionName, CancellationToken ct = default)
    {
        _logger.LogWarning(
            "CustomProtocol does not support start charging action. Robot={SerialNumber}, Action={ActionName}",
            robot.RobotSerialNumber,
            actionName);
        return Task.FromResult(ApiResponse.Failed("Custom protocol does not support start charging action"));
    }

    public Task<ApiResponse> StopChargingAsync(Robot robot, CancellationToken ct = default)
    {
        _logger.LogWarning(
            "CustomProtocol does not support stop charging action. Robot={SerialNumber}",
            robot.RobotSerialNumber);
        return Task.FromResult(ApiResponse.Failed("Custom protocol does not support stop charging action"));
    }

    public async Task RobotPauseAsync(Robot robot, CancellationToken ct = default)
    {
        _logger.LogInformation("CustomProtocol - 暂停,机器人: {SerialNumber}", robot.RobotSerialNumber);

        var result = await _lanYinService.PauseRobotAsync(robot.RobotSerialNumber, ct);
        if (!result.Success)
        {
            _logger.LogError($"机器人暂停失败: {result.Message}");
        }
    }

    public async Task RobotUnPauseAsync(Robot robot, CancellationToken ct = default)
    {
        _logger.LogInformation("CustomProtocol - 取消暂停,机器人: {SerialNumber}", robot.RobotSerialNumber);

        var result = await _lanYinService.UnPauseRobotAsync(robot.RobotSerialNumber, ct);
        if (!result.Success)
        {
            _logger.LogError($"机器人取消暂停失败: {result.Message}");
        }
    }

    public async Task<ApiResponse> ReLocationAsync(Robot robot, string mapCode, double x, double y, double theta, CancellationToken ct = default)
    {
        var result = await _lanYinService.RelocateRobotAsync(robot.RobotSerialNumber, Convert.ToInt32(mapCode), x, y, theta, ct);
        if (!result.Success)
        {
            _logger.LogError($"机器人重定位失败: {result.Message}");
            return ApiResponse.Failed($"机器人重定位失败: {result.Message}");
        }
        return ApiResponse.Successful();
    }

    /// <summary>
    /// 发送下一段路径指令(自定义协议暂不支持分段下发)
    /// </summary>
    /// <param name="robot">机器人实体</param>
    /// <param name="currentSubTask">当前子任务</param>
    /// <param name="ct">取消令牌</param>
    /// <param name="reExec">是否重新执行</param>
    /// <returns>操作响应</returns>
    public Task<ApiResponse> SendNextSegmentAsync(Robot robot, RobotSubTask currentSubTask, CancellationToken ct = default, bool reExec = false)
    {
        _logger.LogWarning("自定义协议暂不支持分段下发下一段路径指令,机器人: {SerialNumber}, 任务: {TaskCode}",
            robot.RobotSerialNumber, currentSubTask.SubTaskId);
        return Task.FromResult(ApiResponse.Failed("自定义协议暂不支持分段下发功能"));
    }

    public Task<ApiResponse> SendNextSegmentAsync(string robotManufacturer, string robotSerialNumber, CancellationToken ct = default, bool reExec = false)
    {
        return Task.FromResult(ApiResponse.Failed("自定义协议暂不支持分段下发功能"));
    }

    /// <summary>
    /// 判断是否所有段已发送完毕(自定义协议不支持,始终返回false)
    /// @author zzy
    /// </summary>
    public Task<bool> IsAllSegmentsSentAsync(string robotManufacturer, string robotSerialNumber, CancellationToken ct = default)
    {
        return Task.FromResult(false);
    }

    /// <summary>
    /// 判断机器人当前是否行驶在目前已下发路径集合中的最后一段路径上(自定义协议不支持,始终返回false,表示跳过发送)
    /// @author zzy
    /// </summary>
    public Task<bool> IsOnLastSentSegmentAsync(string robotManufacturer, string robotSerialNumber, CancellationToken ct = default)
    {
        return Task.FromResult(false);
    }

    /// <summary>
    /// 根据VDA5050 State消息判断订单完成(自定义协议不支持)
    /// @author zzy
    /// </summary>
    public Task<bool> TryCompleteOrderFromStateAsync(
        string robotManufacturer, string robotSerialNumber,
        State stateInfo, CancellationToken ct = default)
    {
        return Task.FromResult(false);
    }

    public Task ClearAllVdaPathCacheAsync(Guid robotId)
    {
        return Task.FromResult(false);
    }
}