feat(adminui): DriverTagPicker modal + 9 static address builders
- DriverTagPicker shell: modal chrome + per-driver picker body rendered as ChildContent. - 9 picker bodies (Modbus/AbCip/AbLegacy/S7/TwinCat/FOCAS/ OpcUaClient/Galaxy/Historian.Wonderware). 5 have computed builder logic + unit tests; 4 are free-text passthroughs (live browse for OPC UA + Galaxy is a documented follow-up). - Each typed driver page gets a "Pick address" button that opens the modal with the matching body. Picked address surfaces in the modal footer for manual copy — no JS interop in v1.
This commit is contained in:
+57
@@ -0,0 +1,57 @@
|
||||
@* Static AB CIP address builder: tag name + optional element index → tag[idx] or tag *@
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Tag name</label>
|
||||
<input type="text" class="form-control form-control-sm mono" placeholder="Program:Main.MyTag"
|
||||
@bind="_tagName" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">Use dot notation for nested tags or UDT members.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Element index (optional)</label>
|
||||
<input type="number" class="form-control form-control-sm" min="0"
|
||||
@bind="_elementIndex" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">Leave 0 for non-array tags (no index appended).</div>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<div class="form-check mb-2">
|
||||
<input type="checkbox" class="form-check-input" id="abCipUseIdx"
|
||||
@bind="_useIndex" @bind:after="OnChangedAsync" />
|
||||
<label class="form-check-label" for="abCipUseIdx">Append index</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _tagName = "";
|
||||
private int _elementIndex = 0;
|
||||
private bool _useIndex = false;
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = Build();
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = Build();
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private string Build()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_tagName))
|
||||
return "";
|
||||
return _useIndex ? $"{_tagName}[{_elementIndex}]" : _tagName;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers;
|
||||
|
||||
/// <summary>
|
||||
/// Pure static helper that converts AB Legacy PLC file-type + file-number + element
|
||||
/// into the canonical address string (e.g. N7:0).
|
||||
/// Extracted so unit tests can call it without bUnit.
|
||||
/// </summary>
|
||||
public static class AbLegacyAddressBuilder
|
||||
{
|
||||
public static string Build(string fileType, int fileNumber, int element)
|
||||
=> $"{fileType}{fileNumber}:{element}";
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
@* Static AB Legacy address builder: file type + file number + element → N7:0 *@
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">File type</label>
|
||||
<select class="form-select form-select-sm" @bind="_fileType" @bind:after="OnChangedAsync">
|
||||
<option value="N">N — Integer</option>
|
||||
<option value="B">B — Binary/Bit</option>
|
||||
<option value="F">F — Float</option>
|
||||
<option value="I">I — Input</option>
|
||||
<option value="O">O — Output</option>
|
||||
<option value="S">S — Status</option>
|
||||
<option value="T">T — Timer</option>
|
||||
<option value="C">C — Counter</option>
|
||||
<option value="R">R — Control</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">File number</label>
|
||||
<input type="number" class="form-control form-control-sm" min="0" max="999"
|
||||
@bind="_fileNumber" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">e.g. 7 for N7</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Element</label>
|
||||
<input type="number" class="form-control form-control-sm" min="0" max="9999"
|
||||
@bind="_element" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">e.g. 0 for N7:0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _fileType = "N";
|
||||
private int _fileNumber = 7;
|
||||
private int _element = 0;
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = AbLegacyAddressBuilder.Build(_fileType, _fileNumber, _element);
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = AbLegacyAddressBuilder.Build(_fileType, _fileNumber, _element);
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
@* Static FOCAS address builder: parameter group + parameter ID → axis:5 *@
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Parameter group</label>
|
||||
<select class="form-select form-select-sm" @bind="_group" @bind:after="OnChangedAsync">
|
||||
<option value="axis">axis</option>
|
||||
<option value="spindle">spindle</option>
|
||||
<option value="program">program</option>
|
||||
<option value="status">status</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Parameter ID</label>
|
||||
<input type="number" class="form-control form-control-sm" min="0"
|
||||
@bind="_parameterId" @bind:after="OnChangedAsync" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _group = "axis";
|
||||
private int _parameterId = 0;
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = FocasAddressBuilder.Build(_group, _parameterId);
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = FocasAddressBuilder.Build(_group, _parameterId);
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers;
|
||||
|
||||
/// <summary>
|
||||
/// Pure static helper that converts a FOCAS parameter group + parameter ID
|
||||
/// into the canonical address string (e.g. axis:5).
|
||||
/// Extracted so unit tests can call it without bUnit.
|
||||
/// </summary>
|
||||
public static class FocasAddressBuilder
|
||||
{
|
||||
public static string Build(string group, int parameterId)
|
||||
=> $"{group}:{parameterId}";
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
@* Static Galaxy address builder: tag_name.AttributeName free text → verbatim.
|
||||
Live Galaxy browse deferred to a follow-up phase. *@
|
||||
|
||||
<div class="alert alert-info py-2 px-3 mb-3 small">
|
||||
<strong>Note:</strong> Live Galaxy browse is deferred to a follow-up phase.
|
||||
Enter the tag and attribute name manually below.
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<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" />
|
||||
<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" />
|
||||
<div class="form-text">MXAccess attribute name.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _tagName = "";
|
||||
private string _attributeName = "";
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = Build();
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = Build();
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private string Build()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_tagName))
|
||||
return "";
|
||||
if (string.IsNullOrWhiteSpace(_attributeName))
|
||||
return _tagName;
|
||||
return $"{_tagName}.{_attributeName}";
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers;
|
||||
|
||||
/// <summary>
|
||||
/// Pure static helper that converts a Wonderware Historian tag name + retrieval mode
|
||||
/// + interval into the canonical address query string (e.g. MyTag?mode=Cyclic&interval=60).
|
||||
/// Extracted so unit tests can call it without bUnit.
|
||||
/// </summary>
|
||||
public static class HistorianWonderwareAddressBuilder
|
||||
{
|
||||
public static string Build(string tagName, string mode, int interval)
|
||||
=> $"{tagName}?mode={mode}&interval={interval}";
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
@* Static Wonderware Historian address builder: tag name + retrieval mode + interval
|
||||
→ MyTag?mode=Cyclic&interval=60 *@
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Tag name</label>
|
||||
<input type="text" class="form-control form-control-sm mono" placeholder="SysTimeHour"
|
||||
@bind="_tagName" @bind:after="OnChangedAsync" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Retrieval mode</label>
|
||||
<select class="form-select form-select-sm" @bind="_mode" @bind:after="OnChangedAsync">
|
||||
<option value="Last">Last</option>
|
||||
<option value="Cyclic">Cyclic</option>
|
||||
<option value="Delta">Delta</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Interval (seconds)</label>
|
||||
<input type="number" class="form-control form-control-sm" min="1"
|
||||
@bind="_interval" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">Polling/retrieval interval.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _tagName = "";
|
||||
private string _mode = "Cyclic";
|
||||
private int _interval = 60;
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = string.IsNullOrWhiteSpace(_tagName) ? "" : HistorianWonderwareAddressBuilder.Build(_tagName, _mode, _interval);
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = string.IsNullOrWhiteSpace(_tagName) ? "" : HistorianWonderwareAddressBuilder.Build(_tagName, _mode, _interval);
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers;
|
||||
|
||||
/// <summary>
|
||||
/// Pure static helper that converts Modbus register-type + offset + length into
|
||||
/// the canonical address string used by the Modbus driver (e.g. 4x00001-1).
|
||||
/// Extracted so unit tests can call it without bUnit.
|
||||
/// </summary>
|
||||
public static class ModbusAddressBuilder
|
||||
{
|
||||
public static string Build(string regType, int offset, int length)
|
||||
{
|
||||
var prefix = regType switch
|
||||
{
|
||||
"Coil" => "0x",
|
||||
"DiscreteInput" => "1x",
|
||||
"Input" => "3x",
|
||||
"Holding" => "4x",
|
||||
_ => "4x",
|
||||
};
|
||||
return $"{prefix}{offset:00000}-{length}";
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
@* Static Modbus address builder: register type + offset + length → 4x00001-4 *@
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Register type</label>
|
||||
<select class="form-select form-select-sm" @bind="_regType" @bind:after="OnChangedAsync">
|
||||
<option value="Coil">Coil (0x)</option>
|
||||
<option value="DiscreteInput">DiscreteInput (1x)</option>
|
||||
<option value="Input">Input (3x)</option>
|
||||
<option value="Holding">Holding (4x)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Offset</label>
|
||||
<input type="number" class="form-control form-control-sm" min="0" max="99999"
|
||||
@bind="_offset" @bind:after="OnChangedAsync" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Length</label>
|
||||
<input type="number" class="form-control form-control-sm" min="1" max="125"
|
||||
@bind="_length" @bind:after="OnChangedAsync" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _regType = "Holding";
|
||||
private int _offset = 1;
|
||||
private int _length = 1;
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = ModbusAddressBuilder.Build(_regType, _offset, _length);
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = ModbusAddressBuilder.Build(_regType, _offset, _length);
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
@* Static OPC UA Client address builder: NodeId free text → verbatim.
|
||||
Live browse deferred to a follow-up phase. *@
|
||||
|
||||
<div class="alert alert-info py-2 px-3 mb-3 small">
|
||||
<strong>Note:</strong> Live OPC UA node browse is deferred to a follow-up phase.
|
||||
Enter the NodeId string manually below.
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-10">
|
||||
<label class="form-label">NodeId</label>
|
||||
<input type="text" class="form-control form-control-sm mono" placeholder="ns=2;s=Channel.Device.Tag"
|
||||
@bind="_nodeId" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">
|
||||
OPC UA NodeId string, e.g. <code>ns=2;s=Channel.Device.Tag</code> or <code>i=1001</code>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _nodeId = "";
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = _nodeId;
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = _nodeId;
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers;
|
||||
|
||||
/// <summary>
|
||||
/// Pure static helper that converts S7 area + db-number + offset + data-type
|
||||
/// into the canonical S7 address string (e.g. DB10.DBD20:REAL, M0.0:X).
|
||||
/// Extracted so unit tests can call it without bUnit.
|
||||
/// </summary>
|
||||
public static class S7AddressBuilder
|
||||
{
|
||||
/// <param name="area">DB / M / I / Q</param>
|
||||
/// <param name="dbNumber">Only relevant when area == "DB".</param>
|
||||
/// <param name="offset">Byte offset (decimal).</param>
|
||||
/// <param name="s7Type">X / B / W / D / REAL</param>
|
||||
public static string Build(string area, int dbNumber, int offset, string s7Type)
|
||||
{
|
||||
if (area == "DB")
|
||||
{
|
||||
// e.g. DB10.DBD20:REAL / DB1.DBX0.0:X / DB5.DBW4:W
|
||||
var qualifier = s7Type switch
|
||||
{
|
||||
"X" => $"DBX{offset}.0",
|
||||
"B" => $"DBB{offset}",
|
||||
"W" => $"DBW{offset}",
|
||||
"D" => $"DBD{offset}",
|
||||
"REAL" => $"DBD{offset}",
|
||||
_ => $"DBD{offset}",
|
||||
};
|
||||
return $"DB{dbNumber}.{qualifier}:{s7Type}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// e.g. M0.0:X / I4:B / Q2:W
|
||||
var offsetStr = s7Type == "X" ? $"{offset}.0" : $"{offset}";
|
||||
return $"{area}{offsetStr}:{s7Type}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
@* Static S7 address builder: area + db-number + offset + type → DB10.DBD20:REAL / M0.0:X *@
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Area</label>
|
||||
<select class="form-select form-select-sm" @bind="_area" @bind:after="OnChangedAsync">
|
||||
<option value="DB">DB — Data Block</option>
|
||||
<option value="M">M — Merker</option>
|
||||
<option value="I">I — Input</option>
|
||||
<option value="Q">Q — Output</option>
|
||||
</select>
|
||||
</div>
|
||||
@if (_area == "DB")
|
||||
{
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">DB number</label>
|
||||
<input type="number" class="form-control form-control-sm" min="1" max="65535"
|
||||
@bind="_dbNumber" @bind:after="OnChangedAsync" />
|
||||
</div>
|
||||
}
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Offset (bytes)</label>
|
||||
<input type="number" class="form-control form-control-sm" min="0" max="65535"
|
||||
@bind="_offset" @bind:after="OnChangedAsync" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">S7 type</label>
|
||||
<select class="form-select form-select-sm" @bind="_s7Type" @bind:after="OnChangedAsync">
|
||||
<option value="X">X — Bit</option>
|
||||
<option value="B">B — Byte</option>
|
||||
<option value="W">W — Word</option>
|
||||
<option value="D">D — DWord</option>
|
||||
<option value="REAL">REAL — Float</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _area = "DB";
|
||||
private int _dbNumber = 1;
|
||||
private int _offset = 0;
|
||||
private string _s7Type = "REAL";
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = S7AddressBuilder.Build(_area, _dbNumber, _offset, _s7Type);
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = S7AddressBuilder.Build(_area, _dbNumber, _offset, _s7Type);
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
@* Static TwinCAT address builder: ADS variable name (free text) → verbatim *@
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">ADS variable name</label>
|
||||
<input type="text" class="form-control form-control-sm mono" placeholder="MAIN.fValue"
|
||||
@bind="_varName" @bind:after="OnChangedAsync" />
|
||||
<div class="form-text">
|
||||
Full ADS symbol path, e.g. <code>MAIN.fValue</code> or <code>GVL.iCounter</code>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="text-muted small">Result:</span>
|
||||
<code class="mono ms-2">@_built</code>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||
|
||||
private string _varName = "";
|
||||
private string _built = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_built = _varName;
|
||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
|
||||
private async Task OnChangedAsync()
|
||||
{
|
||||
_built = _varName;
|
||||
await CurrentAddressChanged.InvokeAsync(_built);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user