CreateOrUpdateRobotCommandHandler.cs 11.5 KB
using MassTransit;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Domain.Entities;
using Rcs.Domain.Repositories;
using Rcs.Domain.ValueObjects;
using Rcs.Shared.Utils;

namespace Rcs.Infrastructure.MessageBus.Handlers.Commands;

/// <summary>
/// 创建机器人命令处理器
/// @author zzy - 修改为使用ManufacturerId和RobotTypeId
/// </summary>
public class CreateOrUpdateRobotCommandHandler : IConsumer<CreateOrUpdateRobotCommand>
{
    private readonly ILogger<CreateOrUpdateRobotCommandHandler> _logger;
    private readonly IRobotRepository _robotRepository;
    private readonly IMapRepository _mapRepository;

    public CreateOrUpdateRobotCommandHandler(
        ILogger<CreateOrUpdateRobotCommandHandler> logger,
        IRobotRepository robotRepository,
        IMapRepository mapRepository)
    {
        _logger = logger;
        _robotRepository = robotRepository;
        _mapRepository = mapRepository;
    }

    public async Task Consume(ConsumeContext<CreateOrUpdateRobotCommand> context)
    {
        var command = context.Message;
        try
        {
            Map? existingMap = null;
            if (!string.IsNullOrWhiteSpace(command.CurrentMapCode))
            {
                existingMap = await _mapRepository.GetByMapCodeAsync(command.CurrentMapCode, context.CancellationToken);
                if (existingMap == null)
                {
                    throw new InvalidOperationException($"地图编码 {command.CurrentMapCode} 不存在");
                }
            }

            if (!Guid.TryParse(command.RobotId, out Guid robotId))
            {
                // 新建
                var existingCode = await _robotRepository.GetByRobotCodeAsync(command.RobotCode, context.CancellationToken);
                if (existingCode != null)
                {
                    throw new InvalidOperationException($"机器人编码 {command.RobotCode} 已存在");
                }

                var existingBySerial = await _robotRepository.GetBySerialNumberAsync(command.RobotSerialNumber, context.CancellationToken);
                if (existingBySerial != null)
                {
                    throw new InvalidOperationException($"机器人序列号 {command.RobotSerialNumber} 已存在");
                }

                var robot = new Robot
                {
                    RobotId = Guid.NewGuid(),
                    RobotCode = command.RobotCode,
                    RobotName = command.RobotName,
                    RobotVersion = command.RobotVersion,
                    ProtocolName = command.ProtocolName,
                    ProtocolVersion = command.ProtocolVersion,
                    ProtocolType = (ProtocolType)command.ProtocolType,
                    RobotManufacturer = command.RobotManufacturer,
                    RobotSerialNumber = command.RobotSerialNumber,
                    RobotType = (RobotType)command.RobotType,
                    IpAddress = command.IpAddress,
                    NetPort = command.NetPort,
                    MacAddress = command.MacAddress,
                    CoordinateScale = command.CoordinateScale,
                    PayloadCapacity = command.PayloadCapacity,
                    SpeedMax = command.SpeedMax,
                    SpeedMin = command.SpeedMin,
                    AccelerationMax = command.AccelerationMax,
                    DecelerationMax = command.DecelerationMax,
                    HeightMin = command.HeightMin,
                    HeightMax = command.HeightMax,
                    Width = command.Width,
                    Length = command.Length,
                    MovementType = (MovementType)command.MovementType,
                    Radius = command.Radius,
                    CurrentMapCodeId = existingMap?.MapId,
                    Active = command.Active,
                    MotionCenterToEdge = command.MotionCenterToEdge,
                    RobotModel = command.RobotModel,
                    Status = RobotStatus.Idle,
                    Online = OnlineStatus.Offline,
                    OperatingMode = OperatingMode.Automatic,
                    CreatedAt = DateTime.Now,
                    UpdatedAt = DateTime.Now,
                    ForkRadOffset = command.ForkAngleOffset.Select(fa => (double)AngleConverter.ToCycleRadians(fa)).ToList(),
                    CacheRows = command.CacheRows,
                    CacheColumns = command.CacheColumns,
                    CacheLevels = command.CacheLevels
                };

                // 根据容量配置自动创建缓存储位并赋值给导航属性
                robot.CacheLocations = CreateCacheLocationsList(robot);

                await _robotRepository.AddAsync(robot, context.CancellationToken);
            }
            else
            {
                // 更新
                var robot = await _robotRepository.GetByIdFullAsync(robotId, context.CancellationToken);
                if (robot == null)
                {
                    throw new InvalidOperationException($"未找到机器人ID为 {robotId} 的机器人");
                }

                robot.RobotCode = command.RobotCode;
                robot.RobotName = command.RobotName;
                robot.RobotVersion = command.RobotVersion;
                robot.ProtocolName = command.ProtocolName;
                robot.ProtocolVersion = command.ProtocolVersion;
                robot.ProtocolType = (ProtocolType)command.ProtocolType;
                robot.RobotManufacturer = command.RobotManufacturer;
                robot.RobotSerialNumber = command.RobotSerialNumber;
                robot.RobotType = (RobotType)command.RobotType;
                robot.IpAddress = command.IpAddress;
                robot.NetPort = command.NetPort;
                robot.MacAddress = command.MacAddress;
                robot.CoordinateScale = command.CoordinateScale;
                robot.PayloadCapacity = command.PayloadCapacity;
                robot.SpeedMax = command.SpeedMax;
                robot.SpeedMin = command.SpeedMin;
                robot.AccelerationMax = command.AccelerationMax;
                robot.DecelerationMax = command.DecelerationMax;
                robot.HeightMin = command.HeightMin;
                robot.HeightMax = command.HeightMax;
                robot.Width = command.Width;
                robot.Length = command.Length;
                robot.MovementType = (MovementType)command.MovementType;
                robot.Radius = command.Radius;
                robot.CurrentMapCodeId = existingMap?.MapId;
                robot.Active = command.Active;
                robot.MotionCenterToEdge = command.MotionCenterToEdge;
                robot.RobotModel = command.RobotModel;
                robot.UpdatedAt = DateTime.Now;
                robot.ForkRadOffset = command.ForkAngleOffset.Select(fa => (double)AngleConverter.ToCycleRadians(fa)).ToList();
                robot.CacheRows = command.CacheRows;
                robot.CacheColumns = command.CacheColumns;
                robot.CacheLevels = command.CacheLevels;

                // 同步缓存储位:对比并更新集合
                SyncCacheLocations(robot);

                await _robotRepository.UpdateAsync(robot, context.CancellationToken);
            }

            await context.RespondAsync(ApiResponse.Successful());
        }
        catch (Exception ex)
        {
            await context.RespondAsync(ApiResponse.Failed(ex.Message));
        }
    }

    /// <summary>
    /// 根据Robot容量配置创建缓存储位列表
    /// </summary>
    private ICollection<RobotCacheLocation> CreateCacheLocationsList(Robot robot)
    {
        var locations = new List<RobotCacheLocation>();

        if (!robot.CacheRows.HasValue || !robot.CacheColumns.HasValue || !robot.CacheLevels.HasValue)
            return locations;

        for (int row = 1; row <= robot.CacheRows.Value; row++)
        {
            for (int col = 1; col <= robot.CacheColumns.Value; col++)
            {
                for (int level = 1; level <= robot.CacheLevels.Value; level++)
                {
                    var locationCode = $"{robot.RobotCode}-R{row:D2}C{col:D2}L{level:D2}";
                    var cacheLocation = RobotCacheLocation.Create(
                        robot.RobotId,
                        locationCode,
                        row,
                        col,
                        level,
                        $"自动生成的缓存储位 排{row}列{col}层{level}"
                    );
                    locations.Add(cacheLocation);
                }
            }
        }

        if (locations.Any())
        {
            _logger.LogInformation($"为Robot {robot.RobotCode} 创建了 {locations.Count} 个缓存储位");
        }

        return locations;
    }

    /// <summary>
    /// 同步缓存储位:对比现有储位,处理新增和删除
    /// </summary>
    private void SyncCacheLocations(Robot robot)
    {
        // 如果没有配置容量,清空所有储位
        if (!robot.CacheRows.HasValue || !robot.CacheColumns.HasValue || !robot.CacheLevels.HasValue)
        {
            if (robot.CacheLocations.Any())
            {
                robot.CacheLocations.Clear();
                _logger.LogInformation($"清空Robot {robot.RobotCode} 的所有缓存储位(容量未配置)");
            }
            return;
        }

        // 生成期望的储位集合
        var expectedLocations = new List<(int Row, int Column, int Level)>();
        for (int row = 1; row <= robot.CacheRows.Value; row++)
        {
            for (int col = 1; col <= robot.CacheColumns.Value; col++)
            {
                for (int level = 1; level <= robot.CacheLevels.Value; level++)
                {
                    expectedLocations.Add((row, col, level));
                }
            }
        }

        // 找出需要删除的储位(现有但不在期望中)
        var locationsToDelete = robot.CacheLocations
            .Where(existing => !expectedLocations.Any(expected =>
                expected.Row == existing.Row &&
                expected.Column == existing.Column &&
                expected.Level == existing.Level))
            .ToList();

        // 找出需要新增的储位(期望中但不在现有中)
        var locationsToAdd = expectedLocations
            .Where(expected => !robot.CacheLocations.Any(existing =>
                existing.Row == expected.Row &&
                existing.Column == expected.Column &&
                existing.Level == expected.Level))
            .Select(pos =>
            {
                var locationCode = $"{robot.RobotCode}-R{pos.Row:D2}C{pos.Column:D2}L{pos.Level:D2}";
                return RobotCacheLocation.Create(
                    robot.RobotId,
                    locationCode,
                    pos.Row,
                    pos.Column,
                    pos.Level,
                    $"自动生成的缓存储位 排{pos.Row}列{pos.Column}层{pos.Level}"
                );
            })
            .ToList();

        // 执行删除
        foreach (var location in locationsToDelete)
        {
            robot.CacheLocations.Remove(location);
        }

        // 执行新增
        foreach (var location in locationsToAdd)
        {
            robot.CacheLocations.Add(location);
        }

        if (locationsToDelete.Any() || locationsToAdd.Any())
        {
            _logger.LogInformation(
                $"同步Robot {robot.RobotCode} 的缓存储位: 删除 {locationsToDelete.Count} 个, 新增 {locationsToAdd.Count} 个");
        }
    }
}