Platform_ExtensionHandlerOperation.cs 10.4 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 static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
using PLCConvingStatus = HaHRCS.Rcs.Model.Entities.PLCConvingStatus;

namespace Rcs.Infrastructure.MessageBus.Handlers.Commands.PLCInteraction
{
    /// <summary>
    /// 伸出夹紧站台
    /// </summary>
    public class Platform_ExtensionHandlerOperation : IConsumer<PLCStationConvingExtend>
    {
        private readonly EquipmentExecutor _equipmentExecutor;
        private readonly ILogger<Platform_ExtensionHandlerOperation> _logger;
        private readonly PlatformPickRepository _platformRepository;
        public Platform_ExtensionHandlerOperation(
            ILogger<Platform_ExtensionHandlerOperation> logger,
            PlatformPickRepository platformRepository,
            EquipmentExecutor equipmentExecutor)
        {
            _logger = logger;
            _platformRepository = platformRepository;
            _equipmentExecutor = equipmentExecutor;
        }

        /// <summary>
        /// 参数校验
        /// </summary>
        private void ValidateCommand(PLCStationConvingExtend command, ConsumeContext<PLCStationConvingExtend> 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<PLCStationConvingExtend> context)
        {
            var command = context.Message;
            try
            {
                ValidateCommand(command, context);
                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, $"处理PLC伸出指令异常,设备编码:{command?.EquipmentCode},指令类型:{command?.Operation}");
                throw new InvalidOperationException(ex.Message);
            }
           
           // throw new NotImplementedException();
        }
        /// <summary>
        /// 处理不同伸出指令
        /// </summary>
        private async Task HandlePickupCommand(PLCStationConvingExtend command, Equipment equipment, ConsumeContext<PLCStationConvingExtend> context)
        {
            // 定义超时时间(可根据实际场景调整,比如30秒)
            var timeoutSeconds = 10;
            var pickupStatus = command.Operation;

            switch (pickupStatus)
            {
                // 伸出请求(单个属性写入+重试)
                case var s when s == PLCConvingStatus.Extend.GetIndexInt():
                    var isExtended1 = equipment[$"{CylinderProps.Retracted}"].Value;
                    if (isExtended1 == "")
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc($"请检查设备是否离线,设备不在线"));
                        break;
                    }
                    if (isExtended1 == "True")
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc($"装置已为伸出状态"));
                        break;
                    }
                    // 1. 下发伸出信号
                    await WritePlcPropWithRetry(
                        equipment,
                        CylinderProps.AirExtended.ToString(),
                        "True",
                        "伸出信号下发PLC成功",
                        "下发伸出信号失败",
                        context,
                        retryTimes: 3,
                        delayMs: 500);

                    // 2. 等待「伸出到位」为True(带超时+异步等待+实时刷新值)
                    var extendStartTime = DateTime.Now;
                    bool extendTimeout = false;
                    while (true)
                    {
                        // 实时刷新PLC属性值(关键:避免读取缓存)
                        var isExtended = equipment[$"{CylinderProps.Retracted}"].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 == PLCConvingStatus.Clamp.GetIndexInt():
                    var istrue1 = equipment[$"{CylinderProps.Extended}"].Value;
                    if (istrue1 == "")
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc($"请检查设备是否离线,设备不在线"));
                        break;
                    }
                    if (istrue1 == "True")
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc($"装置已为缩回状态"));
                        break;
                    }
                    // 1. 下发缩回信号
                    await WritePlcPropWithRetry(
                        equipment,
                        CylinderProps.AirRetracted.ToString(),
                        "True",
                        "伸回信号下发PLC成功",
                        "下发伸回信号失败",
                        context,
                        retryTimes: 3,
                        delayMs: 500);

                    // 2. 等待「缩回到位」为True(带超时+异步等待+实时刷新值)
                    var retractStartTime = DateTime.Now;
                    bool retractTimeout = false;
                    while (true)
                    {
                        // 实时刷新PLC属性值
                        var istrue = equipment[$"{CylinderProps.Extended}"].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}秒)"));
                        break;
                    }
                    else
                    {
                        await context.RespondAsync(ApiResponse.SuccessfulToPlc("伸回信号完成"));
                        break;
                    }
                    //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<PLCStationConvingExtend> 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}次)");
        }
    }
}