From a1186685a9f591fb1a27b35fba21ebd7eade5c79 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 17 Jun 2026 11:03:24 -0400 Subject: [PATCH] fix(scriptanalysis): mirror WaitAsync/WaitForAsync on CompileAttributeAccessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the four missing overloads (value + predicate × WaitAsync + WaitForAsync) to CompileAttributeAccessor so template/call scripts that use Attributes.WaitAsync or Attributes.WaitForAsync pass design-time Roslyn validation. Covers both root scope and composed/child scope (Children["x"].Attributes.WaitAsync) automatically since CompileCompositionAccessor.Attributes already returns CompileAttributeAccessor. --- .../ScriptCompileSurface.cs | 8 ++++++++ .../RoslynScriptCompilerTests.cs | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/ZB.MOM.WW.ScadaBridge.ScriptAnalysis/ScriptCompileSurface.cs b/src/ZB.MOM.WW.ScadaBridge.ScriptAnalysis/ScriptCompileSurface.cs index 77e4fecf..03390ae1 100644 --- a/src/ZB.MOM.WW.ScadaBridge.ScriptAnalysis/ScriptCompileSurface.cs +++ b/src/ZB.MOM.WW.ScadaBridge.ScriptAnalysis/ScriptCompileSurface.cs @@ -185,6 +185,14 @@ public sealed class ScriptCompileSurface /// Mirrors AttributeAccessor.Resolve. public string Resolve(string key) => throw new NotSupportedException(CompileOnly); + + /// Mirrors AttributeAccessor.WaitAsync. + public Task WaitAsync(string key, object? targetValue, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly); + public Task WaitAsync(string key, Func predicate, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly); + + /// Mirrors AttributeAccessor.WaitForAsync. + public Task WaitForAsync(string key, object? targetValue, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly); + public Task WaitForAsync(string key, Func predicate, TimeSpan timeout, bool requireGoodQuality = false) => throw new NotSupportedException(CompileOnly); } /// Compile-only mirror of ChildrenAccessor. diff --git a/tests/ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests/RoslynScriptCompilerTests.cs b/tests/ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests/RoslynScriptCompilerTests.cs index e6545685..71ce7812 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests/RoslynScriptCompilerTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests/RoslynScriptCompilerTests.cs @@ -69,4 +69,24 @@ public class RoslynScriptCompilerTests var diagnostics = RoslynScriptCompiler.Compile(expr, typeof(TriggerCompileSurface)); Assert.Empty(diagnostics); } + + [Fact] + public void Compile_Empty_ForWaitAsyncAndWaitForAsync() + { + // Covers all four overloads: value + predicate for both WaitAsync and + // WaitForAsync, on both root scope and composed/child scope. + const string code = """ + // Root scope — value overload + var matched = await Attributes.WaitAsync("Flag", true, System.TimeSpan.FromSeconds(5)); + // Root scope — predicate overload with requireGoodQuality + var matched2 = await Attributes.WaitAsync("Flag", v => v != null, System.TimeSpan.FromSeconds(5), true); + // Root scope — WaitForAsync value overload + var r = await Attributes.WaitForAsync("Flag", true, System.TimeSpan.FromSeconds(5)); + // Composed/child scope — value overload + var childMatched = await Children["LeftMESReceiver"].Attributes.WaitAsync("MoveInCompleteFlag", true, System.TimeSpan.FromSeconds(5)); + """; + + var diagnostics = RoslynScriptCompiler.Compile(code, typeof(ScriptCompileSurface)); + Assert.Empty(diagnostics); + } }