probe: InitializeConsumer required — provider visible after, alarms still absent
InitializeConsumer was the missing call. Adding it before
RegisterConsumer makes the \Galaxy! provider appear in
GetProviders (status 0 -> 100 within 500ms). Without Initialize,
GetProviders returns an empty list even though everything else
returns rc=0 (success).
Probe trace 2026-05-01:
InitializeConsumer -> 0
RegisterConsumer -> 0
GetProviders [after Register] -> count=0 list=[]
Subscribe('\Galaxy!') -> 0
GetProviders [after Subscribe] -> count=1 list=[ 0 \Galaxy!]
GetProviders [poll #1] -> count=1 list=[100 \Galaxy!]
Despite the provider being at "100% query complete" for the
entire 60s window, GetStatistics continued to report
total=0 active=0 codes=[7] -- no alarm transitions reached the
consumer even with a System Platform script flipping
TestMachine_001.TestAlarm001 every 10s during the run.
So the consumer chain works end-to-end. What's missing is alarm
traffic from the producer side. The next discriminator is
whether ObjectViewer (or another live consumer) sees the alarm
fire while the script runs.
API-ordering bug fix to apply to PR A.5's AlarmClientConsumer
regardless of how A.2 lands: AlarmClientConsumer.Subscribe
should call InitializeConsumer before RegisterConsumer (currently
omits Initialize entirely, which means the provider chain is
never visible from the worker either). That fix lifts a
fundamental bug independent of the polling-vs-callback question.
Probe changes:
- Added InitializeConsumer call before RegisterConsumer.
- Added LogProviders helper that logs only on change; called
after Register, after Subscribe, and on every poll. Easier
to spot when the provider chain transitions from empty to
populated.
- Restored Skip-gating after run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -196,6 +196,46 @@ absence.
|
||||
gateway-worker installation runs under a service account
|
||||
that may have access where `dotnet test` doesn't.
|
||||
|
||||
## InitializeConsumer required — fourth probe run, 2026-05-01
|
||||
|
||||
Adding `InitializeConsumer("AlarmProbe.Tests")` before
|
||||
`RegisterConsumer` made `\Galaxy!` appear in `GetProviders`
|
||||
(count=1, status 0 → 100 within 500ms). So #2 and #3 above are
|
||||
NOT the cause — the consumer can see the alarm provider once it
|
||||
calls Initialize. That's a missing API-call ordering, not a
|
||||
permission or service issue.
|
||||
|
||||
```
|
||||
InitializeConsumer -> 0
|
||||
RegisterConsumer -> 0
|
||||
GetProviders [after Register] -> rc=0 count=0 list=[]
|
||||
Subscribe('\Galaxy!') -> 0
|
||||
GetProviders [after Subscribe] -> rc=0 count=1 list=[ 0 \Galaxy!]
|
||||
GetProviders [poll #1] -> rc=0 count=1 list=[100 \Galaxy!]
|
||||
```
|
||||
|
||||
Despite the provider being visible at "100% query complete" for
|
||||
the entire 60s window, `GetStatistics` continued to report
|
||||
`total=0 active=0 codes=[7]` — no alarm transitions reached the
|
||||
consumer even with a System Platform script flipping the test
|
||||
boolean every 10s during the run.
|
||||
|
||||
That isolates the remaining unknown to whether the test bool's
|
||||
alarm extension is actually generating MxAccess alarm-provider
|
||||
events when its value flips. The probe has confirmed every link
|
||||
in the consumer chain works (Initialize → Register → Subscribe →
|
||||
provider visible at 100%) — what's missing is alarm traffic from
|
||||
the producer side. ObjectViewer or another live consumer running
|
||||
alongside the script is the next discriminator: does it visibly
|
||||
see the alarm fire?
|
||||
|
||||
API-ordering finding: `InitializeConsumer` MUST precede
|
||||
`RegisterConsumer` (or at least, must be called before
|
||||
`GetProviders` returns anything). PR A.5's `AlarmClientConsumer`
|
||||
omits `InitializeConsumer` entirely — that's a bug fix to apply
|
||||
even before A.2 lands, since without it the provider chain never
|
||||
becomes visible.
|
||||
|
||||
### Implications for A.2 implementation
|
||||
|
||||
The A.2 PR's value is unmeasurable until at least one alarm
|
||||
|
||||
@@ -231,6 +231,22 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
try
|
||||
{
|
||||
client = new AlarmClient();
|
||||
|
||||
// Try InitializeConsumer first — separate from RegisterConsumer
|
||||
// per the discovered API surface; previous probe runs skipped
|
||||
// it. Some AVEVA managed-client patterns require Initialize
|
||||
// before Register; others reverse the order. Try Initialize
|
||||
// first; on failure proceed to Register.
|
||||
try
|
||||
{
|
||||
int init = client.InitializeConsumer("AlarmProbe.Tests");
|
||||
Log($"InitializeConsumer -> {init}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"InitializeConsumer threw: {ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
|
||||
int register = client.RegisterConsumer(
|
||||
hWnd: probeWindow.ToInt32(),
|
||||
szProductName: "AlarmProbe",
|
||||
@@ -239,18 +255,7 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
bRetainHiddenAlarms: false);
|
||||
Log($"RegisterConsumer -> {register}");
|
||||
|
||||
// Discover what providers AVEVA sees before subscribing, so we
|
||||
// can spot a wrong subscription expression up front.
|
||||
try
|
||||
{
|
||||
var providers = new System.Collections.Generic.List<string>();
|
||||
int gp = client.GetProviders(providers);
|
||||
Log($"GetProviders -> rc={gp} count={providers.Count} list=[{string.Join(", ", providers)}]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"GetProviders threw: {ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
LogProviders(client, "after Register");
|
||||
|
||||
int subscribe = client.Subscribe(
|
||||
szSubscription: SubscriptionExpression,
|
||||
@@ -261,6 +266,8 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
FilterSpecification: eAlarmFilterState.asNone);
|
||||
Log($"Subscribe('{SubscriptionExpression}') -> {subscribe}");
|
||||
|
||||
LogProviders(client, "after Subscribe");
|
||||
|
||||
// 3c. Pump for the configured duration. Log every message we see
|
||||
// (filtered light to avoid noise from WM_PAINT / WM_TIMER /
|
||||
// WM_GETICON spam from typical pumps). Poll GetStatistics on
|
||||
@@ -290,6 +297,7 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
if (DateTime.UtcNow >= nextPoll)
|
||||
{
|
||||
PollGetStatistics(client, ++pollCount);
|
||||
LogProviders(client, $"poll #{pollCount}");
|
||||
nextPoll = DateTime.UtcNow + PollInterval;
|
||||
}
|
||||
Thread.Sleep(10);
|
||||
@@ -316,6 +324,26 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
}
|
||||
|
||||
private string lastStatsSummary = string.Empty;
|
||||
private string lastProvidersSummary = string.Empty;
|
||||
|
||||
private void LogProviders(AlarmClient client, string when)
|
||||
{
|
||||
try
|
||||
{
|
||||
var providers = new System.Collections.Generic.List<string>();
|
||||
int rc = client.GetProviders(providers);
|
||||
string summary = $"count={providers.Count} list=[{string.Join(", ", providers)}]";
|
||||
if (summary != lastProvidersSummary)
|
||||
{
|
||||
Log($"GetProviders [{when}] -> rc={rc} {summary} (changed)");
|
||||
lastProvidersSummary = summary;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"GetProviders [{when}] threw: {ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drive an MxAccess write to <see cref="TriggerTagReference"/> with the
|
||||
|
||||
Reference in New Issue
Block a user