| | |
| | | using WIDESEAWCS_QuartzJob; |
| | | using Microsoft.Extensions.Logging; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_QuartzJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// 输送线设备请求处理器:处理拘束机/插拔钉机上下层请求。 |
| | | /// 输送线目标地址选择器 - 处理拘束机/插拔钉机的上下层请求 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 核心职责: |
| | | /// 1. 处理入库场景的目标地址选择 |
| | | /// 2. 处理出库场景的目标地址选择 |
| | | /// 3. 判断拘束机和插拔钉机的物料请求状态 |
| | | /// 4. 协调输送线与上下层设备之间的物料流转 |
| | | /// |
| | | /// 拘束机和插拔钉机都有上下两层结构, |
| | | /// 每层都有独立的物料请求和出料信号,需要分别处理。 |
| | | /// </remarks> |
| | | public class ConveyorLineTargetAddressSelector |
| | | { |
| | | /// <summary> |
| | | /// 拘束机名称常量 |
| | | /// </summary> |
| | | private const string ConstraintMachineName = "拘束机"; |
| | | |
| | | /// <summary> |
| | | /// 插拔钉机名称常量 |
| | | /// </summary> |
| | | private const string PinMachineName = "插拔钉机"; |
| | | |
| | | // 拘束机点位 |
| | | /// <summary> |
| | | /// 拘束机对应的点位编码列表 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 当目标地址在这些编码中时,表示需要与拘束机交互。 |
| | | /// </remarks> |
| | | private static readonly List<string> ConstraintMachineCodes = new List<string> { "10180", "20090" }; |
| | | // 插拔钉机点位 |
| | | |
| | | /// <summary> |
| | | /// 插拔钉机对应的点位编码列表 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 当目标地址在这些编码中时,表示需要与插拔钉机交互。 |
| | | /// </remarks> |
| | | private static readonly List<string> PinMachineCodes = new List<string> { "10190", "20100" }; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="logger">日志记录器</param> |
| | | public ConveyorLineTargetAddressSelector(ILogger logger) |
| | | { |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理入库场景的下一地址请求 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 当入库任务执行到某个位置时调用此方法。 |
| | | /// 判断目标设备是否需要物料或可以出料。 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象</param> |
| | | /// <param name="nextAddress">下一地址/目标设备编码</param> |
| | | /// <param name="childDeviceCode">当前子设备编码</param> |
| | | public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | _logger.LogDebug("HandleInboundNextAddress:入库下一地址,子设备: {ChildDeviceCode},目标地址: {NextAddress}", childDeviceCode, nextAddress); |
| | | QuartzLogger.Debug($"HandleInboundNextAddress:入库下一地址,子设备: {childDeviceCode},目标地址: {nextAddress}", conveyorLine.DeviceCode); |
| | | // 调用通用处理方法,isUpper = true 表示处理上层 |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理出库场景的下一地址请求 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 当出库任务执行到某个位置时调用此方法。 |
| | | /// 判断目标设备是否需要物料或可以出料。 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象</param> |
| | | /// <param name="nextAddress">下一地址/目标设备编码</param> |
| | | /// <param name="childDeviceCode">当前子设备编码</param> |
| | | public void HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | _logger.LogDebug("HandleOutboundNextAddress:出库下一地址,子设备: {ChildDeviceCode},目标地址: {NextAddress}", childDeviceCode, nextAddress); |
| | | QuartzLogger.Debug($"HandleOutboundNextAddress:出库下一地址,子设备: {childDeviceCode},目标地址: {nextAddress}", conveyorLine.DeviceCode); |
| | | // 调用通用处理方法,isUpper = false 表示处理下层 |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: false); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 通用设备请求处理方法 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 根据目标地址类型(拘束机/插拔钉机)调用相应的处理逻辑。 |
| | | /// 处理上下层设备的物料请求和出料协调。 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象</param> |
| | | /// <param name="nextAddress">下一地址/目标设备编码</param> |
| | | /// <param name="childDeviceCode">当前子设备编码</param> |
| | | /// <param name="isUpper">是否处理上层(true=上层,false=下层)</param> |
| | | private void HandleDeviceRequest(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode, bool isUpper) |
| | | { |
| | | // 获取全局设备列表 |
| | | var devices = Storage.Devices; |
| | | |
| | | // 判断目标设备类型 |
| | | if (ConstraintMachineCodes.Contains(nextAddress)) |
| | | { |
| | | // 拘束机处理分支 |
| | | // 查找拘束机设备 |
| | | ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName); |
| | | if (constraint == null) |
| | | { |
| | | _logger.LogDebug("HandleDeviceRequest:未找到拘束机设备"); |
| | | QuartzLogger.Debug("HandleDeviceRequest:未找到拘束机设备", conveyorLine.DeviceCode); |
| | | // 未找到拘束机设备,直接返回 |
| | | return; |
| | | } |
| | | |
| | | // 处理拘束机的请求 |
| | | ProcessDeviceRequest( |
| | | conveyorLine, |
| | | childDeviceCode, |
| | | // 获取物料请求标志(上层或下层) |
| | | getMaterialRequest: () => isUpper |
| | | ? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestUpper) != 0 |
| | | : constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestLower) != 0, |
| | | // 获取出料请求标志(上层或下层) |
| | | getOutputRequest: () => isUpper |
| | | ? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestUpper) != 0 |
| | | : constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestLower) != 0, |
| | | // 设置输出就绪标志(上层或下层) |
| | | setOutputReady: outputReq => |
| | | { |
| | | if (isUpper) |
| | |
| | | { |
| | | constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | }, |
| | | "拘束机"); |
| | | } |
| | | else if (PinMachineCodes.Contains(nextAddress)) |
| | | { |
| | | // 插拔钉机处理分支 |
| | | // 查找插拔钉机设备 |
| | | PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName); |
| | | if (pinMachine == null) |
| | | { |
| | | _logger.LogDebug("HandleDeviceRequest:未找到插拔钉机设备"); |
| | | QuartzLogger.Debug("HandleDeviceRequest:未找到插拔钉机设备", conveyorLine.DeviceCode); |
| | | return; |
| | | } |
| | | |
| | | // 处理插拔钉机的请求 |
| | | ProcessDeviceRequest( |
| | | conveyorLine, |
| | | childDeviceCode, |
| | | // 获取物料请求标志(上层或下层) |
| | | getMaterialRequest: () => isUpper |
| | | ? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestUpper) != 0 |
| | | : pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestLower) != 0, |
| | | // 获取出料请求标志(上层或下层) |
| | | getOutputRequest: () => isUpper |
| | | ? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestUpper) != 0 |
| | | : pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestLower) != 0, |
| | | // 设置输出就绪标志(上层或下层) |
| | | setOutputReady: outputReq => |
| | | { |
| | | if (isUpper) |
| | |
| | | { |
| | | pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | }, |
| | | "插拔钉机"); |
| | | } |
| | | } |
| | | |
| | | private static void ProcessDeviceRequest( |
| | | /// <summary> |
| | | /// 处理设备请求的核心逻辑 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 根据物料请求和出料请求的状态: |
| | | /// - 如果有物料请求,设置目标地址并发送 ACK |
| | | /// - 如果有出料请求,设置设备的输出就绪标志 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象</param> |
| | | /// <param name="childDeviceCode">当前子设备编码</param> |
| | | /// <param name="getMaterialRequest">获取物料请求状态的委托</param> |
| | | /// <param name="getOutputRequest">获取出料请求状态的委托</param> |
| | | /// <param name="setOutputReady">设置输出就绪标志的委托</param> |
| | | /// <param name="deviceType">设备类型描述</param> |
| | | private void ProcessDeviceRequest( |
| | | CommonConveyorLine conveyorLine, |
| | | string childDeviceCode, |
| | | Func<bool> getMaterialRequest, |
| | | Func<bool> getOutputRequest, |
| | | Action<bool> setOutputReady) |
| | | Action<bool> setOutputReady, |
| | | string deviceType) |
| | | { |
| | | // 获取物料请求状态 |
| | | bool materialReq = getMaterialRequest(); |
| | | |
| | | // 获取出料请求状态 |
| | | bool outputReq = getOutputRequest(); |
| | | |
| | | _logger.LogDebug("ProcessDeviceRequest:{DeviceType},子设备: {ChildDeviceCode},物料请求: {MaterialReq},出料请求: {OutputReq}", |
| | | deviceType, childDeviceCode, materialReq, outputReq); |
| | | QuartzLogger.Debug($"ProcessDeviceRequest:{deviceType},子设备: {childDeviceCode},物料请求: {materialReq},出料请求: {outputReq}", conveyorLine.DeviceCode); |
| | | |
| | | // 如果设备需要物料 |
| | | if (materialReq) |
| | | { |
| | | // 设置目标地址为 1(表示有料进来) |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Target, 1, childDeviceCode); |
| | | |
| | | // 回复 ACK 确认信号 |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | | |
| | | _logger.LogInformation("ProcessDeviceRequest:{DeviceType} 需要物料,已设置目标地址和ACK", deviceType); |
| | | QuartzLogger.Info($"ProcessDeviceRequest:{deviceType} 需要物料,已设置目标地址和ACK", conveyorLine.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | // 设备不需要物料,设置输出就绪标志 |
| | | // 通知设备可以继续出料 |
| | | setOutputReady(outputReq); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |