feat(adminui): isHistorized + historianTagname as first-class Tag fields
This commit is contained in:
+7
-20
@@ -4,26 +4,20 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
|
||||
|
||||
/// <summary>Typed working model for a Wonderware (AVEVA) Historian equipment tag's TagConfig JSON. The
|
||||
/// tag binds to a historian tag by its full reference (<c>FullName</c> — the historian tagname/source
|
||||
/// the driver reads against), plus the optional driver-agnostic server-side HistoryRead intent
|
||||
/// (<c>isHistorized</c> / <c>historianTagname</c>). Preserves unrecognised JSON keys across a load→save.</summary>
|
||||
/// the driver reads against). Preserves unrecognised JSON keys across a load→save.</summary>
|
||||
/// <remarks>
|
||||
/// The <c>FullName</c> key is intentionally PascalCase: the deploy-time composer + node walker
|
||||
/// (<c>Phase7Composer.ExtractTagFullName</c>, <c>EquipmentNodeWalker</c>) read it via a
|
||||
/// case-sensitive <c>TryGetProperty("FullName", …)</c>, so the editor MUST persist that exact
|
||||
/// casing. The history keys (<c>isHistorized</c> / <c>historianTagname</c>) are camelCase to match
|
||||
/// <c>Phase7Composer.ExtractTagHistorize</c>.
|
||||
/// casing. The driver-agnostic server-side HistoryRead intent keys (<c>isHistorized</c> /
|
||||
/// <c>historianTagname</c>) are NOT modelled here — they are owned by the TagModal-merge seam
|
||||
/// (<see cref="TagHistorizeConfig"/>) and survive a load→save of this model as preserved unknown keys.
|
||||
/// </remarks>
|
||||
public sealed class HistorianWonderwareTagConfigModel
|
||||
{
|
||||
/// <summary>Historian tagname/source the tag binds to (the driver-side full reference). Required.</summary>
|
||||
public string FullName { get; set; } = "";
|
||||
|
||||
/// <summary>Whether the server exposes OPC UA HistoryRead over this tag's variable node.</summary>
|
||||
public bool IsHistorized { get; set; }
|
||||
|
||||
/// <summary>Optional historian tagname override; blank means the historian tagname defaults to <see cref="FullName"/>.</summary>
|
||||
public string HistorianTagname { get; set; } = "";
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -35,24 +29,17 @@ public sealed class HistorianWonderwareTagConfigModel
|
||||
return new HistorianWonderwareTagConfigModel
|
||||
{
|
||||
FullName = TagConfigJson.GetString(o, "FullName") ?? "",
|
||||
IsHistorized = TagConfigJson.GetBool(o, "isHistorized"),
|
||||
HistorianTagname = TagConfigJson.GetString(o, "historianTagname") ?? "",
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Serialises this model back to a TagConfig JSON string over the preserved key bag.
|
||||
/// <c>FullName</c> is written PascalCase (the composer/walker contract key); the history keys are
|
||||
/// written camelCase and dropped when default (absent <c>isHistorized</c> ⇒ false at the composer;
|
||||
/// blank <c>historianTagname</c> ⇒ defaults to <c>FullName</c>).</summary>
|
||||
/// <c>FullName</c> is written PascalCase (the composer/walker contract key); any history keys merged
|
||||
/// by the TagModal (<c>isHistorized</c> / <c>historianTagname</c>) are carried through untouched as
|
||||
/// preserved unknown keys.</summary>
|
||||
public string ToJson()
|
||||
{
|
||||
TagConfigJson.Set(_bag, "FullName", FullName.Trim());
|
||||
// Drop isHistorized when false so the persisted blob stays minimal and matches the
|
||||
// composer's "absent ⇒ false" convention; same for a blank historianTagname override.
|
||||
TagConfigJson.Set(_bag, "isHistorized", IsHistorized ? true : null);
|
||||
TagConfigJson.Set(_bag, "historianTagname",
|
||||
string.IsNullOrWhiteSpace(HistorianTagname) ? null : HistorianTagname.Trim());
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,27 +4,21 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
|
||||
|
||||
/// <summary>Typed working model for an OPC UA Client (gateway) equipment tag's TagConfig JSON. The tag
|
||||
/// is bound to the upstream OPC UA server node by its full reference (<c>FullName</c> — the persisted
|
||||
/// stable <c>nsu=…;s=…</c> or plain <c>ns=2;s=…</c> NodeId the driver reads/writes/subscribes against),
|
||||
/// plus the optional driver-agnostic server-side HistoryRead intent (<c>isHistorized</c> /
|
||||
/// <c>historianTagname</c>). Preserves unrecognised JSON keys across a load→save.</summary>
|
||||
/// stable <c>nsu=…;s=…</c> or plain <c>ns=2;s=…</c> NodeId the driver reads/writes/subscribes against).
|
||||
/// Preserves unrecognised JSON keys across a load→save.</summary>
|
||||
/// <remarks>
|
||||
/// The <c>FullName</c> key is intentionally PascalCase: the deploy-time composer + node walker
|
||||
/// (<c>Phase7Composer.ExtractTagFullName</c>, <c>EquipmentNodeWalker</c>) read it via a
|
||||
/// case-sensitive <c>TryGetProperty("FullName", …)</c>, so the editor MUST persist that exact
|
||||
/// casing. The history keys (<c>isHistorized</c> / <c>historianTagname</c>) are camelCase to match
|
||||
/// <c>Phase7Composer.ExtractTagHistorize</c>.
|
||||
/// casing. The driver-agnostic server-side HistoryRead intent keys (<c>isHistorized</c> /
|
||||
/// <c>historianTagname</c>) are NOT modelled here — they are owned by the TagModal-merge seam
|
||||
/// (<see cref="TagHistorizeConfig"/>) and survive a load→save of this model as preserved unknown keys.
|
||||
/// </remarks>
|
||||
public sealed class OpcUaClientTagConfigModel
|
||||
{
|
||||
/// <summary>Upstream OPC UA node reference the tag binds to (the driver-side full reference). Required.</summary>
|
||||
public string FullName { get; set; } = "";
|
||||
|
||||
/// <summary>Whether the server exposes OPC UA HistoryRead over this tag's variable node.</summary>
|
||||
public bool IsHistorized { get; set; }
|
||||
|
||||
/// <summary>Optional historian tagname override; blank means the historian tagname defaults to <see cref="FullName"/>.</summary>
|
||||
public string HistorianTagname { get; set; } = "";
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -36,24 +30,17 @@ public sealed class OpcUaClientTagConfigModel
|
||||
return new OpcUaClientTagConfigModel
|
||||
{
|
||||
FullName = TagConfigJson.GetString(o, "FullName") ?? "",
|
||||
IsHistorized = TagConfigJson.GetBool(o, "isHistorized"),
|
||||
HistorianTagname = TagConfigJson.GetString(o, "historianTagname") ?? "",
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Serialises this model back to a TagConfig JSON string over the preserved key bag.
|
||||
/// <c>FullName</c> is written PascalCase (the composer/walker contract key); the history keys are
|
||||
/// written camelCase and dropped when default (absent <c>isHistorized</c> ⇒ false at the composer;
|
||||
/// blank <c>historianTagname</c> ⇒ defaults to <c>FullName</c>).</summary>
|
||||
/// <c>FullName</c> is written PascalCase (the composer/walker contract key); any history keys merged
|
||||
/// by the TagModal (<c>isHistorized</c> / <c>historianTagname</c>) are carried through untouched as
|
||||
/// preserved unknown keys.</summary>
|
||||
public string ToJson()
|
||||
{
|
||||
TagConfigJson.Set(_bag, "FullName", FullName.Trim());
|
||||
// Drop isHistorized when false so the persisted blob stays minimal and matches the
|
||||
// composer's "absent ⇒ false" convention; same for a blank historianTagname override.
|
||||
TagConfigJson.Set(_bag, "isHistorized", IsHistorized ? true : null);
|
||||
TagConfigJson.Set(_bag, "historianTagname",
|
||||
string.IsNullOrWhiteSpace(HistorianTagname) ? null : HistorianTagname.Trim());
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
|
||||
|
||||
/// <summary>
|
||||
/// Pure, driver-agnostic merge helper for the two server-side HistoryRead intent keys at the ROOT of a
|
||||
/// tag's <c>TagConfig</c> JSON: <c>isHistorized</c> (camelCase bool — omit/false default) and
|
||||
/// <c>historianTagname</c> (camelCase string override — omit when blank). These map to what the server's
|
||||
/// <c>Phase7Composer.ExtractTagHistorize</c> reads (see <c>docs/Historian.md</c>).
|
||||
///
|
||||
/// <para>
|
||||
/// This is the TagModal-merge seam: the TagModal owns the canonical TagConfig JSON; the driver's typed
|
||||
/// editor reads/writes its driver-specific fields on that same JSON, and the modal's "Historize this tag"
|
||||
/// checkbox + "Historian tagname (override)" textbox read/write these two keys through this helper. They
|
||||
/// COMPOSE because both sides preserve every unrecognised key (delegated to
|
||||
/// <see cref="TagConfigJson.ParseOrNew"/> / <see cref="TagConfigJson.Set"/>), so neither clobbers the
|
||||
/// other's fields. These keys are intentionally NOT carried inside any per-driver typed model.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class TagHistorizeConfig
|
||||
{
|
||||
/// <summary>The two history-intent values parsed from a TagConfig JSON's root.</summary>
|
||||
/// <param name="IsHistorized">Whether the server exposes OPC UA HistoryRead over this tag's node.</param>
|
||||
/// <param name="HistorianTagname">Optional historian tagname override; <c>""</c> means default to the tag's FullName.</param>
|
||||
public readonly record struct HistorizeState(bool IsHistorized, string HistorianTagname);
|
||||
|
||||
/// <summary>Reads the history-intent keys from a TagConfig JSON, defaulting any absent key
|
||||
/// (null/blank/malformed input ⇒ <c>(false, "")</c>).</summary>
|
||||
public static HistorizeState Read(string? json)
|
||||
{
|
||||
var o = TagConfigJson.ParseOrNew(json);
|
||||
return new HistorizeState(
|
||||
TagConfigJson.GetBool(o, "isHistorized"),
|
||||
TagConfigJson.GetString(o, "historianTagname") ?? "");
|
||||
}
|
||||
|
||||
/// <summary>Merges the two history-intent keys into <paramref name="json"/>, preserving every other
|
||||
/// (driver-specific or unknown) key. <c>isHistorized</c> is dropped when false (absent ⇒ false at the
|
||||
/// composer); <c>historianTagname</c> is dropped when null/blank (absent ⇒ defaults to FullName) and
|
||||
/// trimmed otherwise.</summary>
|
||||
public static string Set(string? json, bool isHistorized, string? historianTagname)
|
||||
{
|
||||
var o = TagConfigJson.ParseOrNew(json);
|
||||
TagConfigJson.Set(o, "isHistorized", isHistorized ? true : null);
|
||||
TagConfigJson.Set(o, "historianTagname",
|
||||
string.IsNullOrWhiteSpace(historianTagname) ? null : historianTagname.Trim());
|
||||
return TagConfigJson.Serialize(o);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user