syntax = "proto3";

package galaxy_repository.v1;

option csharp_namespace = "ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy";

import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";

// Wire-compatibility policy (ProtobufStyleGuide): this contract evolves
// 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.

// Read-only browse over the AVEVA System Platform Galaxy Repository (ZB SQL
// database). Lets clients enumerate the deployed object hierarchy and each
// object's dynamic attributes so they know what tag references to subscribe
// to via the MxAccessGateway service.
service GalaxyRepository {
  rpc TestConnection(TestConnectionRequest) returns (TestConnectionReply);
  rpc GetLastDeployTime(GetLastDeployTimeRequest) returns (GetLastDeployTimeReply);
  rpc DiscoverHierarchy(DiscoverHierarchyRequest) returns (DiscoverHierarchyReply);

  // Server-stream of deploy events. The server emits the current state immediately
  // on subscribe (so clients can bootstrap their cache without waiting for the next
  // deploy), then emits one event each time the gateway's hierarchy cache observes
  // a new galaxy.time_of_last_deploy. The sequence field is monotonically
  // increasing per server start; gaps indicate the per-subscriber buffer dropped
  // older events because the client was too slow.
  rpc WatchDeployEvents(WatchDeployEventsRequest) returns (stream DeployEvent);

  // Returns the direct children of a parent object (or the root objects when
  // `parent` is unset). Designed for OPC UA-style lazy expand: clients walk
  // one level at a time instead of paging the full hierarchy. Filters mirror
  // DiscoverHierarchy exactly. Backed by the same shared hierarchy cache.
  rpc BrowseChildren(BrowseChildrenRequest) returns (BrowseChildrenReply);
}

message TestConnectionRequest {}

message TestConnectionReply {
  bool ok = 1;
}

message GetLastDeployTimeRequest {}

message GetLastDeployTimeReply {
  bool present = 1;
  google.protobuf.Timestamp time_of_last_deploy = 2;
}

message DiscoverHierarchyRequest {
  // Maximum number of objects to return. The server applies its default when
  // unset and rejects non-positive values.
  int32 page_size = 1;
  // Opaque token returned by a previous DiscoverHierarchy response.
  string page_token = 2;
  // Optional. When set, return only this object and its descendants.
  // Empty = full hierarchy.
  oneof root {
    int32 root_gobject_id = 3;
    string root_tag_name = 4;
    string root_contained_path = 5;
  }
  // Optional. Cap on descendant depth from root. Zero returns only the root.
  // Unset means unlimited depth.
  google.protobuf.Int32Value max_depth = 6;
  // Optional object category id filters.
  repeated int32 category_ids = 7;
  // Optional case-insensitive substring filters against template names.
  repeated string template_chain_contains = 8;
  // Optional anchored, case-insensitive glob over object tag_name.
  string tag_name_glob = 9;
  // Optional. Unset or true includes attributes. False returns object skeletons.
  optional bool include_attributes = 10;
  // Optional. Return only objects with at least one alarm-bearing attribute.
  bool alarm_bearing_only = 11;
  // Optional. Return only objects with at least one historized attribute.
  bool historized_only = 12;
}

message DiscoverHierarchyReply {
  repeated GalaxyObject objects = 1;
  // Non-empty when another page is available.
  string next_page_token = 2;
  // Total number of objects in the cached hierarchy at the time of the call.
  int32 total_object_count = 3;
}

message WatchDeployEventsRequest {
  // Optional. When set, the bootstrap event is suppressed if the cached deploy
  // time matches this value. Future events are still emitted normally.
  google.protobuf.Timestamp last_seen_deploy_time = 1;
}

message DeployEvent {
  // Monotonically increasing per server start. Gaps indicate dropped events.
  uint64 sequence = 1;
  // Server wall-clock when the cache observed the deploy.
  google.protobuf.Timestamp observed_at = 2;
  // Galaxy.time_of_last_deploy. Absent only when the Galaxy table reports null.
  google.protobuf.Timestamp time_of_last_deploy = 3;
  bool time_of_last_deploy_present = 4;
  int32 object_count = 5;
  int32 attribute_count = 6;
}

message GalaxyObject {
  int32 gobject_id = 1;
  string tag_name = 2;
  string contained_name = 3;
  string browse_name = 4;
  int32 parent_gobject_id = 5;
  bool is_area = 6;
  int32 category_id = 7;
  int32 hosted_by_gobject_id = 8;
  repeated string template_chain = 9;
  repeated GalaxyAttribute attributes = 10;
}

message GalaxyAttribute {
  string attribute_name = 1;
  string full_tag_reference = 2;
  // Raw Galaxy SQL `dbo.data_type` identifier, passed through unchanged.
  // This is NOT a member of `mxaccess_gateway.v1.MxDataType` — Galaxy's
  // type enumeration is distinct from MXAccess's wire data-type enum and
  // the two must not be cast or compared. The GalaxyRepository service is
  // metadata-only and deliberately does not share types with
  // mxaccess_gateway.proto. See docs/GalaxyRepository.md.
  int32 mx_data_type = 3;
  // Human-readable name from Galaxy's `dbo.data_type` table (e.g. "Float",
  // "Integer", "Boolean"). Free-form Galaxy text; not a stable enum.
  string data_type_name = 4;
  bool is_array = 5;
  int32 array_dimension = 6;
  bool array_dimension_present = 7;
  // Raw Galaxy SQL attribute-category identifier, passed through unchanged.
  // Galaxy-specific; not mapped to any gateway enum. See
  // docs/GalaxyRepository.md.
  int32 mx_attribute_category = 8;
  // Raw Galaxy SQL security-classification identifier, passed through
  // unchanged. Galaxy-specific; not mapped to any gateway enum. See
  // docs/GalaxyRepository.md.
  int32 security_classification = 9;
  bool is_historized = 10;
  bool is_alarm = 11;
}

message BrowseChildrenRequest {
  // Parent selector. Empty oneof returns root objects (parent_gobject_id == 0).
  oneof parent {
    int32 parent_gobject_id = 1;
    string parent_tag_name = 2;
    string parent_contained_path = 3;
  }

  // Maximum number of direct children to return. Server default 500; cap 5000.
  int32 page_size = 4;
  // Opaque token returned by a previous BrowseChildren response. Bound to the
  // cache sequence, parent selector, and the filter set; a mismatch returns
  // InvalidArgument.
  string page_token = 5;

  // --- Filter parity with DiscoverHierarchy. AND-combined. ---
  repeated int32 category_ids = 6;
  repeated string template_chain_contains = 7;
  string tag_name_glob = 8;
  optional bool include_attributes = 9;
  bool alarm_bearing_only = 10;
  bool historized_only = 11;
}

message BrowseChildrenReply {
  // Direct children matching the filter, sorted areas-first then by
  // case-insensitive display name (same order as the dashboard tree).
  repeated GalaxyObject children = 1;
  // Non-empty when another page of siblings is available.
  string next_page_token = 2;
  // Total matching direct children of the parent (post-filter).
  int32 total_child_count = 3;
  // Parallel array, indexed with `children`. True when the child has at least
  // one matching descendant under the same filter set. Lets a UI choose
  // whether to draw an expand triangle without an extra round trip.
  repeated bool child_has_children = 4;
  // Cache sequence this reply was projected from. Clients may pass it back as
  // part of the page_token contract. Mismatch on the next page -> InvalidArgument.
  uint64 cache_sequence = 5;
}
