Platfrom_GatingHandlerOpenOrClose.cs 9.16 KB
using HaHRCS.Rcs.Executor.Enums;
using HaHRCS.Rcs.Executor.PLC;
using HaHRCS.Rcs.Model.Entities;
using MassTransit;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Application.MessageBus.Commands.PlatformInteraction;
using Rcs.Domain.Repositories;
using Rcs.Executor.PLC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PLCGatingStatus = HaHRCS.Rcs.Model.Entities.PLCGatingStatus;


namespace Rcs.Infrastructure.MessageBus.Handlers.Commands.PLCInteraction
{
    public class Platfrom_GatingHandlerOpenOrClose : IConsumer<PLCStaionGating>
    {

        private readonly EquipmentExecutor _equipmentExecutor;
        private readonly ILogger<Platfrom_GatingHandlerOpenOrClose> _logger;
        private readonly PlatformPickRepository _platformRepository;
        public Platfrom_GatingHandlerOpenOrClose(
            ILogger<Platfrom_GatingHandlerOpenOrClose> logger,
            PlatformPickRepository platformRepository,
            EquipmentExecutor equipmentExecutor)
        {
            _logger = logger;
            _platformRepository = platformRepository;
            _equipmentExecutor = equipmentExecutor;
        }
        private void ValidateCommand(PLCStaionGating command, ConsumeContext<PLCStaionGating> context)
        {
            //if (string.IsNullOrEmpty(command.EquipmentCode))
            //{
            //    context.RespondAsync(ApiResponse.ErrorToPlc("请确认设备"));
            //    return;
            //}
            //if (command.Operation == 0)
            //{
            //    context.RespondAsync(ApiResponse.ErrorToPlc("请确认指令发送"));
            //    return;
            //}
        }
        public async Task Consume(ConsumeContext<PLCStaionGating> context)
        {
            var command = context.Message;
            try
            {
                //ValidateCommand(command, context);
                Thread.Sleep(2000);
                await context.RespondAsync(ApiResponse.Successful($"开门成功"));
                //var equipment = _equipmentExecutor.equipment.First(t => t.Code == command.EquipmentCode);
                //if (equipment == null || string.IsNullOrEmpty(equipment.Code))
                //    ApiResponse.ErrorToPlc("无效站台设备,请检查设备名称");
                //await HandlePickupCommand(command, equipment, context);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"处理门控指令异常,设备编码:{command?.EquipmentCode},指令类型:{command?.Operation}");
                throw new InvalidOperationException(ex.Message);
            }

            //throw new NotImplementedException();
        }
        /// <summary>
        /// 处理不同伸出指令
        /// </summary>
        private async Task HandlePickupCommand(PLCStaionGating command, Equipment equipment, ConsumeContext<PLCStaionGating> context)
        {
            // 定义超时时间(可根据实际场景调整,比如30秒)
            var timeoutSeconds = 30;
            var pickupStatus = command.Operation;

            switch (pickupStatus)
            {
                // 伸出请求(单个属性写入+重试)
                case var s when s == PLCGatingStatus.Opendoor.GetIndexInt():
                    // 1. 下发伸出信号
                    await WritePlcPropWithRetry(
                        equipment,
                        GatingProps.Opendoor.ToString(),
                        "True",
                        "下发门控开门信号成功",
                        "下发门控开门信号异常",
                        context,
                        retryTimes: 3,
                        delayMs: 500);

                    // 2. 等待「开门到位」为True(带超时+异步等待+实时刷新值)
                    var extendStartTime = DateTime.Now;
                    bool extendTimeout = false;
                    while (true)
                    {
                        // 实时刷新PLC属性值(关键:避免读取缓存)
                        var isExtended = equipment[$"{GatingProps.Opendoorposition}"].Value;
                        var isExtendedBool = Convert.ToBoolean(isExtended);

                        // 条件1:到位信号为True → 跳出循环
                        if (isExtendedBool)
                        {
                            break;
                        }

                        // 条件2:超时 → 标记超时并跳出
                        if ((DateTime.Now - extendStartTime).TotalSeconds >= timeoutSeconds)
                        {
                            extendTimeout = true;
                            break;
                        }

                        // 异步等待(替代Thread.Sleep,避免阻塞线程)
                        await Task.Delay(100);
                    }

                    // 3. 根据结果返回响应
                    if (extendTimeout)
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc($"等待开门到位超时({timeoutSeconds}秒)"));
                    }
                    else
                    {
                        await context.RespondAsync(ApiResponse.SuccessfulToPlc("开门信号完成"));
                    }
                    break;

                // 缩回请求(批量属性写入+重试)
                case var s when s == PLCGatingStatus.Closedoor.GetIndexInt():
                    // 1. 下发缩回信号
                    await WritePlcPropWithRetry(
                        equipment,
                        GatingProps.Closedoor.ToString(),
                        "True",
                        "关门信号下发门控成功",
                        "关门信号下发门控异常",
                        context,
                        retryTimes: 3,
                        delayMs: 500);

                    // 2. 等待「关门到位」为True(带超时+异步等待+实时刷新值)
                    var retractStartTime = DateTime.Now;
                    bool retractTimeout = false;
                    while (true)
                    {
                        // 实时刷新PLC属性值
                        var istrue = equipment[$"{GatingProps.Closedoorproperly}"].Value;
                        var isRetractedBool = Convert.ToBoolean(istrue);

                        // 条件1:到位信号为True → 跳出循环
                        if (isRetractedBool)
                        {
                            break;
                        }

                        // 条件2:超时 → 标记超时并跳出
                        if ((DateTime.Now - retractStartTime).TotalSeconds >= timeoutSeconds)
                        {
                            retractTimeout = true;
                            break;
                        }

                        // 异步等待
                        await Task.Delay(100);
                    }

                    // 3. 根据结果返回响应
                    if (retractTimeout)
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc($"等待关门到位超时({timeoutSeconds}秒)"));
                    }
                    else
                    {
                        await context.RespondAsync(ApiResponse.SuccessfulToPlc("伸回关门完成"));
                    }
                    break;
            }
        }
        /// <summary>
        /// 单个PLC属性写入(带重试机制)
        /// </summary>
        /// <param name="retryTimes">重试次数</param>
        /// <param name="delayMs">每次重试间隔(毫秒)</param>
        private async Task WritePlcPropWithRetry(Equipment equipment, string propKey, string value,
            string successMsg, string failMsg, ConsumeContext<PLCStaionGating> context,
            int retryTimes = 3, int delayMs = 500)
        {


            // 重试逻辑
            int attempt = 0;
            while (attempt < retryTimes)
            {
                lock (_equipmentExecutor.WriteFlag)
                {
                    var prop = equipment[$"{propKey}"];
                    prop.Value = value;
                    try
                    {
                        var result = _equipmentExecutor.EquipmentCommunicationHub.Write(prop);
                        if (result.Success)
                        {
                            //context.RespondAsync(ApiResponse.SuccessfulToPlc(successMsg));
                            return; // 成功则退出
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, $"第{attempt + 1}次写入PLC属性【{propKey}】失败,将重试");
                    }
                }
                attempt++;
                if (attempt < retryTimes)
                    await Task.Delay(delayMs); // 重试前等待
            }

            // 所有重试都失败,抛出异常
            throw new InvalidOperationException($"{failMsg}(已重试{retryTimes}次)");
        }
    }
}