syntax = "proto3"; option csharp_namespace = "ZB.MOM.WW.ScadaBridge.Communication.Grpc"; package scadabridge.sitecommand.v1; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; // ───────────────────────────────────────────────────────────────────────────── // Site command plane (central → site) — the gRPC replacement for the per-site // ClusterClient command/control channel handled by SiteCommunicationActor. // // The 28 migrated commands are grouped into six domain RPCs rather than 28 // individual RPCs, because the grouping is what carries the DEADLINE policy: // every command inside one group shares a timeout class today (see // CommunicationOptions), so one RPC per group keeps the deadline choice in one // place on the client and one dispatch switch on the server. A `oneof` request // / reply envelope preserves per-command typing inside the group. // // EVOLUTION RULE (same as sitestream.proto): field numbers are never reused and // changes are additive only. New commands take the next free oneof tag; an // older peer simply sees an unset oneof and answers with a clean error. // // The generated C# is CHECKED IN under Communication/SiteCommandGrpc/ because // protoc segfaults inside the linux_arm64 Docker build image. Regenerate with // docker/regen-proto.sh (or by hand per the recipe in the .csproj) — never by // leaving an active item in the project file. // ───────────────────────────────────────────────────────────────────────────── service SiteCommandService { // Deployment + instance lifecycle (6 commands). rpc ExecuteLifecycle(LifecycleRequest) returns (LifecycleReply); // Interactive OPC UA / MxGateway design-time commands (8 commands). rpc ExecuteOpcUa(OpcUaRequest) returns (OpcUaReply); // Remote read-only queries: event log + debug view (4 commands). rpc ExecuteQuery(QueryRequest) returns (QueryReply); // Parked store-and-forward message + cached-operation actions (5 commands). rpc ExecuteParked(ParkedRequest) returns (ParkedReply); // Inbound-API Route.To() relays (4 commands). rpc ExecuteRoute(RouteRequest) returns (RouteReply); // Operator-initiated manual site-pair failover (1 command). rpc TriggerFailover(TriggerSiteFailoverDto) returns (SiteFailoverAckDto); } // ───────────────────────────────────────────────────────────────────────────── // Shared value carrier // ───────────────────────────────────────────────────────────────────────────── // Explicit null marker. Needed because a map entry always // has a value message present, so "the dictionary holds a null for this key" // cannot be expressed by absence the way a nullable message FIELD can. message LooseNull {} message LooseValueList { repeated LooseValue items = 1; } message LooseValueMap { map entries = 1; } // Type-tagged carrier for the `object?` members the command plane still uses // (script parameters and return values, attribute values, tag read/write // values). The tags cover every CLR type these fields actually carry, so those // values round-trip with their runtime type intact; anything else falls back to // `json_value` (see the mapper's documented lossiness note). // // Date/time and decimal ride as invariant round-trip STRINGS rather than // google.protobuf.Timestamp: Timestamp normalises everything to UTC and would // silently drop DateTime.Kind / DateTimeOffset.Offset, which for an operator's // tag value is data loss, not normalisation. message LooseValue { oneof kind { LooseNull null_value = 1; bool bool_value = 2; int32 int32_value = 3; int64 int64_value = 4; double double_value = 5; float float_value = 6; string string_value = 7; string decimal_value = 8; // invariant-culture round-trip string date_time_value = 9; // DateTime, "O" (preserves Kind) string date_time_offset_value = 10; // DateTimeOffset, "O" (preserves Offset) string guid_value = 11; // "D" string time_span_value = 12; // "c" (constant/round-trip) bytes bytes_value = 13; LooseValueList list_value = 14; LooseValueMap map_value = 15; string json_value = 16; // fallback; decodes to JsonElement } } // ───────────────────────────────────────────────────────────────────────────── // Enums. Every enum reserves 0 for _UNSPECIFIED so a value that is absent on // the wire is never mistaken for the first CLR member. The mapper translates // explicitly in both directions (never by ordinal), so reordering a C# enum // cannot silently re-map the wire. // ───────────────────────────────────────────────────────────────────────────── enum DeploymentStatusDto { DEPLOYMENT_STATUS_DTO_UNSPECIFIED = 0; DEPLOYMENT_STATUS_DTO_PENDING = 1; DEPLOYMENT_STATUS_DTO_IN_PROGRESS = 2; DEPLOYMENT_STATUS_DTO_SUCCESS = 3; DEPLOYMENT_STATUS_DTO_FAILED = 4; } enum BrowseNodeClassDto { BROWSE_NODE_CLASS_DTO_UNSPECIFIED = 0; BROWSE_NODE_CLASS_DTO_OBJECT = 1; BROWSE_NODE_CLASS_DTO_VARIABLE = 2; BROWSE_NODE_CLASS_DTO_METHOD = 3; BROWSE_NODE_CLASS_DTO_OTHER = 4; } enum BrowseFailureKindDto { BROWSE_FAILURE_KIND_DTO_UNSPECIFIED = 0; BROWSE_FAILURE_KIND_DTO_CONNECTION_NOT_FOUND = 1; BROWSE_FAILURE_KIND_DTO_CONNECTION_NOT_CONNECTED = 2; BROWSE_FAILURE_KIND_DTO_NOT_BROWSABLE = 3; BROWSE_FAILURE_KIND_DTO_TIMEOUT = 4; BROWSE_FAILURE_KIND_DTO_SERVER_ERROR = 5; } enum ReadTagValuesFailureKindDto { READ_TAG_VALUES_FAILURE_KIND_DTO_UNSPECIFIED = 0; READ_TAG_VALUES_FAILURE_KIND_DTO_CONNECTION_NOT_FOUND = 1; READ_TAG_VALUES_FAILURE_KIND_DTO_CONNECTION_NOT_CONNECTED = 2; READ_TAG_VALUES_FAILURE_KIND_DTO_TIMEOUT = 3; READ_TAG_VALUES_FAILURE_KIND_DTO_SERVER_ERROR = 4; } enum VerifyFailureKindDto { VERIFY_FAILURE_KIND_DTO_UNSPECIFIED = 0; VERIFY_FAILURE_KIND_DTO_UNREACHABLE = 1; VERIFY_FAILURE_KIND_DTO_AUTH_FAILED = 2; VERIFY_FAILURE_KIND_DTO_UNTRUSTED_CERTIFICATE = 3; VERIFY_FAILURE_KIND_DTO_TIMEOUT = 4; VERIFY_FAILURE_KIND_DTO_SERVER_ERROR = 5; } enum StoreAndForwardCategoryDto { STORE_AND_FORWARD_CATEGORY_DTO_UNSPECIFIED = 0; STORE_AND_FORWARD_CATEGORY_DTO_EXTERNAL_SYSTEM = 1; STORE_AND_FORWARD_CATEGORY_DTO_NOTIFICATION = 2; STORE_AND_FORWARD_CATEGORY_DTO_CACHED_DB_WRITE = 3; } enum AlarmStateDto { ALARM_STATE_DTO_UNSPECIFIED = 0; ALARM_STATE_DTO_ACTIVE = 1; ALARM_STATE_DTO_NORMAL = 2; } enum AlarmLevelDto { ALARM_LEVEL_DTO_UNSPECIFIED = 0; ALARM_LEVEL_DTO_NONE = 1; ALARM_LEVEL_DTO_LOW = 2; ALARM_LEVEL_DTO_LOW_LOW = 3; ALARM_LEVEL_DTO_HIGH = 4; ALARM_LEVEL_DTO_HIGH_HIGH = 5; } enum AlarmKindDto { ALARM_KIND_DTO_UNSPECIFIED = 0; ALARM_KIND_DTO_COMPUTED = 1; ALARM_KIND_DTO_NATIVE_OPC_UA = 2; ALARM_KIND_DTO_NATIVE_MX_ACCESS = 3; } enum AlarmShelveStateDto { ALARM_SHELVE_STATE_DTO_UNSPECIFIED = 0; ALARM_SHELVE_STATE_DTO_UNSHELVED = 1; ALARM_SHELVE_STATE_DTO_ONE_SHOT_SHELVED = 2; ALARM_SHELVE_STATE_DTO_TIMED_SHELVED = 3; ALARM_SHELVE_STATE_DTO_PERMANENT_SHELVED = 4; } // ───────────────────────────────────────────────────────────────────────────── // RPC 1 — ExecuteLifecycle // ───────────────────────────────────────────────────────────────────────────── message RefreshDeploymentCommandDto { string deployment_id = 1; string instance_unique_name = 2; string revision_hash = 3; string deployed_by = 4; google.protobuf.Timestamp timestamp = 5; string central_fetch_base_url = 6; string fetch_token = 7; } message EnableInstanceCommandDto { string command_id = 1; string instance_unique_name = 2; google.protobuf.Timestamp timestamp = 3; } message DisableInstanceCommandDto { string command_id = 1; string instance_unique_name = 2; google.protobuf.Timestamp timestamp = 3; } message DeleteInstanceCommandDto { string command_id = 1; string instance_unique_name = 2; google.protobuf.Timestamp timestamp = 3; } message DeploymentStateQueryRequestDto { string correlation_id = 1; string instance_unique_name = 2; google.protobuf.Timestamp timestamp = 3; } message SharedScriptArtifactDto { string name = 1; string code = 2; string parameter_definitions = 3; // empty string represents null string return_definition = 4; // empty string represents null } message ExternalSystemArtifactDto { string name = 1; string endpoint_url = 2; string auth_type = 3; string auth_configuration = 4; // empty string represents null string method_definitions_json = 5; // empty string represents null int32 timeout_seconds = 6; } message DatabaseConnectionArtifactDto { string name = 1; string connection_string = 2; int32 max_retries = 3; google.protobuf.Duration retry_delay = 4; } message NotificationListArtifactDto { string name = 1; repeated string recipient_emails = 2; } message DataConnectionArtifactDto { string name = 1; string protocol = 2; string primary_configuration_json = 3; // empty string represents null string backup_configuration_json = 4; // empty string represents null int32 failover_retry_count = 5; } message SmtpConfigurationArtifactDto { string name = 1; string server = 2; int32 port = 3; string auth_mode = 4; string from_address = 5; string username = 6; // empty string represents null string password = 7; // empty string represents null string oauth_config = 8; // empty string represents null } // Each artifact collection on DeployArtifactsCommand is a NULLABLE list, and // proto3 `repeated` cannot distinguish null from empty. Wrapping each in its // own message makes the null/empty distinction a message-presence question, // which proto3 does model — the same silent-data-loss class the transport // round-trip guard caught in PLAN-05 T8. message SharedScriptArtifactListDto { repeated SharedScriptArtifactDto items = 1; } message ExternalSystemArtifactListDto { repeated ExternalSystemArtifactDto items = 1; } message DatabaseConnectionArtifactListDto { repeated DatabaseConnectionArtifactDto items = 1; } message NotificationListArtifactListDto { repeated NotificationListArtifactDto items = 1; } message DataConnectionArtifactListDto { repeated DataConnectionArtifactDto items = 1; } message SmtpConfigurationArtifactListDto { repeated SmtpConfigurationArtifactDto items = 1; } message DeployArtifactsCommandDto { string deployment_id = 1; SharedScriptArtifactListDto shared_scripts = 2; // absent => null ExternalSystemArtifactListDto external_systems = 3; // absent => null DatabaseConnectionArtifactListDto database_connections = 4; // absent => null NotificationListArtifactListDto notification_lists = 5; // absent => null DataConnectionArtifactListDto data_connections = 6; // absent => null SmtpConfigurationArtifactListDto smtp_configurations = 7; // absent => null google.protobuf.Timestamp timestamp = 8; } message DeploymentStatusResponseDto { string deployment_id = 1; string instance_unique_name = 2; DeploymentStatusDto status = 3; string error_message = 4; // empty string represents null google.protobuf.Timestamp timestamp = 5; } message InstanceLifecycleResponseDto { string command_id = 1; string instance_unique_name = 2; bool success = 3; string error_message = 4; // empty string represents null google.protobuf.Timestamp timestamp = 5; } message DeploymentStateQueryResponseDto { string correlation_id = 1; string instance_unique_name = 2; bool is_deployed = 3; string applied_deployment_id = 4; // empty string represents null string applied_revision_hash = 5; // empty string represents null google.protobuf.Timestamp timestamp = 6; } message ArtifactDeploymentResponseDto { string deployment_id = 1; string site_id = 2; bool success = 3; string error_message = 4; // empty string represents null google.protobuf.Timestamp timestamp = 5; } message LifecycleRequest { oneof command { RefreshDeploymentCommandDto refresh_deployment = 1; EnableInstanceCommandDto enable_instance = 2; DisableInstanceCommandDto disable_instance = 3; DeleteInstanceCommandDto delete_instance = 4; DeploymentStateQueryRequestDto deployment_state_query = 5; DeployArtifactsCommandDto deploy_artifacts = 6; } } message LifecycleReply { oneof reply { DeploymentStatusResponseDto deployment_status = 1; InstanceLifecycleResponseDto instance_lifecycle = 2; DeploymentStateQueryResponseDto deployment_state_query = 3; ArtifactDeploymentResponseDto artifact_deployment = 4; } } // ───────────────────────────────────────────────────────────────────────────── // RPC 2 — ExecuteOpcUa // ───────────────────────────────────────────────────────────────────────────── message BrowseNodeCommandDto { string connection_name = 1; string parent_node_id = 2; // empty string represents null (browse root) string continuation_token = 3; // empty string represents null (first page) string site_identifier = 4; // empty string represents null } message BrowseNodeDto { string node_id = 1; string display_name = 2; BrowseNodeClassDto node_class = 3; bool has_children = 4; string data_type = 5; // empty string represents null google.protobuf.Int32Value value_rank = 6; // absent => null google.protobuf.BoolValue writable = 7; // absent => null } message BrowseFailureDto { BrowseFailureKindDto kind = 1; string message = 2; } message BrowseNodeResultDto { repeated BrowseNodeDto children = 1; bool truncated = 2; BrowseFailureDto failure = 3; // absent => null (success) string continuation_token = 4; // empty string represents null (final page) } message SearchAddressSpaceCommandDto { string connection_name = 1; string query = 2; int32 max_depth = 3; int32 max_results = 4; string site_identifier = 5; // empty string represents null } message AddressSpaceMatchDto { BrowseNodeDto node = 1; string path = 2; } message SearchAddressSpaceResultDto { repeated AddressSpaceMatchDto matches = 1; bool cap_reached = 2; BrowseFailureDto failure = 3; // absent => null (success) } message ReadTagValuesCommandDto { string connection_name = 1; repeated string tag_paths = 2; } message TagReadOutcomeDto { string tag_path = 1; bool success = 2; LooseValue value = 3; // absent => null string quality = 4; google.protobuf.Timestamp timestamp = 5; string error_message = 6; // empty string represents null } message ReadTagValuesFailureDto { ReadTagValuesFailureKindDto kind = 1; string message = 2; } message ReadTagValuesResultDto { repeated TagReadOutcomeDto outcomes = 1; ReadTagValuesFailureDto failure = 2; // absent => null (success) } message VerifyEndpointCommandDto { string connection_name = 1; string protocol = 2; string config_json = 3; string site_identifier = 4; // empty string represents null } message ServerCertInfoDto { string thumbprint = 1; string subject = 2; string issuer = 3; google.protobuf.Timestamp not_before_utc = 4; google.protobuf.Timestamp not_after_utc = 5; string der_base64 = 6; } // failure_kind is a NULLABLE enum on the CLR side, and proto3 enums have no // presence, so it rides inside a one-field message exactly like Int32Value. message VerifyFailureKindValue { VerifyFailureKindDto value = 1; } message VerifyEndpointResultDto { bool success = 1; VerifyFailureKindValue failure_kind = 2; // absent => null (success) string error = 3; // empty string represents null ServerCertInfoDto cert = 4; // absent => null } message TrustServerCertCommandDto { string connection_name = 1; string der_base64 = 2; string thumbprint = 3; string site_identifier = 4; // empty string represents null } message ListServerCertsCommandDto { string site_identifier = 1; // empty string represents null } message RemoveServerCertCommandDto { string thumbprint = 1; string site_identifier = 2; // empty string represents null } message TrustedCertInfoDto { string thumbprint = 1; string subject = 2; string issuer = 3; google.protobuf.Timestamp not_before_utc = 4; google.protobuf.Timestamp not_after_utc = 5; bool rejected = 6; } // CertTrustResult.Certs is a nullable list (null for trust/remove, populated // for list) — same null-vs-empty problem as the artifact collections. message TrustedCertInfoListDto { repeated TrustedCertInfoDto items = 1; } message CertTrustResultDto { bool success = 1; string error = 2; // empty string represents null TrustedCertInfoListDto certs = 3; // absent => null } message WriteTagRequestDto { string correlation_id = 1; string connection_name = 2; string tag_path = 3; LooseValue value = 4; // absent => null google.protobuf.Timestamp timestamp = 5; } message WriteTagResponseDto { string correlation_id = 1; bool success = 2; string error_message = 3; // empty string represents null google.protobuf.Timestamp timestamp = 4; } message OpcUaRequest { oneof command { BrowseNodeCommandDto browse_node = 1; SearchAddressSpaceCommandDto search_address_space = 2; ReadTagValuesCommandDto read_tag_values = 3; VerifyEndpointCommandDto verify_endpoint = 4; TrustServerCertCommandDto trust_server_cert = 5; ListServerCertsCommandDto list_server_certs = 6; RemoveServerCertCommandDto remove_server_cert = 7; WriteTagRequestDto write_tag = 8; } } message OpcUaReply { oneof reply { BrowseNodeResultDto browse_node = 1; SearchAddressSpaceResultDto search_address_space = 2; ReadTagValuesResultDto read_tag_values = 3; VerifyEndpointResultDto verify_endpoint = 4; CertTrustResultDto cert_trust = 5; WriteTagResponseDto write_tag = 6; } } // ───────────────────────────────────────────────────────────────────────────── // RPC 3 — ExecuteQuery // ───────────────────────────────────────────────────────────────────────────── message EventLogQueryRequestDto { string correlation_id = 1; string site_id = 2; google.protobuf.Timestamp from = 3; // absent => null google.protobuf.Timestamp to = 4; // absent => null string event_type = 5; // empty string represents null string severity = 6; // empty string represents null string instance_id = 7; // empty string represents null string keyword_filter = 8; // empty string represents null string continuation_token = 9; // empty string represents null int32 page_size = 10; google.protobuf.Timestamp timestamp = 11; } message EventLogEntryDto { string id = 1; google.protobuf.Timestamp timestamp = 2; string event_type = 3; string severity = 4; string instance_id = 5; // empty string represents null string source = 6; string message = 7; string details = 8; // empty string represents null } message EventLogQueryResponseDto { string correlation_id = 1; string site_id = 2; repeated EventLogEntryDto entries = 3; string continuation_token = 4; // empty string represents null bool has_more = 5; bool success = 6; string error_message = 7; // empty string represents null google.protobuf.Timestamp timestamp = 8; } message DebugSnapshotRequestDto { string instance_unique_name = 1; string correlation_id = 2; } message SubscribeDebugViewRequestDto { string instance_unique_name = 1; string correlation_id = 2; } message UnsubscribeDebugViewRequestDto { string instance_unique_name = 1; string correlation_id = 2; } // UnsubscribeDebugView is a Tell today (CommunicationService.UnsubscribeDebugView // fires and forgets). A unary RPC must still answer something, so the site sends // this empty ack; the central transport ignores it to keep the caller-visible // fire-and-forget semantics identical. message UnsubscribeDebugViewAckDto {} message AlarmConditionStateDto { bool active = 1; bool acknowledged = 2; google.protobuf.BoolValue confirmed = 3; // absent => null (not confirmable) AlarmShelveStateDto shelve = 4; bool suppressed = 5; int32 severity = 6; } message DebugAttributeValueDto { string instance_unique_name = 1; string attribute_path = 2; string attribute_name = 3; LooseValue value = 4; // absent => null string quality = 5; google.protobuf.Timestamp timestamp = 6; } // Full-fidelity projection of Commons AlarmStateChanged. Deliberately NOT the // sitestream AlarmStateUpdate: that one flattens the value to a display string // and the shelve state to free text, which is right for a live stream but would // lose data on a snapshot the central UI renders as authoritative state. message DebugAlarmStateDto { string instance_unique_name = 1; string alarm_name = 2; AlarmStateDto state = 3; int32 priority = 4; google.protobuf.Timestamp timestamp = 5; AlarmLevelDto level = 6; string message = 7; AlarmKindDto kind = 8; // Absent means "not explicitly set" — the CLR record then derives the // computed default from state + priority. See the mapper's note on why the // encoder omits a condition that already equals that derived default. AlarmConditionStateDto condition = 9; string source_reference = 10; string alarm_type_name = 11; string category = 12; string operator_user = 13; string operator_comment = 14; google.protobuf.Timestamp original_raise_time = 15; // absent => null string current_value = 16; string limit_value = 17; string native_source_canonical_name = 18; bool is_configured_placeholder = 19; } message DebugViewSnapshotDto { string instance_unique_name = 1; repeated DebugAttributeValueDto attribute_values = 2; repeated DebugAlarmStateDto alarm_states = 3; google.protobuf.Timestamp snapshot_timestamp = 4; bool instance_not_found = 5; } message QueryRequest { oneof command { EventLogQueryRequestDto event_log_query = 1; DebugSnapshotRequestDto debug_snapshot = 2; SubscribeDebugViewRequestDto subscribe_debug_view = 3; UnsubscribeDebugViewRequestDto unsubscribe_debug_view = 4; } } message QueryReply { oneof reply { EventLogQueryResponseDto event_log_query = 1; DebugViewSnapshotDto debug_view_snapshot = 2; UnsubscribeDebugViewAckDto unsubscribe_debug_view = 3; } } // ───────────────────────────────────────────────────────────────────────────── // RPC 4 — ExecuteParked // ───────────────────────────────────────────────────────────────────────────── message ParkedMessageQueryRequestDto { string correlation_id = 1; string site_id = 2; int32 page_number = 3; int32 page_size = 4; google.protobuf.Timestamp timestamp = 5; } message ParkedMessageEntryDto { string message_id = 1; string target_system = 2; string method_name = 3; string error_message = 4; int32 attempt_count = 5; google.protobuf.Timestamp original_timestamp = 6; google.protobuf.Timestamp last_attempt_timestamp = 7; int32 max_attempts = 8; StoreAndForwardCategoryDto category = 9; string origin_instance = 10; // empty string represents null } message ParkedMessageQueryResponseDto { string correlation_id = 1; string site_id = 2; repeated ParkedMessageEntryDto messages = 3; int32 total_count = 4; int32 page_number = 5; int32 page_size = 6; bool success = 7; string error_message = 8; // empty string represents null google.protobuf.Timestamp timestamp = 9; } message ParkedMessageRetryRequestDto { string correlation_id = 1; string site_id = 2; string message_id = 3; google.protobuf.Timestamp timestamp = 4; } message ParkedMessageRetryResponseDto { string correlation_id = 1; bool success = 2; string error_message = 3; // empty string represents null } message ParkedMessageDiscardRequestDto { string correlation_id = 1; string site_id = 2; string message_id = 3; google.protobuf.Timestamp timestamp = 4; } message ParkedMessageDiscardResponseDto { string correlation_id = 1; bool success = 2; string error_message = 3; // empty string represents null } message RetryParkedOperationDto { string correlation_id = 1; string tracked_operation_id = 2; // TrackedOperationId GUID, "D" format } message DiscardParkedOperationDto { string correlation_id = 1; string tracked_operation_id = 2; // TrackedOperationId GUID, "D" format } message ParkedOperationActionAckDto { string correlation_id = 1; bool applied = 2; string error_message = 3; // empty string represents null } message ParkedRequest { oneof command { ParkedMessageQueryRequestDto parked_message_query = 1; ParkedMessageRetryRequestDto parked_message_retry = 2; ParkedMessageDiscardRequestDto parked_message_discard = 3; RetryParkedOperationDto retry_parked_operation = 4; DiscardParkedOperationDto discard_parked_operation = 5; } } message ParkedReply { oneof reply { ParkedMessageQueryResponseDto parked_message_query = 1; ParkedMessageRetryResponseDto parked_message_retry = 2; ParkedMessageDiscardResponseDto parked_message_discard = 3; ParkedOperationActionAckDto parked_operation_action = 4; } } // ───────────────────────────────────────────────────────────────────────────── // RPC 5 — ExecuteRoute // ───────────────────────────────────────────────────────────────────────────── message RouteToCallRequestDto { string correlation_id = 1; string instance_unique_name = 2; string script_name = 3; LooseValueMap parameters = 4; // absent => null (distinct from empty) google.protobuf.Timestamp timestamp = 5; string parent_execution_id = 6; // empty string represents null } message RouteToCallResponseDto { string correlation_id = 1; bool success = 2; LooseValue return_value = 3; // absent => null string error_message = 4; // empty string represents null google.protobuf.Timestamp timestamp = 5; } message RouteToGetAttributesRequestDto { string correlation_id = 1; string instance_unique_name = 2; repeated string attribute_names = 3; google.protobuf.Timestamp timestamp = 4; string parent_execution_id = 5; // empty string represents null } message RouteToGetAttributesResponseDto { string correlation_id = 1; LooseValueMap values = 2; // non-nullable on the CLR side; absent => empty bool success = 3; string error_message = 4; // empty string represents null google.protobuf.Timestamp timestamp = 5; } message RouteToSetAttributesRequestDto { string correlation_id = 1; string instance_unique_name = 2; map attribute_values = 3; google.protobuf.Timestamp timestamp = 4; string parent_execution_id = 5; // empty string represents null } message RouteToSetAttributesResponseDto { string correlation_id = 1; bool success = 2; string error_message = 3; // empty string represents null google.protobuf.Timestamp timestamp = 4; } message RouteToWaitForAttributeRequestDto { string correlation_id = 1; string instance_unique_name = 2; string attribute_name = 3; // NOT the empty-string-means-null convention: "wait for this attribute to // become the empty string" is a legitimate target that must stay distinct // from "no target supplied", so this one nullable string rides a wrapper. google.protobuf.StringValue target_value_encoded = 4; google.protobuf.Duration timeout = 5; google.protobuf.Timestamp timestamp = 6; string parent_execution_id = 7; // empty string represents null bool require_good_quality = 8; } message RouteToWaitForAttributeResponseDto { string correlation_id = 1; bool matched = 2; LooseValue value = 3; // absent => null string quality = 4; // empty string represents null bool timed_out = 5; bool success = 6; string error_message = 7; // empty string represents null google.protobuf.Timestamp timestamp = 8; } message RouteRequest { oneof command { RouteToCallRequestDto route_to_call = 1; RouteToGetAttributesRequestDto route_to_get_attributes = 2; RouteToSetAttributesRequestDto route_to_set_attributes = 3; RouteToWaitForAttributeRequestDto route_to_wait_for_attribute = 4; } } message RouteReply { oneof reply { RouteToCallResponseDto route_to_call = 1; RouteToGetAttributesResponseDto route_to_get_attributes = 2; RouteToSetAttributesResponseDto route_to_set_attributes = 3; RouteToWaitForAttributeResponseDto route_to_wait_for_attribute = 4; } } // ───────────────────────────────────────────────────────────────────────────── // RPC 6 — TriggerFailover // ───────────────────────────────────────────────────────────────────────────── message TriggerSiteFailoverDto { string correlation_id = 1; string site_id = 2; } message SiteFailoverAckDto { string correlation_id = 1; bool accepted = 2; string target_address = 3; // empty string represents null string error_message = 4; // empty string represents null }