feat(uns): Modbus typed tag-config editor (F-uns-1 T3)

This commit is contained in:
Joseph Doherty
2026-06-09 09:31:01 -04:00
parent fd9fa75d0e
commit 5990b673cc
4 changed files with 208 additions and 2 deletions
@@ -0,0 +1,62 @@
using System.Text.Json.Nodes;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
/// <summary>Typed working model for a Modbus tag's TagConfig JSON (the driver-specific addressing/encoding
/// fields; name/writable live on the Tag entity). Preserves unrecognised JSON keys across a load→save.</summary>
public sealed class ModbusTagConfigModel
{
/// <summary>Register region (HoldingRegisters/InputRegisters/Coils/DiscreteInputs).</summary>
public ModbusRegion Region { get; set; } = ModbusRegion.HoldingRegisters;
/// <summary>Starting register/coil address.</summary>
public int Address { get; set; }
/// <summary>Wire value type the register block decodes to.</summary>
public ModbusDataType DataType { get; set; } = ModbusDataType.Int16;
/// <summary>Word/byte ordering for multi-register values.</summary>
public ModbusByteOrder ByteOrder { get; set; } = ModbusByteOrder.BigEndian;
/// <summary>Bit index (0-15) for BitInRegister tags.</summary>
public int BitIndex { get; set; }
/// <summary>String length in characters for String tags.</summary>
public int StringLength { get; set; }
private JsonObject _bag = new();
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
/// every original key (so fields this editor doesn't expose survive a load→save).</summary>
public static ModbusTagConfigModel FromJson(string? json)
{
var o = TagConfigJson.ParseOrNew(json);
return new ModbusTagConfigModel
{
Region = TagConfigJson.GetEnum(o, "region", ModbusRegion.HoldingRegisters),
Address = TagConfigJson.GetInt(o, "address"),
DataType = TagConfigJson.GetEnum(o, "dataType", ModbusDataType.Int16),
ByteOrder = TagConfigJson.GetEnum(o, "byteOrder", ModbusByteOrder.BigEndian),
BitIndex = TagConfigJson.GetInt(o, "bitIndex"),
StringLength = TagConfigJson.GetInt(o, "stringLength"),
_bag = o,
};
}
/// <summary>Serialises this model back to a TagConfig JSON string, writing the six exposed fields
/// (enums as their name strings) over the preserved key bag.</summary>
public string ToJson()
{
TagConfigJson.Set(_bag, "region", Region);
TagConfigJson.Set(_bag, "address", Address);
TagConfigJson.Set(_bag, "dataType", DataType);
TagConfigJson.Set(_bag, "byteOrder", ByteOrder);
TagConfigJson.Set(_bag, "bitIndex", BitIndex);
TagConfigJson.Set(_bag, "stringLength", StringLength);
return TagConfigJson.Serialize(_bag);
}
/// <summary>Validation hook; returns an error message or null when the model is valid.</summary>
public string? Validate() => null;
}
@@ -10,8 +10,8 @@ public static class TagConfigEditorMap
private static readonly IReadOnlyDictionary<string, Type> Map =
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
{
// Editors registered by later tasks, e.g.:
// ["ModbusTcp"] = typeof(Components.Shared.Uns.TagEditors.ModbusTagConfigEditor),
["ModbusTcp"] = typeof(Components.Shared.Uns.TagEditors.ModbusTagConfigEditor),
// Further editors registered by later tasks, e.g. ["AbCip"] = typeof(...).
};
/// <summary>Returns the editor component type for a driver type, or null if none is registered.</summary>