Render array values and element type in the Browse panel
The subscription panel showed "(array)" for any array-valued tag and "Unspecified" for its type. A scalar MxValue carries its type in MxValue.DataType, but an array leaves that Unspecified and carries the element type and dimensions on the MxArray itself. DashboardMxValueFormatter.FormatValue now joins the typed MxArray elements (e.g. "[1.5, 2.25, 3]"), capped at 24 elements with a "... N total" suffix, and FormatDataType reads the element type and dimensions off the array (e.g. "Double[10]"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,9 @@ namespace MxGateway.Server.Dashboard;
|
||||
/// </summary>
|
||||
public static class DashboardMxValueFormatter
|
||||
{
|
||||
/// <summary>Maximum array elements rendered inline before the value is truncated.</summary>
|
||||
private const int MaxArrayElements = 24;
|
||||
|
||||
/// <summary>Formats the value payload of an <see cref="MxValue"/>.</summary>
|
||||
/// <param name="value">The value to format; may be null.</param>
|
||||
/// <returns>A display string — never null.</returns>
|
||||
@@ -37,7 +40,7 @@ public static class DashboardMxValueFormatter
|
||||
.ToDateTimeOffset()
|
||||
.UtcDateTime
|
||||
.ToString("yyyy-MM-dd HH:mm:ss.fff 'UTC'", CultureInfo.InvariantCulture),
|
||||
MxValue.KindOneofCase.ArrayValue => "(array)",
|
||||
MxValue.KindOneofCase.ArrayValue => FormatArray(value.ArrayValue),
|
||||
MxValue.KindOneofCase.RawValue => $"({value.RawValue.Length} bytes)",
|
||||
_ => "-",
|
||||
};
|
||||
@@ -45,9 +48,63 @@ public static class DashboardMxValueFormatter
|
||||
|
||||
/// <summary>Formats the MXAccess data type of an <see cref="MxValue"/>.</summary>
|
||||
/// <param name="value">The value whose data type to describe; may be null.</param>
|
||||
/// <returns>The data-type name — never null.</returns>
|
||||
/// <returns>The data-type name — never null. Arrays render as <c>Element[dims]</c>.</returns>
|
||||
public static string FormatDataType(MxValue? value)
|
||||
{
|
||||
return value is null ? "-" : value.DataType.ToString();
|
||||
if (value is null)
|
||||
{
|
||||
return "-";
|
||||
}
|
||||
|
||||
// A scalar carries its type in MxValue.DataType, but an array leaves
|
||||
// that Unspecified and carries the element type on the MxArray itself.
|
||||
return value.KindCase == MxValue.KindOneofCase.ArrayValue
|
||||
? FormatArrayDataType(value.ArrayValue)
|
||||
: value.DataType.ToString();
|
||||
}
|
||||
|
||||
private static string FormatArrayDataType(MxArray array)
|
||||
{
|
||||
string dimensions = array.Dimensions.Count > 0
|
||||
? string.Join(",", array.Dimensions)
|
||||
: string.Empty;
|
||||
return $"{array.ElementDataType}[{dimensions}]";
|
||||
}
|
||||
|
||||
private static string FormatArray(MxArray array)
|
||||
{
|
||||
IReadOnlyList<string> elements = array.ValuesCase switch
|
||||
{
|
||||
MxArray.ValuesOneofCase.BoolValues =>
|
||||
array.BoolValues.Values.Select(item => item ? "true" : "false").ToArray(),
|
||||
MxArray.ValuesOneofCase.Int32Values =>
|
||||
array.Int32Values.Values.Select(item => item.ToString(CultureInfo.InvariantCulture)).ToArray(),
|
||||
MxArray.ValuesOneofCase.Int64Values =>
|
||||
array.Int64Values.Values.Select(item => item.ToString(CultureInfo.InvariantCulture)).ToArray(),
|
||||
MxArray.ValuesOneofCase.FloatValues =>
|
||||
array.FloatValues.Values.Select(item => item.ToString("G7", CultureInfo.InvariantCulture)).ToArray(),
|
||||
MxArray.ValuesOneofCase.DoubleValues =>
|
||||
array.DoubleValues.Values.Select(item => item.ToString("G15", CultureInfo.InvariantCulture)).ToArray(),
|
||||
MxArray.ValuesOneofCase.StringValues =>
|
||||
array.StringValues.Values.Select(item => $"\"{item}\"").ToArray(),
|
||||
MxArray.ValuesOneofCase.TimestampValues =>
|
||||
array.TimestampValues.Values
|
||||
.Select(item => item.ToDateTimeOffset().UtcDateTime
|
||||
.ToString("yyyy-MM-dd HH:mm:ss 'UTC'", CultureInfo.InvariantCulture))
|
||||
.ToArray(),
|
||||
MxArray.ValuesOneofCase.RawValues =>
|
||||
array.RawValues.Values.Select(item => $"({item.Length} bytes)").ToArray(),
|
||||
_ => [],
|
||||
};
|
||||
|
||||
if (elements.Count == 0)
|
||||
{
|
||||
return "[]";
|
||||
}
|
||||
|
||||
string body = string.Join(", ", elements.Take(MaxArrayElements));
|
||||
return elements.Count > MaxArrayElements
|
||||
? $"[{body}, … {elements.Count} total]"
|
||||
: $"[{body}]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,4 +126,17 @@ public sealed class DashboardBrowseAndAlarmModelTests
|
||||
Assert.True(unackedRow.IsUnacknowledged);
|
||||
Assert.False(ackedRow.IsUnacknowledged);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatValue_AndDataType_RenderArrayElementsAndElementType()
|
||||
{
|
||||
MxArray array = new() { ElementDataType = MxDataType.Double };
|
||||
array.Dimensions.Add(3u);
|
||||
array.DoubleValues = new DoubleArray();
|
||||
array.DoubleValues.Values.Add(new[] { 1.5, 2.25, 3.0 });
|
||||
MxValue value = new() { ArrayValue = array };
|
||||
|
||||
Assert.Equal("[1.5, 2.25, 3]", DashboardMxValueFormatter.FormatValue(value));
|
||||
Assert.Equal("Double[3]", DashboardMxValueFormatter.FormatDataType(value));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user