feat(adminui): isHistorized + historianTagname as first-class Tag fields
This commit is contained in:
-11
@@ -6,17 +6,6 @@
|
||||
placeholder="Reactor1.Temperature"
|
||||
@onchange="@(e => Update(() => _m.FullName = e.Value?.ToString() ?? string.Empty))" />
|
||||
<div class="form-text">The AVEVA Historian tagname the driver reads against.</div></div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" class="form-check-input" checked="@_m.IsHistorized"
|
||||
@onchange="@(e => Update(() => _m.IsHistorized = e.Value is true))" />
|
||||
<label class="form-check-label">Historized (expose OPC UA HistoryRead)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12"><label class="form-label">Historian tagname override (optional)</label>
|
||||
<input type="text" class="form-control form-control-sm mono" value="@_m.HistorianTagname"
|
||||
@onchange="@(e => Update(() => _m.HistorianTagname = e.Value?.ToString() ?? string.Empty))" />
|
||||
<div class="form-text">Blank defaults the historian tagname to the FullName above.</div></div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
-11
@@ -6,17 +6,6 @@
|
||||
placeholder="nsu=urn:server:ns;s=Line3.Temp"
|
||||
@onchange="@(e => Update(() => _m.FullName = e.Value?.ToString() ?? string.Empty))" />
|
||||
<div class="form-text">The remote OPC UA NodeId the driver reads/writes/subscribes against. Use the browse picker on the driver page to find it.</div></div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" class="form-check-input" checked="@_m.IsHistorized"
|
||||
@onchange="@(e => Update(() => _m.IsHistorized = e.Value is true))" />
|
||||
<label class="form-check-label">Historized (expose OPC UA HistoryRead)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12"><label class="form-label">Historian tagname override (optional)</label>
|
||||
<input type="text" class="form-control form-control-sm mono" value="@_m.HistorianTagname"
|
||||
@onchange="@(e => Update(() => _m.HistorianTagname = e.Value?.ToString() ?? string.Empty))" />
|
||||
<div class="form-text">Blank defaults the historian tagname to the FullName above.</div></div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
@@ -136,6 +136,39 @@
|
||||
<ValidationMessage For="@(() => _form.TagConfig)" />
|
||||
</div>
|
||||
|
||||
@* Driver-agnostic server-side HistoryRead intent. Distinct from the native-alarm
|
||||
"Historize to AVEVA" toggle below: THIS gates TAG-VALUE history (root keys
|
||||
`isHistorized` / `historianTagname`, read by Phase7Composer.ExtractTagHistorize),
|
||||
merged onto the canonical TagConfig via the pure TagHistorizeConfig seam so it
|
||||
composes with the typed editor's driver-specific fields (both preserve unknown keys).
|
||||
Shown for EVERY driver once one is picked. *@
|
||||
@if (!string.IsNullOrEmpty(_form.DriverInstanceId))
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label class="form-label">History</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="tag-historize"
|
||||
checked="@_historizeState.IsHistorized"
|
||||
@onchange="OnHistorizeChanged" />
|
||||
<label class="form-check-label" for="tag-historize">Historize this tag (expose OPC UA HistoryRead)</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
When checked, the server serves OPC UA HistoryRead over this tag's value
|
||||
from the configured historian.
|
||||
</div>
|
||||
@if (_historizeState.IsHistorized)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<label class="form-label" for="tag-historian-tagname">Historian tagname (override, optional)</label>
|
||||
<input type="text" class="form-control form-control-sm mono" id="tag-historian-tagname"
|
||||
value="@_historizeState.HistorianTagname"
|
||||
@onchange="OnHistorianTagnameChanged" />
|
||||
<div class="form-text">Blank defaults the historian tagname to this tag's driver FullName.</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@* Native-alarm options: shown only when the TagConfig carries an `alarm` object (the tag
|
||||
is a Part 9 condition). The "Historize to AVEVA" toggle edits the alarm.historizeToAveva
|
||||
opt-out (bool?, unchecked-via-clear ⇒ absent ⇒ historize default-on at the server gate;
|
||||
@@ -211,6 +244,11 @@
|
||||
private bool _showGalaxyPicker;
|
||||
private string _galaxyAddress = "";
|
||||
|
||||
// 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.
|
||||
private TagHistorizeConfig.HistorizeState _historizeState;
|
||||
|
||||
// The DriverType of the currently-selected driver (drives editor dispatch). Null when no driver chosen.
|
||||
private string? SelectedDriverType =>
|
||||
Drivers.FirstOrDefault(d => d.Id == _form.DriverInstanceId).DriverType;
|
||||
@@ -238,6 +276,8 @@
|
||||
_form.TagConfig = "{}";
|
||||
// The Galaxy reference belongs to the previous driver; clear the picker's working address too.
|
||||
_galaxyAddress = "";
|
||||
// The reset TagConfig carries no history intent — reflect that in the historize controls.
|
||||
_historizeState = TagHistorizeConfig.Read(_form.TagConfig);
|
||||
}
|
||||
|
||||
// The operator picked a Galaxy reference (tag_name.AttributeName); store it as the canonical
|
||||
@@ -255,14 +295,32 @@
|
||||
["ConfigJsonChanged"] = EventCallback.Factory.Create<string>(this, v => _form.TagConfig = v),
|
||||
};
|
||||
|
||||
// Cache a single NativeAlarmModel parse keyed on the current TagConfig string, so the two computed
|
||||
// properties below don't each re-parse the JSON on every render. Re-parsed only when TagConfig changes.
|
||||
private NativeAlarmModel _nativeAlarm = NativeAlarmModel.FromJson("{}");
|
||||
private string? _nativeAlarmSource;
|
||||
|
||||
private NativeAlarmModel NativeAlarm
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_nativeAlarmSource != _form.TagConfig)
|
||||
{
|
||||
_nativeAlarmSource = _form.TagConfig;
|
||||
_nativeAlarm = NativeAlarmModel.FromJson(_form.TagConfig);
|
||||
}
|
||||
return _nativeAlarm;
|
||||
}
|
||||
}
|
||||
|
||||
// True when the current TagConfig carries an `alarm` object — i.e. the tag is materialised as a Part 9
|
||||
// native-alarm condition rather than a value variable. Gates the "Historize to AVEVA" toggle's visibility.
|
||||
private bool HasNativeAlarm => NativeAlarmModel.FromJson(_form.TagConfig).IsAlarm;
|
||||
private bool HasNativeAlarm => NativeAlarm.IsAlarm;
|
||||
|
||||
// The native alarm's HistorizeToAveva intent reflected for the checkbox: absent (null) ⇒ historize
|
||||
// (default-on at the server gate), so the box is checked for both null and explicit true; only an
|
||||
// explicit false leaves it unchecked.
|
||||
private bool AlarmHistorizeToAveva => NativeAlarmModel.FromJson(_form.TagConfig).HistorizeToAveva != false;
|
||||
private bool AlarmHistorizeToAveva => NativeAlarm.HistorizeToAveva != false;
|
||||
|
||||
// Toggle the alarm.historizeToAveva opt-out in the raw TagConfig. Checked ⇒ remove the key (null ⇒
|
||||
// absent ⇒ historize default-on); unchecked ⇒ write an explicit false (suppress the durable AVEVA row).
|
||||
@@ -271,11 +329,28 @@
|
||||
{
|
||||
var model = NativeAlarmModel.FromJson(_form.TagConfig);
|
||||
if (!model.IsAlarm) { return; }
|
||||
var isChecked = e.Value is bool b && b;
|
||||
model.HistorizeToAveva = isChecked ? null : false;
|
||||
model.HistorizeToAveva = e.Value is true ? null : false;
|
||||
_form.TagConfig = model.ToJson();
|
||||
}
|
||||
|
||||
// Toggle the root `isHistorized` tag-value history intent in the raw TagConfig, merged via the pure
|
||||
// TagHistorizeConfig seam so the typed editor's driver-specific keys are preserved. Distinct from the
|
||||
// native-alarm "Historize to AVEVA" opt-out above (that gates alarm-transition history, not tag values).
|
||||
private void OnHistorizeChanged(ChangeEventArgs e)
|
||||
{
|
||||
var isHistorized = e.Value is true;
|
||||
_form.TagConfig = TagHistorizeConfig.Set(_form.TagConfig, isHistorized, _historizeState.HistorianTagname);
|
||||
_historizeState = TagHistorizeConfig.Read(_form.TagConfig);
|
||||
}
|
||||
|
||||
// Merge the optional historian-tagname override (root `historianTagname`) into the raw TagConfig.
|
||||
private void OnHistorianTagnameChanged(ChangeEventArgs e)
|
||||
{
|
||||
var tagname = e.Value?.ToString() ?? string.Empty;
|
||||
_form.TagConfig = TagHistorizeConfig.Set(_form.TagConfig, _historizeState.IsHistorized, tagname);
|
||||
_historizeState = TagHistorizeConfig.Read(_form.TagConfig);
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
// Rebuild the working form whenever the host (re)opens the modal for a fresh target.
|
||||
@@ -301,6 +376,8 @@
|
||||
_showGalaxyPicker = 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.
|
||||
_historizeState = TagHistorizeConfig.Read(_form.TagConfig);
|
||||
}
|
||||
|
||||
// Best-effort extraction of FullName from a Galaxy TagConfig; returns "" when absent or unparseable.
|
||||
|
||||
Reference in New Issue
Block a user