| | |
| | | using WIDESEAWCS_QuartzJob; |
| | | using Serilog; |
| | | using WIDESEAWCS_QuartzJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// 输送线设备请求处理器:处理拘束机/插拔钉机上下层请求。 |
| | | /// 输送线目标地址选择器 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// </remarks> |
| | | public class ConveyorLineTargetAddressSelector |
| | | { |
| | | private const string ConstraintMachineName = "拘束机"; |
| | | private const string PinMachineName = "插拔钉机"; |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 通过 Microsoft.Extensions.Logging 接口注入,用于结构化日志输出。 |
| | | /// </remarks> |
| | | private readonly ILogger _logger; |
| | | |
| | | public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="logger">日志记录器,由依赖注入容器自动注入</param> |
| | | public ConveyorLineTargetAddressSelector(ILogger logger) |
| | | { |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true); |
| | | _logger = logger; // 保存日志记录器实例,供后续方法使用 |
| | | } |
| | | |
| | | public void HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | /// <summary> |
| | | /// 处理入库场景的下一地址请求 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 入库任务到达某个位置时调用此方法,判断目标设备是否需要物料。 |
| | | /// 入库对应上层工位(Layer.Upper),因为物料从上层进入仓库。 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象,用于写入目标地址和 ACK 信号</param> |
| | | /// <param name="nextAddress">下一地址/目标设备编码,用于识别目标设备类型</param> |
| | | /// <param name="childDeviceCode">当前子设备编码,用于精确定位写入哪个子设备</param> |
| | | public bool HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: false); |
| | | // 记录入库场景的调试日志,包含子设备和目标地址信息 |
| | | WriteDebug(conveyorLine, "入库下一地址", childDeviceCode, nextAddress); |
| | | |
| | | var cvState = conveyorLine.GetValue<ConveyorLineDBNameNew, byte>(ConveyorLineDBNameNew.CV_State, nextAddress); |
| | | bool isAvailable = cvState == 2; |
| | | if (isAvailable) |
| | | { |
| | | return conveyorLine.SetValue(ConveyorLineDBNameNew.Target, Convert.ToInt16(nextAddress), childDeviceCode); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private void HandleDeviceRequest(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode, bool isUpper) |
| | | /// <summary> |
| | | /// 处理出库场景的下一地址请求 |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 出库任务到达某个位置时调用此方法,判断目标设备是否需要出料。 |
| | | /// 出库对应下层工位(Layer.Lower),因为物料从下层离开仓库。 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象,用于写入目标地址和 ACK 信号</param> |
| | | /// <param name="nextAddress">下一地址/目标设备编码,用于识别目标设备类型</param> |
| | | /// <param name="childDeviceCode">当前子设备编码,用于精确定位写入哪个子设备</param> |
| | | public bool HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | var devices = Storage.Devices; |
| | | // 记录出库场景的调试日志,包含子设备和目标地址信息 |
| | | WriteDebug(conveyorLine, "出库下一地址", childDeviceCode, nextAddress); |
| | | |
| | | if (string.Equals(nextAddress, ConstraintMachineName, StringComparison.Ordinal)) |
| | | var cvState = conveyorLine.GetValue<ConveyorLineDBNameNew, byte>(ConveyorLineDBNameNew.CV_State, nextAddress); |
| | | bool isAvailable = cvState == 2; |
| | | if (isAvailable) |
| | | { |
| | | ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName); |
| | | if (constraint == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | ProcessDeviceRequest( |
| | | conveyorLine, |
| | | childDeviceCode, |
| | | getMaterialRequest: () => isUpper |
| | | ? constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestUpper) |
| | | : constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestLower), |
| | | getOutputRequest: () => isUpper |
| | | ? constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestUpper) |
| | | : constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestLower), |
| | | setOutputReady: outputReq => |
| | | { |
| | | if (isUpper) |
| | | { |
| | | constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyUpper, outputReq ? 1 : 0); |
| | | } |
| | | else |
| | | { |
| | | constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | return conveyorLine.SetValue(ConveyorLineDBNameNew.Target, Convert.ToInt16(nextAddress), childDeviceCode); |
| | | } |
| | | else if (string.Equals(nextAddress, PinMachineName, StringComparison.Ordinal)) |
| | | { |
| | | PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName); |
| | | if (pinMachine == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | ProcessDeviceRequest( |
| | | conveyorLine, |
| | | childDeviceCode, |
| | | getMaterialRequest: () => isUpper |
| | | ? pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestUpper) |
| | | : pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestLower), |
| | | getOutputRequest: () => isUpper |
| | | ? pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestUpper) |
| | | : pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestLower), |
| | | setOutputReady: outputReq => |
| | | { |
| | | if (isUpper) |
| | | { |
| | | pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyUpper, outputReq ? 1 : 0); |
| | | } |
| | | else |
| | | { |
| | | pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private static void ProcessDeviceRequest( |
| | | CommonConveyorLine conveyorLine, |
| | | string childDeviceCode, |
| | | Func<bool> getMaterialRequest, |
| | | Func<bool> getOutputRequest, |
| | | Action<bool> setOutputReady) |
| | | /// <summary> |
| | | /// 写入调试日志(同时输出到两个日志系统) |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 统一入口点日志格式,同时向 Microsoft.Extensions.Logging 和 QuartzLogger 写入, |
| | | /// 保证日志既能在控制台查看也能在文件中追溯。 |
| | | /// </remarks> |
| | | /// <param name="conveyorLine">输送线设备对象,用于获取设备编码写入 QuartzLogger</param> |
| | | /// <param name="scenario">场景描述,如"入库下一地址"或"出库下一地址"</param> |
| | | /// <param name="childDeviceCode">子设备编码</param> |
| | | /// <param name="nextAddress">目标设备编码</param> |
| | | private void WriteDebug(CommonConveyorLine conveyorLine, string scenario, string childDeviceCode, string nextAddress) |
| | | { |
| | | bool materialReq = getMaterialRequest(); |
| | | bool outputReq = getOutputRequest(); |
| | | |
| | | if (materialReq) |
| | | { |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Target, 1, childDeviceCode); |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | | } |
| | | else |
| | | { |
| | | setOutputReady(outputReq); |
| | | } |
| | | // 写入结构化日志(可被 Serilog 等日志框架捕获) |
| | | QuartzLogHelper.LogDebug(_logger, "Handle{Scenario}:子设备: {ChildDeviceCode},目标地址: {NextAddress}", $"Handle{scenario}:子设备: {childDeviceCode},目标地址: {nextAddress}", conveyorLine.DeviceCode, scenario, childDeviceCode, nextAddress); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |