9c631b47e1
Adds an additive, site-wide, alarm-only gRPC stream backing the aggregated
Alarm Summary. Proto: new SiteStreamRequest { correlation_id } message + rpc
SubscribeSite(SiteStreamRequest) returns (stream SiteStreamEvent) on
SiteStreamService — purely additive, no field renumbering. Regenerated the
checked-in SiteStreamGrpc/*.cs.
Server: SubscribeInstance and the new SubscribeSite now delegate to a shared
RunSubscriptionStreamAsync helper (readiness/shutdown guards, correlation-id
validation, duplicate replacement, concurrency cap, bounded DropOldest channel,
relay actor, SiteConnectionOpened/Closed telemetry, guaranteed cleanup). The
only variation is the subscribe delegate: SubscribeSite calls
ISiteStreamSubscriber.SubscribeSiteAlarms (no per-instance filter). Added
SubscribeSiteAlarms to the ISiteStreamSubscriber contract (SiteStreamManager
already implements it from T1). StreamRelayActor reused unchanged — it already
drops IsConfiguredPlaceholder rows and maps the enriched AlarmStateUpdate.
Tests: SubscribeSite subscribes site alarms + removes on cancel, rejects unsafe
correlation ids, and relays a domain AlarmStateChanged as a proto
AlarmStateUpdate on the stream.
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
199 lines
7.8 KiB
Protocol Buffer
199 lines
7.8 KiB
Protocol Buffer
syntax = "proto3";
|
|
option csharp_namespace = "ZB.MOM.WW.ScadaBridge.Communication.Grpc";
|
|
package sitestream;
|
|
|
|
import "google/protobuf/timestamp.proto";
|
|
import "google/protobuf/wrappers.proto"; // Int32Value
|
|
|
|
service SiteStreamService {
|
|
rpc SubscribeInstance(InstanceStreamRequest) returns (stream SiteStreamEvent);
|
|
// Site-wide, alarm-only live stream (aggregated Alarm Summary): every
|
|
// AlarmStateChanged for ALL instances on the site, no per-instance filter.
|
|
// Attribute updates are never carried on this stream.
|
|
rpc SubscribeSite(SiteStreamRequest) returns (stream SiteStreamEvent);
|
|
rpc IngestAuditEvents(AuditEventBatch) returns (IngestAck);
|
|
rpc IngestCachedTelemetry(CachedTelemetryBatch) returns (IngestAck);
|
|
rpc PullAuditEvents(PullAuditEventsRequest) returns (PullAuditEventsResponse);
|
|
rpc PullSiteCalls(PullSiteCallsRequest) returns (PullSiteCallsResponse);
|
|
}
|
|
|
|
message InstanceStreamRequest {
|
|
string correlation_id = 1;
|
|
string instance_unique_name = 2;
|
|
}
|
|
|
|
// Request for the site-wide, alarm-only SubscribeSite stream. Unlike
|
|
// InstanceStreamRequest there is NO instance filter — the stream carries alarm
|
|
// transitions for every instance on the site.
|
|
message SiteStreamRequest {
|
|
string correlation_id = 1;
|
|
}
|
|
|
|
message SiteStreamEvent {
|
|
string correlation_id = 1;
|
|
oneof event {
|
|
AttributeValueUpdate attribute_changed = 2;
|
|
AlarmStateUpdate alarm_changed = 3;
|
|
}
|
|
}
|
|
|
|
enum Quality {
|
|
QUALITY_UNSPECIFIED = 0;
|
|
QUALITY_GOOD = 1;
|
|
QUALITY_UNCERTAIN = 2;
|
|
QUALITY_BAD = 3;
|
|
}
|
|
|
|
enum AlarmStateEnum {
|
|
ALARM_STATE_UNSPECIFIED = 0;
|
|
ALARM_STATE_NORMAL = 1;
|
|
ALARM_STATE_ACTIVE = 2;
|
|
}
|
|
|
|
// Severity level for an active alarm. Binary trigger types (ValueMatch,
|
|
// RangeViolation, RateOfChange) always emit ALARM_LEVEL_NONE. The HiLo
|
|
// trigger type emits one of the directional values.
|
|
enum AlarmLevelEnum {
|
|
ALARM_LEVEL_NONE = 0;
|
|
ALARM_LEVEL_LOW = 1;
|
|
ALARM_LEVEL_LOW_LOW = 2;
|
|
ALARM_LEVEL_HIGH = 3;
|
|
ALARM_LEVEL_HIGH_HIGH = 4;
|
|
}
|
|
|
|
message AttributeValueUpdate {
|
|
string instance_unique_name = 1;
|
|
string attribute_path = 2;
|
|
string attribute_name = 3;
|
|
string value = 4;
|
|
Quality quality = 5;
|
|
google.protobuf.Timestamp timestamp = 6;
|
|
}
|
|
|
|
message AlarmStateUpdate {
|
|
string instance_unique_name = 1;
|
|
string alarm_name = 2;
|
|
AlarmStateEnum state = 3;
|
|
int32 priority = 4;
|
|
google.protobuf.Timestamp timestamp = 5;
|
|
AlarmLevelEnum level = 6; // ALARM_LEVEL_NONE for binary trigger types; set by HiLo.
|
|
string message = 7; // Optional per-band operator message; empty when unset.
|
|
|
|
// Native alarm enrichment (additive — computed alarms leave these at defaults).
|
|
// kind: "Computed" | "NativeOpcUa" | "NativeMxAccess".
|
|
string kind = 8;
|
|
bool active = 9; // unified condition: active vs inactive
|
|
bool acknowledged = 10; // acked vs unacked
|
|
bool confirmed = 11; // confirmed (false when not confirmable)
|
|
string shelve_state = 12; // Unshelved | OneShotShelved | TimedShelved | PermanentShelved
|
|
bool suppressed = 13;
|
|
string source_reference = 14; // native per-condition key; empty for computed
|
|
string alarm_type_name = 15;
|
|
string category = 16;
|
|
string operator_user = 17;
|
|
string operator_comment = 18;
|
|
google.protobuf.Timestamp original_raise_time = 19; // null when unknown
|
|
string current_value = 20;
|
|
string limit_value = 21;
|
|
string native_source_canonical_name = 22; // native binding canonical name; empty for computed
|
|
bool is_configured_placeholder = 23; // true for a quiet-binding placeholder row
|
|
}
|
|
|
|
// Audit Log (#23) telemetry: single lifecycle event ferried from a site SQLite
|
|
// hot-path row to central via IngestAuditEvents. Mirrors AuditEvent (Commons)
|
|
// minus the site-local ForwardState and the central IngestedAtUtc (set on ingest).
|
|
message AuditEventDto {
|
|
string event_id = 1;
|
|
google.protobuf.Timestamp occurred_at_utc = 2;
|
|
string channel = 3;
|
|
string kind = 4;
|
|
string correlation_id = 5; // empty string represents null
|
|
string source_site_id = 6;
|
|
string source_instance_id = 7;
|
|
string source_script = 8;
|
|
string actor = 9;
|
|
string target = 10;
|
|
string status = 11;
|
|
google.protobuf.Int32Value http_status = 12; // null when absent
|
|
google.protobuf.Int32Value duration_ms = 13;
|
|
string error_message = 14;
|
|
string error_detail = 15;
|
|
string request_summary = 16;
|
|
string response_summary = 17;
|
|
bool payload_truncated = 18;
|
|
string extra = 19;
|
|
string execution_id = 20; // empty string represents null
|
|
string parent_execution_id = 21; // empty string represents null
|
|
string source_node = 22; // empty string represents null
|
|
}
|
|
|
|
message AuditEventBatch { repeated AuditEventDto events = 1; }
|
|
message IngestAck { repeated string accepted_event_ids = 1; }
|
|
|
|
// Audit Log (#23) M3 cached-call combined telemetry: a single packet carries
|
|
// both the AuditEvent row to insert and the SiteCalls operational-state upsert
|
|
// for one lifecycle event of a cached outbound call. Central writes both rows
|
|
// in one MS SQL transaction so the audit and operational mirrors never drift.
|
|
message SiteCallOperationalDto {
|
|
string tracked_operation_id = 1; // GUID string ("D" format)
|
|
string channel = 2; // "ApiOutbound" | "DbOutbound"
|
|
string target = 3;
|
|
string source_site = 4;
|
|
string status = 5; // AuditStatus name
|
|
int32 retry_count = 6;
|
|
string last_error = 7; // empty when null
|
|
google.protobuf.Int32Value http_status = 8;
|
|
google.protobuf.Timestamp created_at_utc = 9;
|
|
google.protobuf.Timestamp updated_at_utc = 10;
|
|
google.protobuf.Timestamp terminal_at_utc = 11; // absent when not terminal
|
|
string source_node = 12; // empty string represents null
|
|
}
|
|
|
|
message CachedTelemetryPacket {
|
|
AuditEventDto audit_event = 1;
|
|
SiteCallOperationalDto operational = 2;
|
|
}
|
|
|
|
message CachedTelemetryBatch { repeated CachedTelemetryPacket packets = 1; }
|
|
|
|
// Audit Log (#23) M6 reconciliation pull: central→site request for any
|
|
// site-local AuditLog rows with OccurredAtUtc >= since_utc that have not yet
|
|
// been ingested centrally (ForwardState in {Pending, Forwarded}). The site
|
|
// flips returned rows to Reconciled after the response is on the wire.
|
|
// more_available signals batch_size was saturated so the caller knows to
|
|
// issue a follow-up pull with an advanced since_utc cursor.
|
|
message PullAuditEventsRequest {
|
|
google.protobuf.Timestamp since_utc = 1;
|
|
int32 batch_size = 2;
|
|
}
|
|
|
|
message PullAuditEventsResponse {
|
|
repeated AuditEventDto events = 1;
|
|
bool more_available = 2;
|
|
}
|
|
|
|
// Site Call Audit (#22) reconciliation pull: central→site request for any
|
|
// site-local operation-tracking rows whose UpdatedAtUtc >= since_utc — the
|
|
// self-heal feed that backfills the eventually-consistent central SiteCalls
|
|
// mirror when best-effort push telemetry is lost. Mirrors PullAuditEvents
|
|
// but is a SEPARATE RPC (the tracking store is the operational source of
|
|
// truth, distinct from the site audit queue). more_available signals
|
|
// batch_size was saturated so the caller advances since_utc and pulls again.
|
|
message PullSiteCallsRequest {
|
|
google.protobuf.Timestamp since_utc = 1;
|
|
int32 batch_size = 2;
|
|
// Composite-keyset cursor (Task 15): the TrackedOperationId ("D" GUID form) of
|
|
// the last row already consumed at since_utc. When set, the site returns only
|
|
// rows strictly after (since_utc, after_id) under a deterministic
|
|
// (UpdatedAtUtc, TrackedOperationId) order — un-pinning a batch that would
|
|
// otherwise stall when more than batch_size rows share one since_utc instant.
|
|
// Empty (the proto3 string default) preserves the legacy inclusive >= behaviour,
|
|
// so an older central that never sets it is unaffected. Additive-only.
|
|
string after_id = 3;
|
|
}
|
|
|
|
message PullSiteCallsResponse {
|
|
repeated SiteCallOperationalDto operationals = 1;
|
|
bool more_available = 2;
|
|
}
|