fix(siteruntime): M2.11 — unknown-instance debug snapshot returns InstanceNotFound=true (#24)
RouteDebugSnapshot and RouteDebugViewSubscribe on DeploymentManagerActor
previously returned an empty DebugViewSnapshot for unknown instances,
indistinguishable from a deployed-but-empty instance. Callers had no way
to differentiate "not deployed here" from "deployed, no data yet."
Approach — additive field on existing message contract:
Added `bool InstanceNotFound = false` as an optional trailing parameter
to DebugViewSnapshot (Commons). All existing positional constructor calls
and serialized wire frames are unaffected (default = false). A dedicated
new message type was considered but rejected: the ClusterClient channel
and DebugStreamService TCS are already typed on DebugViewSnapshot, and a
second reply union would require wider changes for zero additive-safety
gain.
Changes:
- Commons/DebugViewSnapshot: add InstanceNotFound = false (additive)
- DeploymentManagerActor: set InstanceNotFound=true in both unknown-
instance branches (RouteDebugViewSubscribe, RouteDebugSnapshot)
- DebugStreamBridgeActor: when snapshot.InstanceNotFound, forward it to
_onEvent (resolves the TCS) then stop cleanly; no gRPC stream opened
- DebugView.razor: check session.InitialSnapshot.InstanceNotFound after
connect and show a clear "not deployed on this site" error toast
- 3 new tests in DeploymentManagerActorTests covering: unknown→snapshot,
unknown→subscribe, known-empty→InstanceNotFound stays false
This commit is contained in:
@@ -2,8 +2,38 @@ using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.DebugView;
|
||||
|
||||
/// <summary>
|
||||
/// Snapshot of an instance's debug state returned in response to a
|
||||
/// <see cref="DebugSnapshotRequest"/> or <see cref="SubscribeDebugViewRequest"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Additive-only contract (M2.11):</b> <see cref="InstanceNotFound"/> is an
|
||||
/// optional trailing parameter with a default of <see langword="false"/> so every
|
||||
/// existing positional constructor call and every existing serialized wire frame
|
||||
/// remains valid. Callers that receive a snapshot with
|
||||
/// <c>InstanceNotFound = true</c> know the instance was unknown on the site and
|
||||
/// should distinguish that from a deployed-but-empty instance
|
||||
/// (<c>InstanceNotFound = false</c>, empty <see cref="AttributeValues"/> and
|
||||
/// <see cref="AlarmStates"/>).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// A new dedicated message type (<c>DebugViewInstanceNotFound</c>) was
|
||||
/// considered but rejected: the ClusterClient / ClusterClientReceptionist
|
||||
/// channel is typed on the request side and the bridge actor is already
|
||||
/// pattern-matching on <c>DebugViewSnapshot</c> for the initial-snapshot TCS
|
||||
/// in <c>DebugStreamService</c>. Introducing a second reply type would require
|
||||
/// every consumer to handle an additional <c>Ask</c> result union — more change
|
||||
/// for no additive-safety gain. The defaulted field is strictly additive and
|
||||
/// keeps all call sites untouched.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public record DebugViewSnapshot(
|
||||
string InstanceUniqueName,
|
||||
IReadOnlyList<AttributeValueChanged> AttributeValues,
|
||||
IReadOnlyList<AlarmStateChanged> AlarmStates,
|
||||
DateTimeOffset SnapshotTimestamp);
|
||||
DateTimeOffset SnapshotTimestamp,
|
||||
// M2.11 — additive field: true when the requested instance is not registered
|
||||
// on this site. Defaults to false so all existing call sites and wire
|
||||
// frames are unaffected.
|
||||
bool InstanceNotFound = false);
|
||||
|
||||
Reference in New Issue
Block a user