using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using TeamAAS.Core; using TeamAAS.Feeder.Interfaces; using TeamAAS.FlowEditor.Models; using TeamAAS.FlowEditor.Plugins; using TeamAAS.Feeder.Models; using Plugins.Feeder.Models; namespace Plugins.Feeder { [Plugin("供料器送料", PluginCategory.硬件模块, typeof(FeederPluginModel), "PackageVariant")] [Serializable] public class FeederPlugin : BasePlugin { public override List DeclareOutputs() => new List { new OutputField("供料器", typeof(string)), new OutputField("流程", typeof(string)), new OutputField("执行步数", typeof(int)), new OutputField("结果", typeof(bool)), }; public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary results) { var res = new Dictionary(); results = res; // 统一失败出口:写 Error + 结果=false + 记 1 级错误日志(任何阈值都落盘) NodeRunStatus Fail(string message) { res["Error"] = message; res["结果"] = false; Log(1, $"供料器送料失败: {message}", TeamAAS.LogLevel.Error); return NodeRunStatus.Failed; } var mgr = Service(); if (mgr == null) return Fail("FeederManager 未初始化"); Log(3, $"开始送料,供料器={Model.FeederName}, 流程={Model.SequenceKey}"); var info = mgr.GetAll().FirstOrDefault(f => string.Equals(f.FeederName, Model.FeederName, StringComparison.OrdinalIgnoreCase)); if (info == null) return Fail($"未找到供料器 \"{Model.FeederName}\""); res["供料器"] = info.FeederName; // SequenceKey 格式: "供料器名/流程名" 或纯 "流程名" var seqName = Model.SequenceKey; var slashIdx = seqName?.IndexOf('/') ?? -1; if (slashIdx >= 0) seqName = seqName.Substring(slashIdx + 1); var seq = info.Sequences.FirstOrDefault(s => string.Equals(s.Name, seqName, StringComparison.OrdinalIgnoreCase)); if (seq == null) return Fail($"供料器 \"{info.FeederName}\" 上不存在流程 \"{seqName}\""); res["流程"] = seq.Name; var enabledSteps = seq.Steps.Where(s => s.Enabled).ToList(); res["执行步数"] = enabledSteps.Count; Log(4, $"流程 \"{seq.Name}\" 启用步骤 {enabledSteps.Count} 个"); var device = mgr.GetOrCreateDevice(info); if (device == null) return Fail("创建设备实例失败"); try { if (!device.IsConnected && Model.AutoConnect) { try { device.ConnectAsync().GetAwaiter().GetResult(); } catch (Exception ex) { return Fail($"连接供料器失败: {ex.Message}"); } } if (!device.IsConnected) return Fail($"供料器 \"{info.FeederName}\" 未连接"); int stepIdx = 0; foreach (var step in enabledSteps) { token.ThrowIfCancellationRequested(); stepIdx++; if (step.StepType == SequenceStepType.Delay) { Log(4, $"第{stepIdx}步: 延时 {step.TimeMs}ms"); Task.Delay(step.TimeMs, token).Wait(token); } else { try { Log(4, $"第{stepIdx}步: 方向={step.Direction}, 时长={step.TimeMs}ms"); device.RunDirectionAsync(step.Direction, step.TimeMs) .GetAwaiter().GetResult(); } catch (Exception ex) { return Fail($"第{stepIdx}步执行失败: {ex.Message}"); } } } if (Model.StopWhenDone) { try { device.StopAsync().GetAwaiter().GetResult(); } catch { } } res["结果"] = true; Log(2, $"送料完成,供料器={info.FeederName}, 流程={seq.Name}, 执行 {enabledSteps.Count} 步"); return NodeRunStatus.Success; } catch (OperationCanceledException) { try { device.StopAsync().GetAwaiter().GetResult(); } catch { } return Fail("用户取消"); } catch (Exception ex) { try { device.StopAsync().GetAwaiter().GetResult(); } catch { } return Fail(ex.Message); } } } }