Platform_InteractionHandlerRelease.cs 9.68 KB
using HaHRCS.Rcs.Dal.Repository;
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.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConvingStatus = HaHRCS.Rcs.Model.Entities.ConvingStatus;

namespace Rcs.Infrastructure.MessageBus.Handlers.Commands.PLCInteraction
{
    /// <summary>
    /// 站台交互处理器
    /// </summary>
    public class Platform_InteractionHandlerRelease : IConsumer<PLCStationReleaseInteraction>
    {
        private readonly EquipmentExecutor _equipmentExecutor;
        private readonly ILogger<Platform_InteractionHandlerRelease> _logger;
        private readonly PlatformPickRepository _platformRepository;
        public Platform_InteractionHandlerRelease(
            ILogger<Platform_InteractionHandlerRelease> logger,
            PlatformPickRepository platformRepository,
            EquipmentExecutor equipmentExecutor)
        {
            _logger = logger;
            _platformRepository = platformRepository;
            _equipmentExecutor= equipmentExecutor;
        }
        /// <summary>
        /// 取货
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task Consume(ConsumeContext<PLCStationReleaseInteraction> context)
        {
            var command = context.Message;
            try
            {
                // 1. 集中参数校验
                ValidateCommand(command, context);

                // 2. 获取并校验设备
                var equipment = _equipmentExecutor.equipment.First(t => t.Code == command.EquipmentCode);
                if (equipment == null || string.IsNullOrEmpty(equipment.Code))
                    await context.RespondAsync(ApiResponse.ErrorToPlc("无效站台设备,请检查设备名称"));

                // 3. 检查设备就绪状态
                var pickConveyorReady = equipment[$"{StationProps.PutConveyorReady}"].Value;
                if (pickConveyorReady == "False") 
                {
                    await context.RespondAsync(ApiResponse.ErrorToPlc("放货设备暂未准备就绪"));
                    return;
                }
                

                // 4. 处理取货指令(带重试逻辑)
                await HandlePickupCommand(command, equipment, context);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"处理PLC放货指令异常,设备编码:{command?.EquipmentCode},指令类型:{command?.Release}");
                throw new InvalidOperationException(ex.Message);
            }
        }

        #region 核心辅助方法(带重试)
        /// <summary>
        /// 参数校验
        /// </summary>
        private void ValidateCommand(PLCStationReleaseInteraction command, ConsumeContext<PLCStationReleaseInteraction> context)
        {
            if (string.IsNullOrEmpty(command.EquipmentCode))
            {
                context.RespondAsync(ApiResponse.ErrorToPlc("请确认哪条输送线设备"));
                return;
            }
            if (command.Release == 0)
            {
                context.RespondAsync(ApiResponse.ErrorToPlc("请确认指令发送"));
                return;

            }
            if (string.IsNullOrEmpty(command.Barcode))
            {
                context.RespondAsync(ApiResponse.ErrorToPlc("无效托盘码,托盘码为空"));
                return;
            }
        }

        /// <summary>
        /// 处理不同取货指令
        /// </summary>
        private async Task HandlePickupCommand(PLCStationReleaseInteraction command, Equipment equipment, ConsumeContext<PLCStationReleaseInteraction> context)
        {
            var pickupStatus = command.Release;
            var allowPick = equipment[$"{StationProps.AllowPick}"].Value;

            switch (pickupStatus)
            {
                // 取货请求(单个属性写入+重试)
                case var s when s == ConvingStatus.PutRequest.GetIndexInt():
                    await WritePlcPropWithRetry(equipment, StationProps.PickRequest.ToString(), "True", "放货信号下发PLC成功", "下发取货信号失败", context, retryTimes: 3, delayMs: 500);
                    if (allowPick != "True")
                    {
                        await context.RespondAsync(ApiResponse.ErrorToPlc("PLC未给允许放货信号"));
                        return;
                    }
                    await WritePlcPropWithRetry(equipment, StationProps.PickRuning.ToString(), "True", "放货准备好信号下发成功", "放货准备好信号下发失败", context, retryTimes: 3, delayMs: 500);
                    break;
                    //await WritePlcPropWithRetry(equipment, StationProps.PickRuning.ToString(), "True", "取货准备好信号下发成功", "取货准备好信号下发失败", context, retryTimes: 3, delayMs: 500);
                    //break;

                // 取货运行中(单个属性写入+重试)
                //case var s when s == ConvingStatus.PutRuning.GetIndexInt():
                //    if (allowPick != "True") 
                //    {
                //       await context.RespondAsync(ApiResponse.ErrorToPlc("PLC未给允许放货信号"));
                //        return;
                //    } 
                //    await WritePlcPropWithRetry(equipment, StationProps.PickRuning.ToString(), "True", "取货准备好信号下发成功", "取货准备好信号下发失败", context, retryTimes: 3, delayMs: 500);
                //    break;

                // 取货完成(批量属性写入+重试)
                case var s when s == ConvingStatus.PutDone.GetIndexInt():
                    await WritePlcPropsBatchWithRetry(equipment, context, retryTimes: 3, delayMs: 500);
                    break;
                default:
                    await context.RespondAsync(ApiResponse.ErrorToPlc("指令错误"));
                    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<PLCStationReleaseInteraction> 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}次)");
        }

        /// <summary>
        /// 批量PLC属性写入(带重试机制)
        /// </summary>
        private async Task WritePlcPropsBatchWithRetry(Equipment equipment, ConsumeContext<PLCStationReleaseInteraction> context,
            int retryTimes = 3, int delayMs = 500)
        {
            // 准备要写入的属性
           
            //var props = new List<EquipmentProp> { prop1, prop2, prop3 };

            // 重试逻辑
            int attempt = 0;
            while (attempt < retryTimes)
            {
                lock (_equipmentExecutor.WriteFlag)
                {
                    var prop1 = equipment[$"{StationProps.PutRequest}"]; prop1.Value = "0";
                    var prop2 = equipment[$"{StationProps.PutRuning}"]; prop2.Value = "0";
                    var prop3 = equipment[$"{StationProps.PutDone}"]; prop3.Value = "1";
                    try
                    {
                        var result = _equipmentExecutor.EquipmentCommunicationHub.Writes(prop1, prop2, prop3);
                        if (result.Success)
                        {
                             context.RespondAsync(ApiResponse.SuccessfulToPlc("放货完成信号告知PLC成功"));
                            return; // 成功则退出
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, $"第{attempt + 1}次批量写入PLC属性失败,将重试");
                    }
                }
                attempt++;
                if (attempt < retryTimes)
                    await Task.Delay(delayMs);
            }

            // 所有重试都失败
            throw new InvalidOperationException($"放货完成信号告知PLC失败(已重试{retryTimes}次)");
        }
        #endregion
    }
}