feat(adminui): Galaxy picker pre-fills native-alarm fields from IsAlarm

This commit is contained in:
Joseph Doherty
2026-06-16 16:55:05 -04:00
parent 9a8b8ff6f6
commit 069a5f3165
6 changed files with 129 additions and 5 deletions
@@ -15,13 +15,13 @@
<div class="col-md-5">
<label class="form-label">Tag name</label>
<input type="text" class="form-control form-control-sm mono" placeholder="DelmiaReceiver_001"
@bind="_tagName" @bind:after="OnChangedAsync" />
@bind="_tagName" @bind:after="OnManualChangedAsync" />
<div class="form-text">Globally unique system (tag) name.</div>
</div>
<div class="col-md-5">
<label class="form-label">Attribute name</label>
<input type="text" class="form-control form-control-sm mono" placeholder="DownloadPath"
@bind="_attributeName" @bind:after="OnChangedAsync" />
@bind="_attributeName" @bind:after="OnManualChangedAsync" />
<div class="form-text">MXAccess attribute name.</div>
</div>
</div>
@@ -100,6 +100,13 @@
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
[Parameter, EditorRequired] public Func<string> GetConfigJson { get; set; } = () => "{}";
/// <summary>True when the currently-selected attribute is itself an alarm condition
/// (<c>DriverAttributeInfo.IsAlarm</c>). The host (TagModal) pre-fills a default native-alarm
/// object into the TagConfig on commit. Only a side-panel attribute click sets this; manual
/// tag/attribute typing or an object re-selection clears it (we can't infer alarm-ness by name).</summary>
[Parameter] public bool SelectedIsAlarm { get; set; }
[Parameter] public EventCallback<bool> SelectedIsAlarmChanged { get; set; }
private string _tagName = "";
private string _attributeName = "";
private string _built = "";
@@ -146,6 +153,7 @@
{
_tagName = node.NodeId;
_attributeName = "";
await SetSelectedIsAlarmAsync(false);
_attrs = null;
_attrsLoading = true;
_attrsError = null;
@@ -168,6 +176,7 @@
private async Task SelectAttributeAsync(AttributeInfo a)
{
_attributeName = a.Name;
await SetSelectedIsAlarmAsync(a.IsAlarm);
await OnChangedAsync();
}
@@ -177,6 +186,22 @@
await CurrentAddressChanged.InvokeAsync(_built);
}
/// <summary>Manual tag/attribute typing: we can't infer alarm-ness from a hand-typed name, so
/// clear the pre-fill flag before publishing the rebuilt address.</summary>
private async Task OnManualChangedAsync()
{
await SetSelectedIsAlarmAsync(false);
await OnChangedAsync();
}
/// <summary>Publishes the selected attribute's alarm-ness to the host, only when it changed.</summary>
private async Task SetSelectedIsAlarmAsync(bool isAlarm)
{
if (SelectedIsAlarm == isAlarm) { return; }
SelectedIsAlarm = isAlarm;
await SelectedIsAlarmChanged.InvokeAsync(isAlarm);
}
private string Build()
{
if (string.IsNullOrWhiteSpace(_tagName))
@@ -118,6 +118,7 @@
OnPickAddress="@OnGalaxyAddressPicked">
<GalaxyAddressPickerBody CurrentAddress="@_galaxyAddress"
CurrentAddressChanged="@((s) => _galaxyAddress = s)"
@bind-SelectedIsAlarm="_galaxyPickedIsAlarm"
GetConfigJson="@(() => SelectedDriverConfig)" />
</DriverTagPicker>
}
@@ -244,6 +245,11 @@
private bool _showGalaxyPicker;
private string _galaxyAddress = "";
// True when the attribute most-recently selected in the Galaxy picker is itself an alarm
// (DriverAttributeInfo.IsAlarm). On commit, this pre-fills a default native-alarm object into the
// TagConfig (when none is present) so the operator can author the alarm in one pass.
private bool _galaxyPickedIsAlarm;
// Driver-agnostic server-side HistoryRead intent (root `isHistorized` / `historianTagname`), reflected
// for the "Historize this tag" controls. Re-read from _form.TagConfig whenever the modal (re)opens or
// the driver changes; the change handlers merge it back onto _form.TagConfig via TagHistorizeConfig.
@@ -276,6 +282,7 @@
_form.TagConfig = "{}";
// The Galaxy reference belongs to the previous driver; clear the picker's working address too.
_galaxyAddress = "";
_galaxyPickedIsAlarm = false;
// The reset TagConfig carries no history intent — reflect that in the historize controls.
_historizeState = TagHistorizeConfig.Read(_form.TagConfig);
}
@@ -283,10 +290,18 @@
// The operator picked a Galaxy reference (tag_name.AttributeName); store it as the canonical
// {"FullName":"..."} TagConfig the Galaxy driver resolves to an MXAccess reference. Default
// (PascalCase) serialization yields the "FullName" key the driver/walker reads.
//
// When the picked attribute is itself an alarm (DriverAttributeInfo.IsAlarm), pre-seed a default
// native-alarm `alarm` object so the tag materialises as a Part 9 condition and Task 3's
// "Historize to AVEVA" toggle auto-appears — sparing the operator hand-written JSON. NativeAlarmModel
// .SeedDefaultAlarm only seeds when absent (never overwrites an authored alarm) and preserves FullName.
private void OnGalaxyAddressPicked(string address)
{
_galaxyAddress = address;
_form.TagConfig = JsonSerializer.Serialize(new { FullName = address });
var config = JsonSerializer.Serialize(new { FullName = address });
_form.TagConfig = _galaxyPickedIsAlarm
? NativeAlarmModel.SeedDefaultAlarm(config)
: config;
}
private IDictionary<string, object> BuildEditorParameters() => new Dictionary<string, object>
@@ -374,6 +389,7 @@
}
_error = null;
_showGalaxyPicker = false;
_galaxyPickedIsAlarm = false;
// Seed the picker's working address from any existing {"FullName":"..."} so it opens pre-populated.
_galaxyAddress = ReadFullName(_form.TagConfig);
// Seed the historize controls from any existing root isHistorized/historianTagname keys.