Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c4cc381
ux(write): keep Browser toolbar caption and footprint stable as Write
masarray Oct 23, 2026
2e48dc6
ux(write): use supplied Material pencil vector and fixed Write label
masarray Oct 23, 2026
b519a1b
ux(write): replace bulky single-DA form with compact IEDScout-style N…
masarray Oct 23, 2026
58452e1
ux(write): type-driven Boolean dropdown and current-value prefill; pr…
masarray Oct 23, 2026
c840032
ux(write): compact multi-DA editor with Name/Current/Value and type-d…
masarray Oct 23, 2026
c8c8f0d
ux(write): bind multi-DA Boolean dropdown after live GVA and prefill …
masarray Oct 23, 2026
e5a9739
test(write-ux): enforce stable pencil Write button and live typed IED…
masarray Oct 23, 2026
93dfa25
fix(write-ux): preserve Material pencil button content through select…
masarray Oct 23, 2026
a261c94
test(write-ux): assert selection refresh never destroys pencil visual
masarray Oct 23, 2026
459df5e
ux(write): lock multi-DA inputs until live GVA/Read, no empty or inve…
masarray Oct 23, 2026
6a08c2f
ux(write): enable per-DA typed input only after inspected GVA
masarray Oct 23, 2026
d0ce733
test(write-ux): preserve selector routing while toolbar keeps fixed M…
masarray Oct 23, 2026
4cf9d6f
test(write-ux): preserve selector routing while toolbar keeps fixed M…
masarray Oct 23, 2026
53c20ce
test(write): enforce typed inline editor behavior and fixed pencil ac…
masarray Oct 23, 2026
442134e
chore(cleanroom): remove external product name from test path
masarray Oct 23, 2026
5ed6b6b
test(write): verify all batch editors stay locked until live MMS insp…
masarray Oct 23, 2026
24734b9
fix(build): reconstruct single-DA typed Write UI code from intact FAT…
masarray Oct 23, 2026
9e781aa
fix(build): regenerate batch editor from healthy base with verified G…
masarray Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 30 additions & 24 deletions BrowserBatchWriteWindow.xaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Window x:Class="ArIED61850Tester.BrowserBatchWriteWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Write selected Data Attributes · ARSAS"
Width="760" Height="580" MinWidth="650" MinHeight="420" MaxHeight="690"
Title="Write Data to IED"
Width="660" Height="490" MinWidth="570" MinHeight="360" MaxHeight="700"
WindowStartupLocation="CenterOwner" ResizeMode="CanResize"
Background="#F5F7FA" FontFamily="{StaticResource AppFontFamily}" FontSize="11.5"
Loaded="Window_Loaded" Closed="Window_Closed">
Expand All @@ -14,15 +14,15 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="2,0,2,10">
<TextBlock Text="Write selected Data Attributes" Foreground="#293E56"
<TextBlock Text="Write Data to IED" Foreground="#293E56"
FontWeight="SemiBold" FontSize="15"/>
<TextBlock x:Name="IdentityLabel" Foreground="#5F7389" FontSize="11"
TextTrimming="CharacterEllipsis" Margin="0,3,0,0"/>
<TextBlock Text="One verified DO · exact FC/type · no IEC Control Operate"
Foreground="#637990" FontSize="10.5" Margin="0,3,0,0"/>
</StackPanel>
<TextBlock Grid.Row="1" Text="Select individual rows, enter new values and inspect the live MMS type before confirming. Writes are sequential, never atomic."
TextWrapping="Wrap" Foreground="#596D84" FontSize="10.5" Margin="2,0,2,9"/>
<TextBlock Grid.Row="1" Text="Check attributes and choose values. Inspect verifies the current IED types before Write."
TextTrimming="CharacterEllipsis" Foreground="#596D84" FontSize="10.5" Margin="2,0,2,7"/>
<DataGrid x:Name="RowsGrid" Grid.Row="2" ItemsSource="{Binding Rows}"
Style="{StaticResource ModernDataGrid}" IsReadOnly="False"
AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False"
Expand All @@ -32,30 +32,36 @@
VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling"
EnableRowVirtualization="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Write" Width="53">
<DataGridTemplateColumn Header=" " Width="36">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Eligible}" ToolTip="{Binding Status}"/>
<CheckBox IsChecked="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Eligible}" VerticalAlignment="Center"
HorizontalAlignment="Center" ToolTip="{Binding Status}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="DA (exact MMS)" Binding="{Binding ReferenceText}"
IsReadOnly="True" Width="2.4*" MinWidth="220"/>
<DataGridTextColumn Header="FC" Binding="{Binding Fc}" IsReadOnly="True" Width="45"/>
<DataGridTextColumn Header="MMS type" Binding="{Binding Type}" IsReadOnly="True" Width="89"/>
<DataGridTextColumn Header="Before" Binding="{Binding Old}" IsReadOnly="True" Width="90"/>
<DataGridTemplateColumn Header="New value" Width="1.1*" MinWidth="108">
<DataGridTextColumn Header="Data Attribute" Binding="{Binding DisplayName}"
Width="2*" MinWidth="145" IsReadOnly="True"/>
<DataGridTextColumn Header="FC" Binding="{Binding Fc}" Width="43" IsReadOnly="True"/>
<DataGridTextColumn Header="Current" Binding="{Binding Old}" Width="1*" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Value" Width="1.3*" MinWidth="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding NewValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Eligible}" Padding="5,3" FontSize="11"/>
<Grid>
<TextBox Text="{Binding NewValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEdit}" Visibility="{Binding TextEditorVisibility}"
Padding="6,3" ToolTip="{Binding Status}"/>
<ComboBox ItemsSource="{Binding BooleanChoices}"
SelectedItem="{Binding NewValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding BooleanEditorVisibility}"
IsEnabled="{Binding CanEdit}" Padding="5,1"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Result / reason" Binding="{Binding Status}"
IsReadOnly="True" Width="1.7*" MinWidth="145"/>
<DataGridTextColumn Header="Status" Binding="{Binding Status}"
Width="1.4*" MinWidth="120" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
<Grid Grid.Row="3" Margin="1,10,1,0">
Expand All @@ -72,14 +78,14 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="A confirmed Write is not physical feedback."
<TextBlock Grid.Column="0" Text="Writes are sequential."
Foreground="#70849B" FontSize="10" TextWrapping="Wrap"
VerticalAlignment="Center" Margin="0,0,8,0"/>
<Button x:Name="InspectButton" Grid.Column="1" Content="Inspect selected"
Style="{StaticResource SoftButton}" Width="117" Height="31"
<Button x:Name="InspectButton" Grid.Column="1" Content="Inspect"
Style="{StaticResource SoftButton}" Width="89" Height="31"
Margin="0,0,7,0" Click="Inspect_Click"/>
<Button x:Name="WriteButton" Grid.Column="2" Content="Write selected…"
Width="118" Height="31" IsEnabled="False" Background="#255F99"
<Button x:Name="WriteButton" Grid.Column="2" Content="Write"
Width="94" Height="31" IsEnabled="False" Background="#255F99"
Foreground="White" BorderBrush="#255F99"
Margin="0,0,7,0" Click="Write_Click"/>
<Button Grid.Column="3" Content="Close" Width="72" Height="31"
Expand Down
34 changes: 31 additions & 3 deletions BrowserBatchWriteWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ private void SetBusy(bool busy)
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var work = SystemParameters.WorkArea;
Height = Math.Max(MinHeight, Math.Min(580, work.Height - 26));
Width = Math.Max(MinWidth, Math.Min(760, work.Width - 26));
Height = Math.Max(MinHeight, Math.Min(490, work.Height - 26));
Width = Math.Max(MinWidth, Math.Min(660, work.Width - 26));
}

private BrowserBatchWriteRow[] CheckedRows()
Expand Down Expand Up @@ -142,13 +142,15 @@ private async void Inspect_Click(object sender, RoutedEventArgs e)
throw new InvalidOperationException("IED association changed during inspection.");
StatusLabel.Text = $"Read-only inspect {i + 1}/{selected.Length}: {row.ReferenceText}";
row.Preview = null;
row.NotifyEditorChanged();
var preview = await _runtime.PreviewBrowserDataAttributeWriteAsync(
_device, row.Reference!.Value, _lifetime.Token);
if (!CurrentAssociation)
throw new InvalidOperationException("IED association changed during inspection.");
row.Preview = preview.IsAvailable ? preview : null;
row.Type = preview.Type?.MmsType ?? "unverified";
row.Old = preview.CurrentText;
row.ApplyLiveEditor(preview.Type?.MmsType == "boolean", preview.CurrentText);
row.Status = preview.Message;
if (row.Preview is null)
{
Expand Down Expand Up @@ -270,6 +272,7 @@ private async void Write_Click(object sender, RoutedEventArgs e)
selected[i].Status = result.Rows[i].State?.ToString() ?? "Unattempted";
selected[i].Status += ": " + result.Rows[i].Message;
selected[i].Preview = null;
selected[i].NotifyEditorChanged();
}
StatusLabel.Text =
$"Readback verified: {result.Verified}; rejected: {result.Rejected}; " +
Expand Down Expand Up @@ -302,15 +305,17 @@ private async void Write_Click(object sender, RoutedEventArgs e)
public sealed class BrowserBatchWriteRow : INotifyPropertyChanged
{
private bool _selected;
private string _newValue = "";
private string _newValue = "Inspect first";
private string _status;
private string _type = "Inspect first";
private string _old = "—";

public MmsObjectReference? Reference { get; }
public string ReferenceText { get; }
public string DisplayName { get; }
public string Fc { get; }
public bool Eligible { get; }
public bool CanEdit => Eligible && Preview is { IsAvailable: true, Type: not null };
public bool HasEdited { get; private set; }
public IedBrowserNativeWritePreview? Preview { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
Expand All @@ -321,11 +326,34 @@ public BrowserBatchWriteRow(
{
Reference = reference;
ReferenceText = referenceText;
DisplayName = referenceText.Split((char)36).LastOrDefault() ?? referenceText;
Fc = fc;
Eligible = eligible;
_status = reason;
}

public void NotifyEditorChanged() => Changed(nameof(CanEdit));
public IReadOnlyList<string> BooleanChoices { get; } = new[] { "true", "false" };
public Visibility BooleanEditorVisibility => _booleanEditor ? Visibility.Visible : Visibility.Collapsed;
public Visibility TextEditorVisibility => _booleanEditor ? Visibility.Collapsed : Visibility.Visible;
private bool _booleanEditor;

public void ApplyLiveEditor(bool boolean, string actualValue)
{
if (_booleanEditor != boolean)
{
_booleanEditor = boolean;
Changed(nameof(BooleanEditorVisibility));
Changed(nameof(TextEditorVisibility));
}
if (!HasEdited && !string.IsNullOrWhiteSpace(actualValue))
{
_newValue = actualValue;
Changed(nameof(NewValue));
}
Changed(nameof(CanEdit));
}

public bool Selected
{
get => _selected;
Expand Down
Loading
Loading