RobotStatePersistenceService.cs 3.87 KB
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Rcs.Application.Services;
using Rcs.Domain.Repositories;
using Rcs.Shared.Utils;

namespace Rcs.Infrastructure.Services
{
    /// <summary>
    /// 机器人状态持久化服务 - 定时将Redis中的机器人状态和位置同步到数据库
    /// @author zzy
    /// </summary>
    public class RobotStatePersistenceService : BackgroundService
    {
        private readonly ILogger<RobotStatePersistenceService> _logger;
        private readonly IServiceProvider _serviceProvider;
        private readonly IRobotCacheService _robotCacheService;
        private readonly TimeSpan _syncInterval = TimeSpan.FromSeconds(3);

        public RobotStatePersistenceService(
            ILogger<RobotStatePersistenceService> logger,
            IServiceProvider serviceProvider,
            IRobotCacheService robotCacheService)
        {
            _logger = logger;
            _serviceProvider = serviceProvider;
            _robotCacheService = robotCacheService;
        }

        /// <summary>
        /// 后台执行定时同步任务
        /// @author zzy
        /// </summary>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("[状态持久化] 机器人状态持久化服务已启动,同步间隔: {Interval}秒", _syncInterval.TotalSeconds);

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    await PersistRobotStatesAsync(stoppingToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "[状态持久化] 同步机器人状态到数据库时发生错误");
                }

                await Task.Delay(_syncInterval, stoppingToken);
            }
        }

        /// <summary>
        /// 批量将Redis中的机器人状态和位置同步到数据库
        /// @author zzy
        /// </summary>
        private async Task PersistRobotStatesAsync(CancellationToken cancellationToken)
        {
            var cacheData = await _robotCacheService.GetAllRobotCacheDataAsync();
            var count = 0;

            using var scope = _serviceProvider.CreateScope();
            var robotRepo = scope.ServiceProvider.GetRequiredService<IRobotRepository>();

            foreach (var (manufacturer, serialNumber, status, location) in cacheData)
            {
                if (cancellationToken.IsCancellationRequested) break;

                var robot = await robotRepo.GetByManufacturerAndSerialNumberAsync(manufacturer, serialNumber, cancellationToken);
                if (robot == null) continue;

                // 更新状态
                robot.UpdateDynamicStatus(
                    status: status.Status,
                    online: status.Online,
                    batteryLevel: status.BatteryLevel,
                    driving: status.Driving,
                    paused: status.Paused,
                    charging: status.Charging,
                    operatingMode: status.OperatingMode,
                    errors: status.Errors.ToJsonWithChinese());

                // 更新位置
                robot.UpdateLocation(
                    mapCode: location.MapId,
                    node: location.NodeId,
                    x: location.X,
                    y: location.Y,
                    theta: location.Theta);

                await robotRepo.UpdateAsync(robot, cancellationToken);
                await robotRepo.SaveChangesAsync(cancellationToken);
                count++;
            }

            if (count > 0)
            {
                _logger.LogDebug("[状态持久化] 已同步 {Count} 个机器人状态到数据库", count);
            }
        }
    }
}