Add gateway central alarm monitor and StreamAlarms feed
The gateway now monitors alarms continuously, independent of any client session, and fans the feed out to every client. GatewayAlarmMonitor is an always-on hosted service that owns one gateway-managed worker session dedicated to alarms: it subscribes the configured provider, caches the active-alarm set from the worker's transition events (reconciled periodically against the worker's authoritative snapshot), re-opens the session if the worker faults, and broadcasts to all subscribers. The new session-less StreamAlarms RPC opens with the current active-alarm snapshot, then streams live transitions; any number of clients fan out from the single monitor without opening a worker session. AcknowledgeAlarm is now session-less and routes through the monitor. The session-scoped QueryActiveAlarms RPC and the per-session alarm auto-subscribe hook are removed, along with the now-dead IAlarmRpcDispatcher trio; the dashboard Alarms tab reads the monitor's in-process cache directly. This intentionally reverses the v1 "no multi-subscriber fan-out" decision for the alarm subsystem. Contracts regenerated; gateway, dashboard and tests build clean, 94 alarm-affected tests pass, and the monitor is verified live. Language-client stubs are regenerated in a follow-up change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,7 @@ import "google/protobuf/timestamp.proto";
|
||||
// additively only. Never renumber or repurpose an existing field number or
|
||||
// enum value. When a field or enum value is removed, add a `reserved` range
|
||||
// (and `reserved` name) covering it in the same change so a future editor
|
||||
// cannot accidentally reuse the retired tag. There are no `reserved`
|
||||
// declarations today because no field or enum value has ever been removed.
|
||||
// cannot accidentally reuse the retired tag.
|
||||
|
||||
// Public client API for MXAccess sessions hosted by the gateway.
|
||||
service MxAccessGateway {
|
||||
@@ -21,7 +20,12 @@ service MxAccessGateway {
|
||||
rpc Invoke(MxCommandRequest) returns (MxCommandReply);
|
||||
rpc StreamEvents(StreamEventsRequest) returns (stream MxEvent);
|
||||
rpc AcknowledgeAlarm(AcknowledgeAlarmRequest) returns (AcknowledgeAlarmReply);
|
||||
rpc QueryActiveAlarms(QueryActiveAlarmsRequest) returns (stream ActiveAlarmSnapshot);
|
||||
// Session-less central alarm feed. The stream opens with the current
|
||||
// active-alarm snapshot (one `active_alarm` per alarm), then a single
|
||||
// `snapshot_complete`, then a `transition` for every subsequent change.
|
||||
// Served by the gateway's always-on alarm monitor; any number of clients
|
||||
// fan out from the single monitor without opening a worker session.
|
||||
rpc StreamAlarms(StreamAlarmsRequest) returns (stream AlarmFeedMessage);
|
||||
}
|
||||
|
||||
message OpenSessionRequest {
|
||||
@@ -785,7 +789,10 @@ enum AlarmConditionState {
|
||||
}
|
||||
|
||||
message AcknowledgeAlarmRequest {
|
||||
string session_id = 1;
|
||||
// Retired: acknowledgement is session-less — it routes to the gateway's
|
||||
// central alarm monitor, not a client worker session.
|
||||
reserved 1;
|
||||
reserved "session_id";
|
||||
string client_correlation_id = 2;
|
||||
// Fully-qualified alarm reference matching OnAlarmTransitionEvent.alarm_full_reference.
|
||||
string alarm_full_reference = 3;
|
||||
@@ -797,7 +804,9 @@ message AcknowledgeAlarmRequest {
|
||||
}
|
||||
|
||||
message AcknowledgeAlarmReply {
|
||||
string session_id = 1;
|
||||
// Retired: see AcknowledgeAlarmRequest — acknowledgement is session-less.
|
||||
reserved 1;
|
||||
reserved "session_id";
|
||||
string correlation_id = 2;
|
||||
ProtocolStatus protocol_status = 3;
|
||||
// Native ack return code echoed from the worker. The worker carries the
|
||||
@@ -816,12 +825,27 @@ message AcknowledgeAlarmReply {
|
||||
string diagnostic_message = 6;
|
||||
}
|
||||
|
||||
message QueryActiveAlarmsRequest {
|
||||
string session_id = 1;
|
||||
string client_correlation_id = 2;
|
||||
// Optional alarm-reference prefix used to scope a partial ConditionRefresh
|
||||
// (e.g. equipment sub-tree). Empty means full refresh.
|
||||
string alarm_filter_prefix = 3;
|
||||
// Request to attach to the gateway's central alarm feed (StreamAlarms).
|
||||
message StreamAlarmsRequest {
|
||||
string client_correlation_id = 1;
|
||||
// Optional alarm-reference prefix scoping the feed to an equipment
|
||||
// sub-tree. Empty streams every active alarm.
|
||||
string alarm_filter_prefix = 2;
|
||||
}
|
||||
|
||||
// One message on the StreamAlarms feed. The stream opens with one
|
||||
// `active_alarm` per currently-active alarm, then a single
|
||||
// `snapshot_complete`, then a `transition` for every subsequent change.
|
||||
message AlarmFeedMessage {
|
||||
oneof payload {
|
||||
// Part of the initial active-alarm snapshot (ConditionRefresh).
|
||||
ActiveAlarmSnapshot active_alarm = 1;
|
||||
// Sentinel: the initial snapshot is fully delivered and `transition`
|
||||
// messages follow. Always true when present.
|
||||
bool snapshot_complete = 2;
|
||||
// A live alarm state change (raise / acknowledge / clear).
|
||||
OnAlarmTransitionEvent transition = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message MxStatusProxy {
|
||||
|
||||
Reference in New Issue
Block a user