TaskService.cs 5.01 KB
using System.Text.Json;
using Rcs.Application.Services;
using Rcs.Application.Services.BLL;
using Rcs.Application.Services.PathFind;
using Rcs.Application.Services.PathFind.Models;
using Rcs.Application.Shared;
using Rcs.Domain.Models.VDA5050;

namespace Rcs.Infrastructure.BLL;

public class TaskService : ITaskService
{
    
    private readonly IAgvPathService _agvPathService;
    private readonly IMqttClientService _mqttClientService;
    public TaskService(            
        IAgvPathService agvPathService,
        IMqttClientService mqttClientService)
    {
        _agvPathService = agvPathService;
        _mqttClientService = mqttClientService;
    }

    /// <summary>
    /// 发送下一段路径指令
    /// @author zzy
    /// 2026-02-05 更新:支持两层切割结构
    /// </summary>
    /// <param name="robotBasic">机器人基础信息</param>
    /// <param name="pathCache">路径缓存</param>
    /// <param name="junctionIndex">路口段索引</param>
    /// <param name="resourceIndex">资源段索引</param>
    /// <param name="segmentSequenceId">分段序号</param>
    public async Task SendNextSegmentOrderAsync(
        RobotBasicCache robotBasic,
        VdaSegmentedPathCache pathCache,
        int junctionIndex,
        int resourceIndex,
        int segmentSequenceId)
    {
        // 获取地图图结构
        var graph = await _agvPathService.GetOrBuildGraphAsync(pathCache.MapId);
        if (graph == null)
        {
            throw new Exception($"无法加载地图图结构: MapId={pathCache.MapId}");
        }

        // 构建VDA5050订单
        var order = new Domain.Models.VDA5050.Order
        {
            HeaderId = int.Parse(robotBasic.RobotVersion) + segmentSequenceId * 2,
            Timestamp = DateTime.Now.ToString("O"),
            Version = robotBasic.ProtocolVersion,
            Manufacturer = robotBasic.RobotManufacturer,
            SerialNumber = robotBasic.RobotSerialNumber,
            OrderId = pathCache.TaskCode,
            OrderUpdateId = segmentSequenceId,
            Nodes = new List<Node>(),
            Edges = new List<Edge>()
        };

        // 获取指定的路径段
        var segments = pathCache.JunctionSegments[junctionIndex].ResourceSegments[resourceIndex].Segments;

        // 填充订单节点和边
        FillOrderWithSegments(order, segments, graph);

        // 发送订单
        var payload = JsonSerializer.Serialize(order);
        await _mqttClientService.PublishOrderAsync(
            robotBasic.ProtocolName,
            robotBasic.ProtocolVersion,
            robotBasic.RobotManufacturer,
            robotBasic.RobotSerialNumber,
            payload);
    }
    
    /// <summary>
        /// 将路径段转换为VDA5050的Nodes与Edges并填充到订单中
        /// @author zzy
        /// </summary>
        private void FillOrderWithSegments(Domain.Models.VDA5050.Order order, List<PathSegmentWithCode> segments, PathGraph graph)
        {
            if (segments.Count == 0) return;

            var nodeLookup = graph.Nodes;
            var mapCode = !string.IsNullOrWhiteSpace(graph.MapCode) ? graph.MapCode : graph.MapId.ToString();

            var sequenceId = 0;
            var firstSeg = segments[0];
            order.Nodes.Add(BuildNode(firstSeg.FromNodeId, firstSeg.FromNodeCode, nodeLookup, mapCode, sequenceId));

            foreach (var seg in segments)
            {
                order.Edges.Add(BuildEdge(seg, sequenceId + 1));
                sequenceId += 2;
                order.Nodes.Add(BuildNode(seg.ToNodeId, seg.ToNodeCode, nodeLookup, mapCode, sequenceId));
            }
        }

        /// <summary>
        /// 构建VDA5050节点信息
        /// @author zzy
        /// </summary>
        private static Node BuildNode(Guid nodeId, string nodeCode, IReadOnlyDictionary<Guid, PathNode> lookup, string mapCode, int sequenceId)
        {
            var node = new Node
            {
                NodeId = string.IsNullOrEmpty(nodeCode) ? nodeId.ToString() : nodeCode,
                SequenceId = sequenceId,
                Released = false
            };

            if (lookup.TryGetValue(nodeId, out var pathNode))
            {
                node.NodePosition = new NodePosition
                {
                    X = pathNode.X,
                    Y = pathNode.Y,
                    Theta = pathNode.Theta,
                    MapId = mapCode
                };
            }

            return node;
        }

        /// <summary>
        /// 构建VDA5050边信息
        /// @author zzy
        /// </summary>
        private static Edge BuildEdge(PathSegmentWithCode seg, int sequenceId)
        {
            return new Edge
            {
                EdgeId = string.IsNullOrEmpty(seg.EdgeCode) ? seg.EdgeId.ToString() : seg.EdgeCode,
                SequenceId = sequenceId,
                Released = false,
                StartNodeId = seg.FromNodeCode,
                EndNodeId = seg.ToNodeCode,
                MaxSpeed = seg.MaxSpeed,
                Length = seg.Length
            };
        }
}