chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)

Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.

- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
  the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
  mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
  integration, install).

Build green (0 errors); unit tests pass. Docs left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-17 01:55:28 -04:00
parent 69f02fed7f
commit a25593a9c6
1044 changed files with 365 additions and 343 deletions
@@ -0,0 +1,162 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ZB.MOM.WW.OtOpcUa.Client.UI.ViewModels"
xmlns:views="using:ZB.MOM.WW.OtOpcUa.Client.UI.Views"
x:Class="ZB.MOM.WW.OtOpcUa.Client.UI.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Title="OPC UA Client"
Width="1200"
Height="800">
<DockPanel>
<!-- Top Connection Bar -->
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="12">
<StackPanel Spacing="4">
<!-- Always visible: URL + Connect/Disconnect -->
<StackPanel Orientation="Horizontal" Spacing="8">
<StackPanel Spacing="2" VerticalAlignment="Bottom">
<TextBlock Text="Endpoint URL" FontSize="11" Foreground="Gray" />
<TextBox Text="{Binding EndpointUrl}"
Width="400"
Watermark="opc.tcp://host:port" />
</StackPanel>
<Button Content="Connect"
Command="{Binding ConnectCommand}"
VerticalAlignment="Bottom" Padding="16,6" />
<Button Content="Disconnect"
Command="{Binding DisconnectCommand}"
VerticalAlignment="Bottom" Padding="16,6" />
</StackPanel>
<!-- Expandable settings -->
<Expander Header="Connection Settings" Padding="0,4">
<StackPanel Spacing="10" Margin="4,8,4,4">
<!-- Row 1: Authentication -->
<StackPanel Orientation="Horizontal" Spacing="12">
<StackPanel Spacing="2">
<TextBlock Text="Username" FontSize="11" Foreground="Gray" />
<TextBox Text="{Binding Username}"
Width="160"
Watermark="(anonymous)" />
</StackPanel>
<StackPanel Spacing="2">
<TextBlock Text="Password" FontSize="11" Foreground="Gray" />
<TextBox Text="{Binding Password}"
Width="160"
Watermark="(none)"
PasswordChar="*" />
</StackPanel>
<StackPanel Spacing="2">
<TextBlock Text="Security Mode" FontSize="11" Foreground="Gray" />
<ComboBox ItemsSource="{Binding SecurityModes}"
SelectedItem="{Binding SelectedSecurityMode}"
Width="160" />
</StackPanel>
</StackPanel>
<!-- Row 2: Failover + Session -->
<StackPanel Orientation="Horizontal" Spacing="12">
<StackPanel Spacing="2">
<TextBlock Text="Failover URLs (comma-separated)" FontSize="11" Foreground="Gray" />
<TextBox Text="{Binding FailoverUrls}"
Width="400"
Watermark="opc.tcp://backup1:4840, opc.tcp://backup2:4840" />
</StackPanel>
<StackPanel Spacing="2">
<TextBlock Text="Session Timeout (s)" FontSize="11" Foreground="Gray" />
<NumericUpDown Value="{Binding SessionTimeoutSeconds}"
Minimum="1" Maximum="3600"
Width="120"
FormatString="0" />
</StackPanel>
</StackPanel>
<!-- Row 3: Certificates -->
<StackPanel Orientation="Horizontal" Spacing="12">
<StackPanel Spacing="2">
<TextBlock Text="Certificate Store Path" FontSize="11" Foreground="Gray" />
<StackPanel Orientation="Horizontal" Spacing="4">
<TextBox Text="{Binding CertificateStorePath}"
Width="370"
IsReadOnly="True"
Watermark="(default: AppData/LmxOpcUaClient/pki)" />
<Button Name="BrowseCertPathButton"
Content="..."
Width="30"
ToolTip.Tip="Browse for folder" />
</StackPanel>
</StackPanel>
<CheckBox IsChecked="{Binding AutoAcceptCertificates}"
Content="Auto-accept untrusted server certificates"
VerticalAlignment="Bottom"
Margin="0,0,0,4" />
</StackPanel>
</StackPanel>
</Expander>
<!-- Redundancy Info -->
<StackPanel Orientation="Horizontal"
Spacing="16"
IsVisible="{Binding RedundancyInfo, Converter={x:Static ObjectConverters.IsNotNull}}"
Margin="0,2,0,0">
<TextBlock Text="{Binding RedundancyInfo.Mode, StringFormat='Redundancy: {0}'}"
FontSize="11" Foreground="Gray" />
<TextBlock Text="{Binding RedundancyInfo.ServiceLevel, StringFormat='Service Level: {0}'}"
FontSize="11" Foreground="Gray" />
<TextBlock Text="{Binding RedundancyInfo.ApplicationUri, StringFormat='URI: {0}'}"
FontSize="11" Foreground="Gray" />
</StackPanel>
</StackPanel>
</Border>
<!-- Bottom Status Bar -->
<Border DockPanel.Dock="Bottom" Background="#F0F0F0" Padding="8,4">
<StackPanel Orientation="Horizontal" Spacing="20">
<TextBlock Text="{Binding StatusMessage}" />
<TextBlock Text="{Binding SessionLabel}" Foreground="Gray" />
<TextBlock Text="{Binding SubscriptionCount, StringFormat='Subscriptions: {0}'}" />
</StackPanel>
</Border>
<!-- Main Content -->
<Grid ColumnDefinitions="300,8,*">
<!-- Left: Browse Tree -->
<Border Grid.Column="0" BorderBrush="#CCCCCC" BorderThickness="0,0,1,0">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Text="Browse Tree" FontWeight="Bold" Margin="8,8,8,4" />
<views:BrowseTreeView DataContext="{Binding BrowseTree}"
Name="BrowseTreePanel">
<views:BrowseTreeView.ContextMenu>
<ContextMenu Name="TreeContextMenu">
<MenuItem Header="Subscribe" Name="SubscribeMenuItem" />
<MenuItem Header="View History" Name="ViewHistoryMenuItem" />
<Separator />
<MenuItem Header="Monitor Alarms" Name="MonitorAlarmsMenuItem" />
</ContextMenu>
</views:BrowseTreeView.ContextMenu>
</views:BrowseTreeView>
</DockPanel>
</Border>
<!-- Splitter -->
<GridSplitter Grid.Column="1" Width="8" Background="Transparent" />
<!-- Right: Tab panels -->
<TabControl Grid.Column="2"
IsEnabled="{Binding IsConnected}"
SelectedIndex="{Binding SelectedTabIndex}">
<TabItem Header="Read/Write">
<views:ReadWriteView DataContext="{Binding ReadWrite}" />
</TabItem>
<TabItem Header="{Binding SubscriptionsTabHeader}">
<views:SubscriptionsView DataContext="{Binding Subscriptions}" />
</TabItem>
<TabItem Header="{Binding AlarmsTabHeader}">
<views:AlarmsView DataContext="{Binding Alarms}" />
</TabItem>
<TabItem Header="History">
<views:HistoryView DataContext="{Binding History}" />
</TabItem>
</TabControl>
</Grid>
</DockPanel>
</Window>