using ZB.MOM.WW.OtOpcUa.Configuration.Enums; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns; /// /// A UNS area projected for editing: its operator-editable fields plus the owning cluster and /// the concurrency token the edit modal must echo back on save. /// /// The area's stable id (read-only on edit). /// The area name. /// Optional notes; null when unset. /// The owning cluster id (the served-by selection). /// The optimistic-concurrency token last read. public sealed record AreaEditDto(string UnsAreaId, string Name, string? Notes, string ClusterId, byte[] RowVersion); /// /// A UNS line projected for editing: its operator-editable fields plus the parent area and the /// concurrency token the edit modal must echo back on save. /// /// The line's stable id (read-only on edit). /// The owning area id (the parent-area selection). /// The line name. /// Optional notes; null when unset. /// The optimistic-concurrency token last read. public sealed record LineEditDto(string UnsLineId, string UnsAreaId, string Name, string? Notes, byte[] RowVersion); /// /// An equipment projected for editing: its system-generated id, the operator-editable identity and /// OPC 40010 identification fields, plus the concurrency token the edit modal must echo back on save. /// /// The system-generated stable id (read-only — never operator-edited). /// UNS level-5 segment name. /// Operator colloquial id; unique fleet-wide. /// The owning line id (the UNS-line selection). /// Optional driver binding; null when driver-less. /// Optional ERP equipment id. /// Optional SAP PM equipment id. /// Optional OPC 40010 manufacturer name. /// Optional OPC 40010 model designation. /// Optional OPC 40010 serial number. /// Optional OPC 40010 hardware revision. /// Optional OPC 40010 software revision. /// Optional OPC 40010 year of construction. /// Optional OPC 40010 asset location. /// Optional OPC 40010 manufacturer URI. /// Optional OPC 40010 device-manual URI. /// Whether the equipment is surfaced in deployments. /// The optimistic-concurrency token last read. public sealed record EquipmentEditDto(string EquipmentId, string Name, string MachineCode, string UnsLineId, string? DriverInstanceId, string? ZTag, string? SAPID, string? Manufacturer, string? Model, string? SerialNumber, string? HardwareRevision, string? SoftwareRevision, short? YearOfConstruction, string? AssetLocation, string? ManufacturerUri, string? DeviceManualUri, bool Enabled, byte[] RowVersion); /// /// An equipment-bound tag projected for editing: its operator-editable fields, the owning equipment /// (so the host can scope the candidate-driver list and refresh the right node), plus the concurrency /// token the edit modal must echo back on save. Tree tags are always equipment-bound, /// so FolderPath never surfaces here. /// /// The tag's stable id (read-only on edit). /// The owning equipment id. /// The tag name. /// The bound driver id. /// The OPC UA built-in type name. /// The tag-level access baseline. /// Whether writes are safe to retry. /// Optional poll-group key; null when unset. /// The schemaless per-driver-type JSON config. /// The optimistic-concurrency token last read. public sealed record TagEditDto(string TagId, string EquipmentId, string Name, string DriverInstanceId, string DataType, TagAccessLevel AccessLevel, bool WriteIdempotent, string? PollGroupId, string TagConfig, byte[] RowVersion); /// /// An equipment-bound virtual tag projected for editing: its operator-editable fields, the owning /// equipment (so the host can refresh the right node), plus the concurrency token the edit modal must /// echo back on save. Virtual tags are always scoped to an equipment, so the modal /// never offers an equipment-change control. /// /// The virtual tag's stable id (read-only on edit). /// The owning equipment id. /// The virtual-tag name. /// The OPC UA built-in type name. /// The bound script id. /// Whether the tag re-evaluates on dependency change. /// Optional periodic re-evaluation cadence in ms; null when unset. /// Whether the tag's values are historized. /// Whether the tag is spawned in deployments. /// The optimistic-concurrency token last read. public sealed record VirtualTagEditDto(string VirtualTagId, string EquipmentId, string Name, string DataType, string ScriptId, bool ChangeTriggered, int? TimerIntervalMs, bool Historize, bool Enabled, byte[] RowVersion); /// /// The outcome of a bulk equipment CSV import: how many rows were inserted, how many were skipped /// (existing MachineCode — the importer is additive-only, never an update), and a per-row error list /// for rows that could not be inserted (unknown line, unknown driver, or a cluster /// mismatch). Skipped rows never appear in . /// /// The count of new Equipment rows added. /// The count of rows skipped because their MachineCode already exists. /// The human-readable error strings for rows that failed validation. public sealed record EquipmentImportResult(int Inserted, int Skipped, IReadOnlyList Errors); /// /// Loads the structural portion of the unified-namespace (UNS) browse tree — /// Enterprise → Cluster → Area → Line → Equipment — from the config database. /// Equipment is a leaf in the tree; its tags and virtual tags are accessed via the /// equipment detail page, not via lazy tree expansion. /// public interface IUnsTreeService { /// /// Loads the full structural tree. Empty clusters are retained so they remain /// visible and editable. The returned nodes are detached view-models, safe to /// hold and mutate UI state on after the underlying context is disposed. /// /// A token to cancel the load. /// The enterprise root nodes, each populated down to equipment. Task> LoadStructureAsync(CancellationToken ct = default); /// /// Loads the driver tags bound to a single equipment as flat row projections for the equipment /// page's Tags tab table, ordered by Name. Each row carries the display columns plus the /// TagId the table uses to open the edit modal. Reads untracked. Returns an empty list when /// the equipment has no tags. /// /// The equipment whose tags to load. /// A token to cancel the load. /// The equipment's tag rows ordered by Name; empty if it has none. Task> LoadTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default); /// /// Loads the virtual tags scoped to a single equipment as flat row projections for the equipment /// page's Virtual Tags tab table, ordered by Name. Each row carries the display columns plus the /// VirtualTagId the table uses to open the edit modal. Reads untracked. Returns an empty list /// when the equipment has no virtual tags. /// /// The equipment whose virtual tags to load. /// A token to cancel the load. /// The equipment's virtual-tag rows ordered by Name; empty if it has none. Task> LoadVirtualTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default); // --------------------------------------------------------------------------------------------- // v3 UNS reference-only equipment: the Tags tab lists UnsTagReference rows pointing at raw tags. // --------------------------------------------------------------------------------------------- /// /// Loads the rows for a single equipment as flat /// projections for the equipment page's Tags tab, ordered by SortOrder then effective name. /// Each row carries the effective name (override else the backing raw tag's Name), the backing /// tag's computed RawPath, its inherited (read-only) datatype/access, the optional display-name /// override, and the reference's concurrency token. Reads untracked. Returns an empty list when the /// equipment has no references. /// /// The equipment whose references to load. /// A token to cancel the load. /// The equipment's reference rows; empty if it has none. Task> LoadReferencesForEquipmentAsync(string equipmentId, CancellationToken ct = default); /// /// Adds one per supplied raw TagId to an /// equipment. Every backing tag MUST live in the same cluster as the equipment (cross-cluster /// references are rejected). Each new reference's effective name (the raw tag's Name — no /// override on add) must be unique within the equipment across references, VirtualTags, and /// ScriptedAlarms (enforced via ), and a tag already referenced by /// the equipment is rejected. The batch is all-or-nothing. /// /// The referencing equipment. /// The raw tag ids to reference. /// A token to cancel the operation. /// Success, or the first guard/cluster/duplicate failure (nothing is added). Task AddReferencesAsync(string equipmentId, IReadOnlyList tagIds, CancellationToken ct = default); /// /// Removes a UNS tag reference. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency on . /// /// The reference to remove. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task RemoveReferenceAsync(string unsTagReferenceId, byte[] rowVersion, CancellationToken ct = default); /// /// Sets (or clears) a reference's DisplayNameOverride. A whitespace-only override collapses to /// null (use the raw name). The resulting effective name (override else the backing raw tag's /// Name) must be unique within the equipment across references, VirtualTags, and ScriptedAlarms /// (enforced via , excluding this reference). Uses last-write-wins /// optimistic concurrency. /// /// The reference to update. /// The new override, or null/blank to clear it. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, a name-collision failure, or a concurrency failure. Task SetReferenceOverrideAsync(string unsTagReferenceId, string? displayNameOverride, byte[] rowVersion, CancellationToken ct = default); /// /// Builds the single cluster-rooted that seeds the "+ Add reference" raw-tag /// picker for an equipment, structurally scoped to the equipment's own cluster (raw tags in any other /// cluster are unreachable through this root — the scope is enforced by the query, not merely hidden). /// The picker's RawTree lazily expands it via . /// Returns null when the equipment cannot be resolved to a cluster. /// /// The equipment whose cluster scopes the picker. /// A token to cancel the load. /// The cluster root node, or null when the equipment/cluster can't be resolved. Task LoadReferencePickerRootAsync(string equipmentId, CancellationToken ct = default); /// /// Enumerates every raw TagId beneath a Device or TagGroup picker node (the whole subtree), for /// the picker's "select all tags below" affordance. Returns an empty list for non-container node kinds. /// /// The picker node (Device or TagGroup) to enumerate tags under. /// A token to cancel the query. /// The tag ids in the node's subtree; empty for unsupported kinds. Task> LoadDescendantTagIdsAsync(RawNode node, CancellationToken ct = default); /// /// Loads a single UNS area projected for editing, or null if it no longer exists. /// Reads untracked and captures the current concurrency token for last-write-wins saves. /// /// The area to load. /// A token to cancel the load. /// The area's edit projection, or null when missing. Task LoadAreaAsync(string unsAreaId, CancellationToken ct = default); /// /// Loads a single UNS line projected for editing, or null if it no longer exists. /// Reads untracked and captures the current concurrency token for last-write-wins saves. /// /// The line to load. /// A token to cancel the load. /// The line's edit projection, or null when missing. Task LoadLineAsync(string unsLineId, CancellationToken ct = default); /// /// Loads a single equipment projected for editing, or null if it no longer exists. /// Reads untracked and captures the current concurrency token for last-write-wins saves. /// /// The equipment to load. /// A token to cancel the load. /// The equipment's edit projection, or null when missing. Task LoadEquipmentAsync(string equipmentId, CancellationToken ct = default); /// /// Loads a single equipment-bound tag projected for editing, or null if it no longer exists. /// Reads untracked and captures the current concurrency token for last-write-wins saves. /// /// The tag to load. /// A token to cancel the load. /// The tag's edit projection, or null when missing. Task LoadTagAsync(string tagId, CancellationToken ct = default); /// /// Loads a single equipment-bound virtual tag projected for editing, or null if it no longer /// exists. Reads untracked and captures the current concurrency token for last-write-wins saves. /// /// The virtual tag to load. /// A token to cancel the load. /// The virtual tag's edit projection, or null when missing. Task LoadVirtualTagAsync(string virtualTagId, CancellationToken ct = default); /// /// Loads every driver instance in a cluster (regardless of namespace kind) so the equipment modal /// can offer the full cluster driver list for binding. Ordered by DriverInstanceId. Each is /// projected to a (DriverInstanceId, Display) pair where Display is /// "{DriverInstanceId} — {Name} ({DriverType})". /// /// The cluster whose drivers to load. /// A token to cancel the load. /// The cluster's drivers projected to (DriverInstanceId, Display) pairs. Task> LoadDriversForClusterAsync(string clusterId, CancellationToken ct = default); /// /// Loads the UNS-line and driver (Id, Display) option lists the equipment page's Details tab /// offers, scoped to the cluster that owns the supplied line: every line in that cluster (for the /// line picker) and that cluster's drivers (reusing ). /// Centralizes the resolution the page would otherwise need the loaded tree to perform. Returns /// empty lists when is null/empty or cannot be resolved to a cluster. /// /// The line whose owning cluster scopes the option lists; null/empty yields empties. /// A token to cancel the load. /// The cluster-scoped line and driver options, or empty lists when the line can't be resolved. Task LoadEquipmentPickContextAsync(string? lineId, CancellationToken ct = default); /// /// Creates a new UNS area under a cluster. Fails if an area with the same id already exists. /// Whitespace-only notes are stored as null. /// /// The owning cluster. /// The unique area id to create. /// The area name. /// Optional notes; whitespace collapses to null. /// A token to cancel the operation. /// Success, or a duplicate-id failure. Task CreateAreaAsync(string clusterId, string unsAreaId, string name, string? notes, CancellationToken ct = default); /// /// Updates a UNS area's name, notes, and owning cluster. When the cluster changes, the /// reassignment guard blocks the move if any driver-bound equipment under the /// area is bound to a driver in a different cluster than the target. Uses last-write-wins /// optimistic concurrency on . /// /// The area to update. /// The new name. /// The new notes; whitespace collapses to null. /// The target cluster (may equal the current one). /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, a cluster-guard failure, or a concurrency failure. Task UpdateAreaAsync(string unsAreaId, string name, string? notes, string newClusterId, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes a UNS area. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency; a delete that fails because lines still reference the area surfaces a /// guidance message. /// /// The area to delete. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task DeleteAreaAsync(string unsAreaId, byte[] rowVersion, CancellationToken ct = default); /// /// Creates a new UNS line under an area. Fails if a line with the same id already exists. /// Whitespace-only notes are stored as null. /// /// The owning area. /// The unique line id to create. /// The line name. /// Optional notes; whitespace collapses to null. /// A token to cancel the operation. /// Success, or a duplicate-id failure. Task CreateLineAsync(string unsAreaId, string unsLineId, string name, string? notes, CancellationToken ct = default); /// /// Updates a UNS line's owning area, name, and notes. Uses last-write-wins optimistic /// concurrency on . /// /// The line to update. /// The new name. /// The new notes; whitespace collapses to null. /// The target parent area. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, or a concurrency failure. Task UpdateLineAsync(string unsLineId, string name, string? notes, string newUnsAreaId, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes a UNS line. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency; a delete that fails because equipment still references the line /// surfaces a guidance message. /// /// The line to delete. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task DeleteLineAsync(string unsLineId, byte[] rowVersion, CancellationToken ct = default); /// /// Creates a new equipment under a UNS line. The EquipmentId is system-generated /// (EQ- + the first 12 hex chars of a fresh EquipmentUuid). /// Fails if the line is unset or if the MachineCode is already used fleet-wide. Whitespace-only /// ZTag/SAPID collapse to null. (v3: equipment no longer binds a driver — the reference-only /// Tags tab points at raw tags instead — so no driver-cluster guard applies.) /// /// The operator-editable equipment fields. /// A token to cancel the operation. /// Success, a missing-line failure, or a duplicate-MachineCode failure. Task CreateEquipmentAsync(EquipmentInput input, CancellationToken ct = default); /// /// Bulk-imports equipment from a parsed set of rows in a single /// context, applying the same rules as the single-add path: a row whose UnsLineId does not /// exist is an error; and a row whose MachineCode already exists in the DB or earlier /// in the same batch is silently skipped (additive-only — never an update). Inserted rows get a /// system-generated EQ- id and a fresh EquipmentUuid. All inserts are saved once at /// the end. (v3: equipment no longer binds a driver, so there is no per-row driver-cluster guard.) /// /// The parsed equipment rows to import. /// A token to cancel the operation. /// The insert/skip counts and the per-row error list. Task ImportEquipmentAsync(IReadOnlyList rows, CancellationToken ct = default); /// /// Updates an equipment's mutable fields (line, name, MachineCode, external ids, and the OPC 40010 /// identification fields). Fails on a MachineCode already used by another row. Uses last-write-wins /// optimistic concurrency on . (v3: equipment /// no longer binds a driver, so there is no driver-cluster guard.) /// /// The equipment to update. /// The new operator-editable equipment fields. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, a duplicate-MachineCode failure, or a concurrency failure. Task UpdateEquipmentAsync(string equipmentId, EquipmentInput input, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes an equipment. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency; a delete that fails because tags or virtual tags still reference the /// equipment surfaces a guidance message. /// /// The equipment to delete. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task DeleteEquipmentAsync(string equipmentId, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes a server cluster. A missing row is treated as success (already gone). The delete is /// refuse-if-children: a cluster that still owns nodes, namespaces, UNS areas, or driver /// instances is left intact and a guidance message is returned (the same convention as /// Area/Line/Equipment, and matching the existing cluster-settings "Delete cluster" behaviour). /// The entity carries no concurrency token, /// so — unlike the Area/Line/Equipment deletes — this signature takes no rowVersion. /// /// The cluster to delete. /// A token to cancel the operation. /// Success, or a refuse-if-children failure naming what still references the cluster. Task DeleteClusterAsync(string clusterId, CancellationToken ct = default); /// /// Deletes every cluster carrying the given Enterprise label. The enterprise is a tree /// label (a string value on cluster rows), not a DB entity, so deleting it means deleting all of /// its clusters. The operation is all-or-nothing: it first checks every matching cluster for /// children; if any cluster still owns nodes, namespaces, UNS areas, or driver instances /// the whole delete refuses (naming the first blocking cluster) and nothing is removed — there is /// never a partial enterprise deletion. An enterprise with no matching clusters is a no-op success. /// /// The Enterprise label whose clusters to delete. /// A token to cancel the operation. /// Success, or a refuse-if-children failure naming the blocking cluster. Task DeleteEnterpriseAsync(string enterpriseName, CancellationToken ct = default); /// /// Loads the drivers eligible to back a tag on the given equipment: drivers in the equipment's /// cluster (Equipment.UnsLine → UnsArea.ClusterId) whose namespace is Equipment-kind /// (tree tags are equipment-bound). Ordered by DriverInstanceId. Returns /// an empty list when the equipment cannot be resolved to a cluster. /// /// The equipment whose candidate drivers to load. /// A token to cancel the load. /// The eligible drivers projected to (DriverInstanceId, Display, DriverType, DriverConfig) /// tuples, where DriverType lets the TagModal dispatch to a per-driver-type typed config editor /// and DriverConfig feeds the live-browse address picker (e.g. the Galaxy gateway picker opens its /// browse session against the selected GalaxyMxGateway's config). Task> LoadTagDriversForEquipmentAsync(string equipmentId, CancellationToken ct = default); /// /// Creates a new equipment-bound tag. FolderPath is always null (the tree /// only edits equipment-bound tags). Fails on a duplicate TagId, invalid /// TagConfig JSON, an unknown driver, a driver whose namespace is not Equipment-kind, a /// driver in a different cluster than the equipment, or a name already used on /// the equipment. Whitespace-only PollGroupId collapses to null. /// /// The owning equipment. /// The operator-editable tag fields. /// A token to cancel the operation. /// Success, or one of the guard failures. Task CreateTagAsync(string equipmentId, TagInput input, CancellationToken ct = default); /// /// Updates an equipment-bound tag's driver binding, name, data type, access level, write-retry /// flag, poll group, and config. The owning EquipmentId and the null /// FolderPath are preserved. Re-runs the JSON-validity, namespace-kind, and cluster /// guards against the tag's existing equipment, and enforces name uniqueness on that /// equipment excluding this tag. Uses last-write-wins optimistic concurrency on /// . /// /// The tag to update. /// The new operator-editable tag fields. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, a guard failure, or a concurrency failure. Task UpdateTagAsync(string tagId, TagInput input, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes a tag. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency on . /// /// The tag to delete. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task DeleteTagAsync(string tagId, byte[] rowVersion, CancellationToken ct = default); /// /// Loads the scripts eligible to back a virtual tag, ordered by name. Each is projected to a /// (ScriptId, Display) pair where Display is "{Name} ({Language})". /// /// A token to cancel the load. /// The scripts projected to (ScriptId, Display) pairs. Task> LoadScriptsAsync(CancellationToken ct = default); /// /// Creates a new, empty C# script row so the inline virtual-tag panel can bind a brand-new script /// without leaving the modal. The ScriptId is system-generated (SC- + the first 12 hex /// chars of a fresh , mirroring the Script-edit page's convention); the source is /// blank, the SourceHash is the SHA-256 of that blank source (so it stays consistent with the /// inline save), and the language is CSharp. A whitespace-only name falls back to a default. /// The new id is returned via so the caller can bind it. /// /// The operator-friendly script name; whitespace falls back to a default. /// A token to cancel the operation. /// Success carrying the generated SC-… id in . Task CreateScriptAsync(string name, CancellationToken ct = default); /// /// Creates a new equipment-bound virtual tag (virtual tags are always scoped /// to an equipment). Fails if the equipment does not exist, if no script is chosen, if neither a /// change trigger nor a timer is set, if the timer is below the 50 ms minimum, on a duplicate /// VirtualTagId, or on a name already used on the equipment. /// /// The owning equipment. /// The operator-editable virtual-tag fields. /// A token to cancel the operation. /// Success, or one of the guard failures; or if the chosen script uses the {{equip}} /// token but the equipment has no derivable single tag base. Task CreateVirtualTagAsync(string equipmentId, VirtualTagInput input, CancellationToken ct = default); /// /// Updates an equipment-bound virtual tag's name, data type, script binding, triggers, historize, /// and enabled flags. The owning EquipmentId is preserved. Re-runs the script-chosen, /// change-or-timer, and 50 ms timer-minimum guards, and enforces name uniqueness on the tag's /// existing equipment excluding this virtual tag. Uses last-write-wins optimistic concurrency on /// . /// /// The virtual tag to update. /// The new operator-editable virtual-tag fields. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, a guard failure, or a concurrency failure; or if the /// chosen script uses the {{equip}} token but the equipment has no derivable single tag base. Task UpdateVirtualTagAsync(string virtualTagId, VirtualTagInput input, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes a virtual tag. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency on . /// /// The virtual tag to delete. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task DeleteVirtualTagAsync(string virtualTagId, byte[] rowVersion, CancellationToken ct = default); /// /// Loads a script's editable source for the inline script-source panel in the virtual-tag modal, /// along with the concurrency token the panel must echo back on save and the script's display name. /// Reads untracked. Returns null when the script no longer exists. /// /// The script whose source to load. /// A token to cancel the load. /// The (SourceCode, RowVersion, Name) triple, or null when missing. Task<(string SourceCode, byte[] RowVersion, string Name)?> GetScriptSourceAsync(string scriptId, CancellationToken ct = default); /// /// Counts how many virtual tags bind the given script, so the inline editor can warn the operator /// that an edit to a shared script affects every virtual tag using it. /// /// The script to count usages of. /// A token to cancel the query. /// The number of virtual tags whose ScriptId matches. Task CountVirtualTagsUsingScriptAsync(string scriptId, CancellationToken ct = default); /// /// Saves an edited script body from the inline panel: updates SourceCode and recomputes the /// SHA-256 SourceHash (lower-case hex, matching the Script-edit page). This save is separate /// from the virtual-tag save and is guarded by its own last-write-wins optimistic concurrency on /// . /// /// The script to update. /// The new source body. /// The concurrency token the panel last read. /// A token to cancel the operation. /// Success, a missing-row failure, or a concurrency failure. Task<(bool Ok, string? Error)> UpdateScriptSourceAsync(string scriptId, string sourceCode, byte[] rowVersion, CancellationToken ct = default); /// /// Loads the scripted alarms owned by a single equipment as flat row projections for the equipment /// page's Alarms tab table, ordered by Name. Each row carries the display columns plus the /// ScriptedAlarmId the table uses to open the edit modal. Reads untracked. Returns an empty /// list when the equipment has no scripted alarms. /// /// The equipment whose scripted alarms to load. /// A token to cancel the load. /// The equipment's scripted-alarm rows ordered by Name; empty if it has none. Task> LoadAlarmsForEquipmentAsync(string equipmentId, CancellationToken ct = default); /// /// Loads a single scripted alarm projected for editing, or null if it no longer exists. /// Reads untracked and captures the current concurrency token for last-write-wins saves. /// /// The scripted alarm to load. /// A token to cancel the load. /// The scripted alarm's edit projection, or null when missing. Task LoadScriptedAlarmAsync(string scriptedAlarmId, CancellationToken ct = default); /// /// Creates a new scripted alarm under an equipment. The ScriptedAlarmId is operator-typed /// (not system-generated). Fails on a duplicate ScriptedAlarmId. Field validation (severity /// range, required fields) is enforced client-side in the modal. On success the operator-typed id is /// echoed back via . /// /// The owning equipment. /// The operator-editable scripted-alarm fields. /// A token to cancel the operation. /// Success carrying the created id, or a duplicate-id failure. Task CreateScriptedAlarmAsync(string equipmentId, ScriptedAlarmInput input, CancellationToken ct = default); /// /// Updates a scripted alarm's mutable fields (name, alarm type, severity, message template, /// predicate script, and the historize/retain/enabled flags). The owning EquipmentId is /// preserved (the Alarms tab fixes it). Uses last-write-wins optimistic concurrency on /// . /// /// The scripted alarm to update. /// The new operator-editable scripted-alarm fields. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a missing-row failure, or a concurrency failure. Task UpdateScriptedAlarmAsync(string scriptedAlarmId, ScriptedAlarmInput input, byte[] rowVersion, CancellationToken ct = default); /// /// Deletes a scripted alarm. A missing row is treated as success (already gone). Uses last-write-wins /// optimistic concurrency on . /// /// The scripted alarm to delete. /// The concurrency token the caller last read. /// A token to cancel the operation. /// Success, a concurrency failure, or a delete-failed failure. Task DeleteScriptedAlarmAsync(string scriptedAlarmId, byte[] rowVersion, CancellationToken ct = default); }