Task #149 — Modbus address-preview page + ImportEquipment help

The original task scope assumed a per-tag editor lived in EquipmentTab.razor
or a similar surface. Reading the codebase confirmed that's not the case:
tags are seeded via SQL (scripts/smoke/*) or arrive at runtime through
ITagDiscovery; the Admin UI has no per-tag CRUD page today. Equipment
import is for equipment metadata (Name / MachineCode / ZTag / SAPID /
Identification) — not tag rows.

Adjusted scope:

1. ModbusAddressPreview.razor — new standalone page at /modbus/address-preview.
   Hosts the ModbusAddressEditor component shipped in #145 + the family
   selector + a copy-pasteable grammar reference. Operators can sanity-check
   address-string syntax (40001:F:CDAB / HR1:I / V2000:F / D100:I etc.)
   without committing it to a config row first.

2. ImportEquipment.razor — appended a secondary alert banner clarifying
   that Modbus per-tag addressing isn't part of equipment import; points
   users at the Drivers tab + the new preview tool.

Builds clean against the existing Admin app. The actual per-tag CRUD UI is
still a separate piece of work — when it ships, it can drop in
ModbusAddressEditor directly. The preview page acts as the canonical
demonstration of how to use the component.

Razor caveat: the grammar reference uses literal `<...>` syntax tokens
that the Razor parser interprets as malformed elements when inlined in a
<pre> block. Held as a string field (_grammarReference) and rendered
through @ binding to sidestep the parser conflict.
This commit is contained in:
Joseph Doherty
2026-04-25 01:09:24 -04:00
parent 3b0e093002
commit 8de152df4f
2 changed files with 94 additions and 0 deletions

View File

@@ -26,6 +26,15 @@
reservation conflict exists. reservation conflict exists.
</div> </div>
<div class="alert alert-secondary small mb-3">
<strong>Per-tag addressing for Modbus drivers</strong> isn't part of equipment import —
tags are configured at the driver-instance level via the
<a href="/clusters/@ClusterId/draft/@GenerationId">Drivers tab</a>. Use the
<a href="/modbus/address-preview" target="_blank">address-preview tool</a> to sanity-check
grammar strings (<code>40001:F:CDAB</code>, <code>HR1:I</code>, <code>V2000</code> for
DL205 family, etc.) before pasting them into the driver config.
</div>
<div class="card mb-3"> <div class="card mb-3">
<div class="card-body"> <div class="card-body">
<div class="row g-3"> <div class="row g-3">

View File

@@ -0,0 +1,85 @@
@page "/modbus/address-preview"
@using ZB.MOM.WW.OtOpcUa.Driver.Modbus
@*
#149 — standalone preview / sanity-check tool for Modbus address strings. The Admin UI
doesn't yet have a per-tag CRUD surface (tags are seeded via SQL or arrive at runtime
through ITagDiscovery), so the ModbusAddressEditor component shipped in #145 needs a
page where operators can paste an address string and confirm it parses to what they
expect before committing it to a config row.
Doubles as a "did the parser ship correctly" smoke target for QA + a copy-pasteable
grammar reference for users skimming the docs.
*@
<PageTitle>Modbus address preview</PageTitle>
<div class="container py-4">
<h1>Modbus address preview</h1>
<p class="text-muted">
Paste an address string and watch the parser break it down field by field. Useful for
sanity-checking a tag spreadsheet row before adding it to a driver's <code>DriverConfig</code>.
Full grammar: <a href="https://github.com/" target="_blank">docs/v2/modbus-addressing.md</a>.
</p>
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">PLC family hint (drives the family-native branch)</label>
<select class="form-select" @bind="_family">
@foreach (var f in Enum.GetValues<ModbusFamily>())
{
<option value="@f">@f</option>
}
</select>
</div>
@if (_family == ModbusFamily.MELSEC)
{
<div class="col-md-4">
<label class="form-label">MELSEC sub-family</label>
<select class="form-select" @bind="_melsecSubFamily">
@foreach (var f in Enum.GetValues<MelsecFamily>())
{
<option value="@f">@f</option>
}
</select>
</div>
}
</div>
<div class="mt-4">
<ModbusAddressEditor @bind-AddressString="_address"
Family="_family"
MelsecSubFamily="_melsecSubFamily"/>
</div>
<h3 class="mt-5">Quick-reference grammar</h3>
<pre class="bg-light p-3 rounded small">@_grammarReference</pre>
</div>
@code {
private string? _address;
private ModbusFamily _family = ModbusFamily.Generic;
private MelsecFamily _melsecSubFamily = MelsecFamily.Q_L_iQR;
// Held as a const string rather than inline markup so the Razor parser doesn't try to
// interpret the angle-bracket grammar tokens as element open/close.
private const string _grammarReference = @"<region><offset>[.<bit>][:<type>[<len>]][:<order>][:<count>]
Examples (post-#146 type codes):
40001 HoldingRegisters[0], Int16
400001 same, 6-digit form
40001:F Float32 (HR[0..1])
40001:F:CDAB Float32 word-swapped
40001:STR20 20-char ASCII string
40001:S:5 Int16[5] array (3-field shorthand)
40001:I:CDAB:10 Int32[10] word-swapped (4-field strict)
40001.5 bit 5 of HR[0]
HR1:I Int32 via mnemonic region (matches Wonderware)
C100 Coil 100 (mnemonic, 1-based)
V2000:F:CDAB DL205 V-memory at PDU 1024 (Family=DL205)
D100:I MELSEC D-register 100, Int32 (Family=MELSEC)
Type codes: BOOL, S (Int16), US (UInt16), I (Int32), UI (UInt32),
I_64 (Int64), UI_64 (UInt64), F, D, BCD, BCD_32, STR<n>
Byte order: ABCD (BE default), CDAB (word-swap), BADC (byte-swap), DCBA (full reverse)";
}