feat(adminui): writable checkbox in driver-typed tag editors (R2-11, 05/UNDER-6)
Models gain an optional bool? Writable (absent stays omitted; explicit false round-trips, unknown keys preserved); the six editors gain a Writable checkbox (FOCAS disabled+read-only hint). 9 model round-trip tests green. docker-dev /run verify of one editor deferred-live.
This commit is contained in:
@@ -16,6 +16,10 @@ public sealed class AbCipTagConfigModel
|
||||
/// <summary>Logix atomic type, or <c>AbCipDataType.Structure</c> for UDT-typed tags.</summary>
|
||||
public AbCipDataType DataType { get; set; } = AbCipDataType.DInt;
|
||||
|
||||
/// <summary>Optional writable flag (R2-11); <c>null</c> ⇒ omitted (driver default true), explicit
|
||||
/// <c>false</c> ⇒ read-only. Preserved across load→save.</summary>
|
||||
public bool? Writable { get; set; }
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -30,6 +34,7 @@ public sealed class AbCipTagConfigModel
|
||||
DeviceHostAddress = TagConfigJson.GetString(o, "deviceHostAddress") ?? "",
|
||||
TagPath = TagConfigJson.GetString(o, "tagPath") ?? "",
|
||||
DataType = TagConfigJson.GetEnum(o, "dataType", AbCipDataType.DInt),
|
||||
Writable = TagConfigJson.GetBoolNullable(o, "writable"),
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
@@ -42,6 +47,7 @@ public sealed class AbCipTagConfigModel
|
||||
TagConfigJson.Set(_bag, "deviceHostAddress", string.IsNullOrWhiteSpace(DeviceHostAddress) ? null : DeviceHostAddress.Trim());
|
||||
TagConfigJson.Set(_bag, "tagPath", TagPath.Trim());
|
||||
TagConfigJson.Set(_bag, "dataType", DataType);
|
||||
TagConfigJson.Set(_bag, "writable", Writable);
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ public sealed class AbLegacyTagConfigModel
|
||||
/// <summary>PCCC data type that maps onto SLC / MicroLogix / PLC-5 files.</summary>
|
||||
public AbLegacyDataType DataType { get; set; } = AbLegacyDataType.Int;
|
||||
|
||||
/// <summary>Optional writable flag (R2-11); <c>null</c> ⇒ omitted (driver default true), explicit
|
||||
/// <c>false</c> ⇒ read-only. Preserved across load→save.</summary>
|
||||
public bool? Writable { get; set; }
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -30,6 +34,7 @@ public sealed class AbLegacyTagConfigModel
|
||||
DeviceHostAddress = TagConfigJson.GetString(o, "deviceHostAddress") ?? "",
|
||||
Address = TagConfigJson.GetString(o, "address") ?? "",
|
||||
DataType = TagConfigJson.GetEnum(o, "dataType", AbLegacyDataType.Int),
|
||||
Writable = TagConfigJson.GetBoolNullable(o, "writable"),
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
@@ -42,6 +47,7 @@ public sealed class AbLegacyTagConfigModel
|
||||
TagConfigJson.Set(_bag, "deviceHostAddress", string.IsNullOrWhiteSpace(DeviceHostAddress) ? null : DeviceHostAddress.Trim());
|
||||
TagConfigJson.Set(_bag, "address", Address.Trim());
|
||||
TagConfigJson.Set(_bag, "dataType", DataType);
|
||||
TagConfigJson.Set(_bag, "writable", Writable);
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ public sealed class ModbusTagConfigModel
|
||||
/// <summary>String length in characters for String tags.</summary>
|
||||
public int StringLength { get; set; }
|
||||
|
||||
/// <summary>Optional writable flag (R2-11). <c>null</c> ⇒ the key is omitted (driver default true);
|
||||
/// an explicit <c>false</c> makes the equipment tag read-only. Preserved across load→save so an absent
|
||||
/// key stays absent.</summary>
|
||||
public bool? Writable { get; set; }
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -42,6 +47,7 @@ public sealed class ModbusTagConfigModel
|
||||
ByteOrder = TagConfigJson.GetEnum(o, "byteOrder", ModbusByteOrder.BigEndian),
|
||||
BitIndex = TagConfigJson.GetInt(o, "bitIndex"),
|
||||
StringLength = TagConfigJson.GetInt(o, "stringLength"),
|
||||
Writable = TagConfigJson.GetBoolNullable(o, "writable"),
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
@@ -57,6 +63,7 @@ public sealed class ModbusTagConfigModel
|
||||
TagConfigJson.Set(_bag, "byteOrder", ByteOrder);
|
||||
TagConfigJson.Set(_bag, "bitIndex", BitIndex);
|
||||
TagConfigJson.Set(_bag, "stringLength", StringLength);
|
||||
TagConfigJson.Set(_bag, "writable", Writable);
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ public sealed class S7TagConfigModel
|
||||
/// <summary>For <c>DataType = String</c>: S7-string max length (max 254).</summary>
|
||||
public int StringLength { get; set; }
|
||||
|
||||
/// <summary>Optional writable flag (R2-11); <c>null</c> ⇒ omitted (driver default true), explicit
|
||||
/// <c>false</c> ⇒ read-only. Preserved across load→save.</summary>
|
||||
public bool? Writable { get; set; }
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -30,6 +34,7 @@ public sealed class S7TagConfigModel
|
||||
Address = TagConfigJson.GetString(o, "address") ?? "",
|
||||
DataType = TagConfigJson.GetEnum(o, "dataType", S7DataType.Int16),
|
||||
StringLength = TagConfigJson.GetInt(o, "stringLength"),
|
||||
Writable = TagConfigJson.GetBoolNullable(o, "writable"),
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
@@ -42,6 +47,7 @@ public sealed class S7TagConfigModel
|
||||
TagConfigJson.Set(_bag, "address", Address.Trim());
|
||||
TagConfigJson.Set(_bag, "dataType", DataType);
|
||||
TagConfigJson.Set(_bag, "stringLength", StringLength);
|
||||
TagConfigJson.Set(_bag, "writable", Writable);
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,14 @@ public static class TagConfigJson
|
||||
public static bool GetBool(JsonObject o, string name, bool fallback = false)
|
||||
=> o.TryGetPropertyValue(name, out var n) && n is JsonValue v && v.TryGetValue<bool>(out var b) ? b : fallback;
|
||||
|
||||
/// <summary>Reads a bool value, or <c>null</c> when the property is absent/null/non-boolean — so an
|
||||
/// absent key stays absent through a load→save (distinguishes "not set" from an explicit false).</summary>
|
||||
/// <param name="o">The JSON object to read from.</param>
|
||||
/// <param name="name">The property name to read.</param>
|
||||
/// <returns>The bool value, or <c>null</c> when absent/null/non-boolean.</returns>
|
||||
public static bool? GetBoolNullable(JsonObject o, string name)
|
||||
=> o.TryGetPropertyValue(name, out var n) && n is JsonValue v && v.TryGetValue<bool>(out var b) ? b : null;
|
||||
|
||||
/// <summary>Reads an enum by its serialised name, or <paramref name="fallback"/> if absent/unparseable.</summary>
|
||||
/// <typeparam name="TEnum">The enum type to parse.</typeparam>
|
||||
/// <param name="o">The JSON object to read from.</param>
|
||||
|
||||
@@ -16,6 +16,10 @@ public sealed class TwinCATTagConfigModel
|
||||
/// <summary>TwinCAT / IEC 61131-3 atomic data type.</summary>
|
||||
public TwinCATDataType DataType { get; set; } = TwinCATDataType.DInt;
|
||||
|
||||
/// <summary>Optional writable flag (R2-11); <c>null</c> ⇒ omitted (driver default true), explicit
|
||||
/// <c>false</c> ⇒ read-only. Preserved across load→save.</summary>
|
||||
public bool? Writable { get; set; }
|
||||
|
||||
private JsonObject _bag = new();
|
||||
|
||||
/// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
|
||||
@@ -30,6 +34,7 @@ public sealed class TwinCATTagConfigModel
|
||||
DeviceHostAddress = TagConfigJson.GetString(o, "deviceHostAddress") ?? "",
|
||||
SymbolPath = TagConfigJson.GetString(o, "symbolPath") ?? "",
|
||||
DataType = TagConfigJson.GetEnum(o, "dataType", TwinCATDataType.DInt),
|
||||
Writable = TagConfigJson.GetBoolNullable(o, "writable"),
|
||||
_bag = o,
|
||||
};
|
||||
}
|
||||
@@ -42,6 +47,7 @@ public sealed class TwinCATTagConfigModel
|
||||
TagConfigJson.Set(_bag, "deviceHostAddress", string.IsNullOrWhiteSpace(DeviceHostAddress) ? null : DeviceHostAddress.Trim());
|
||||
TagConfigJson.Set(_bag, "symbolPath", SymbolPath.Trim());
|
||||
TagConfigJson.Set(_bag, "dataType", DataType);
|
||||
TagConfigJson.Set(_bag, "writable", Writable);
|
||||
return TagConfigJson.Serialize(_bag);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user