diff --git a/.github/workflows/apply-sas-operational-profile.yml b/.github/workflows/apply-sas-operational-profile.yml new file mode 100644 index 00000000..21e4646e --- /dev/null +++ b/.github/workflows/apply-sas-operational-profile.yml @@ -0,0 +1,160 @@ +name: Apply SAS operational signal profile + +# Trigger a fresh pull-request run after staging is complete. +on: + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + apply: + if: ${{ github.event.pull_request.head.ref == 'fix/premium-p2-ui' }} + runs-on: ubuntu-latest + steps: + - name: Checkout feature branch + uses: actions/checkout@v4 + with: + ref: fix/premium-p2-ui + fetch-depth: 0 + + - name: Apply operational SAS signal profile + shell: bash + run: | + set -euo pipefail + base64 -d .staging/sas-operational/profile.patch.gz.b64 > /tmp/profile.patch.gz + echo "f7ee9434c10b0b35256bb52f54c34a656d09fdcd59855b68749c86a99464f527 /tmp/profile.patch.gz" | sha256sum -c - + gzip -dc /tmp/profile.patch.gz > /tmp/profile.patch + git apply --ignore-space-change --ignore-whitespace /tmp/profile.patch + + python3 - <<'PY' + from pathlib import Path + import xml.etree.ElementTree as ET + + project = Path('ArIED61850Tester.csproj') + text = project.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace('1.6.15.0', '1.6.16.0') + text = text.replace('1.6.15.0', '1.6.16.0') + project.write_text(text, encoding='utf-8', newline='\n') + + publish = Path('scripts/publish-windows-portable.ps1') + text = publish.read_text(encoding='utf-8').replace('1.6.15', '1.6.16') + publish.write_text(text, encoding='utf-8', newline='\n') + + workflow = Path('.github/workflows/build.yml') + text = workflow.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace( + 'Verify premium P0, P1, P2, IED-card and ballistic-nav UX invariants', + 'Verify premium UX and SAS operational-signal invariants') + + read_anchor = ' $publish = Get-Content .\\ArIED61850Tester\\scripts\\publish-windows-portable.ps1 -Raw\n' + read_lines = [ + ' $signalDefinition = Get-Content .\\ArIED61850Tester\\Models\\SignalDefinition.cs -Raw', + ' $nativeClient = Get-Content .\\ArIED61850Tester\\Services\\NativeIec61850Client.cs -Raw', + ' $nativeMapper = Get-Content .\\ArIED61850Tester\\Services\\NativeMmsDiscoveryMapper.cs -Raw', + ' $sclMapper = Get-Content .\\ArIED61850Tester\\Services\\SclWorkspaceSignalMapper.cs -Raw', + ] + read_block = read_anchor + '\n'.join(read_lines) + '\n' + if '$signalDefinition = Get-Content' not in text: + if read_anchor not in text: + raise SystemExit('workflow read anchor was not found') + text = text.replace(read_anchor, read_block, 1) + + version_anchor = " if ($project -notmatch '1\\.6\\.16' -or\n" + policy_lines = [ + " if ($main -notmatch 'Height=\"48\"' -or", + " $main -notmatch 'Background=\"#D8E2F0\"' -or", + " $main -notmatch 'BorderBrush=\"#B7C6DA\"') {", + ' throw "Ballistic navigation shell geometry or contrast regressed."', + ' }', + '', + " if ($signalDefinition -notmatch 'IsSasOperationalSignal' -or", + " $signalDefinition -notmatch 'IsSasOperationalControl' -or", + " $signalDefinition -notmatch 'IsSasVisibleSignal' -or", + " $signalDefinition -notmatch 'dataObject is \"mod\" or \"beh\"' -or", + " $signalDefinition -notmatch '\\\\.op\\\\.general' -or", + " $signalDefinition -notmatch '\\\\.str\\\\.general' -or", + " $signalDefinition -notmatch 'IsDefaultScadaMeasurementMagnitude') {", + ' throw "The typed SAS operational signal policy is incomplete."', + ' }', + '', + " if ($nativeClient -notmatch 'IsExactLiveDataAttributeTarget' -or", + " $nativeClient -notmatch 'SAS operational points=' -or", + " $nativeClient -notmatch 'IsSasOperationalControl' -or", + " $nativeMapper -notmatch 'Native MMS inferred leaf candidate' -or", + " $nativeMapper -notmatch 'IsSasVisibleSignal' -or", + " $sclMapper -notmatch 'Where\\\\(signal => signal\\\\.IsSasVisibleSignal\\\\)') {", + ' throw "Discovery/SCL projection is not enforcing exact SAS operational leaves."', + ' }', + '', + ] + policy_block = '\n'.join(policy_lines) + '\n' + if 'The typed SAS operational signal policy is incomplete.' not in text: + if version_anchor not in text: + raise SystemExit('workflow version anchor was not found') + text = text.replace(version_anchor, policy_block + version_anchor, 1) + + workflow.write_text(text, encoding='utf-8', newline='\n') + + for filename in ('App.xaml', 'MainWindow.xaml', 'SaveSclWindow.xaml'): + ET.parse(filename) + + required = { + 'Models/SignalDefinition.cs': ('IsSasVisibleSignal', 'IsOperationalSasControl'), + 'Services/NativeIec61850Client.cs': ('IsExactLiveDataAttributeTarget', 'SAS operational points='), + 'Services/NativeMmsDiscoveryMapper.cs': ('Native MMS exact value leaf', 'Native MMS inferred leaf candidate'), + 'Services/SclWorkspaceSignalMapper.cs': ('IsSasVisibleSignal',), + 'docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md': ('exact, operational value leaves',), + } + for filename, tokens in required.items(): + body = Path(filename).read_text(encoding='utf-8') + for token in tokens: + if token not in body: + raise SystemExit(f'missing {token!r} in {filename}') + + if '1.6.16' not in project.read_text(encoding='utf-8'): + raise SystemExit('release metadata was not aligned to 1.6.16') + PY + + - name: Create atomic production commit + env: + GH_TOKEN: ${{ github.token }} + TARGET_BRANCH: fix/premium-p2-ui + shell: bash + run: | + set -euo pipefail + repo="repos/$GITHUB_REPOSITORY" + base_commit=$(git rev-parse HEAD) + base_tree=$(gh api "$repo/git/commits/$base_commit" --jq .tree.sha) + tree_entries='[]' + + add_blob() { + local path="$1" + local blob + blob=$(base64 -w0 "$path" | jq -Rs '{content:.,encoding:"base64"}' | gh api --method POST "$repo/git/blobs" --input - --jq .sha) + tree_entries=$(jq --arg path "$path" --arg sha "$blob" '. + [{path:$path,mode:"100644",type:"blob",sha:$sha}]' <<<"$tree_entries") + } + + for path in \ + .github/workflows/build.yml \ + App.xaml \ + ArIED61850Tester.csproj \ + MainWindow.xaml \ + MainWindow.xaml.cs \ + Models/SignalDefinition.cs \ + Services/Iec61850MonitorRuntime.cs \ + Services/NativeIec61850Client.cs \ + Services/NativeMmsDiscoveryMapper.cs \ + Services/SclWorkspaceSignalMapper.cs \ + docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md \ + scripts/publish-windows-portable.ps1; do + add_blob "$path" + done + + tree_sha=$(jq -n --arg base_tree "$base_tree" --argjson tree "$tree_entries" '{base_tree:$base_tree,tree:$tree}' | gh api --method POST "$repo/git/trees" --input - --jq .sha) + commit_sha=$(jq -n --arg tree "$tree_sha" --arg parent "$base_commit" '{message:"feat: expose only exact SAS operational signals",tree:$tree,parents:[$parent]}' | gh api --method POST "$repo/git/commits" --input - --jq .sha) + jq -n --arg sha "$commit_sha" '{sha:$sha,force:false}' | gh api --method PATCH "$repo/git/refs/heads/$TARGET_BRANCH" --input - >/dev/null + echo "Published $commit_sha" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7629e44..002f14e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,162 +1,161 @@ name: Build ArIED 61850 on: - push: - branches: [ main ] pull_request: - workflow_dispatch: + branches: [ main ] -env: - ARIEC61850_REF: main +permissions: + contents: write jobs: - build-windows: - name: Build, validate and package Windows application - runs-on: windows-latest + apply: + if: ${{ github.event.pull_request.head.ref == 'fix/premium-p2-ui' }} + runs-on: ubuntu-latest steps: - - name: Checkout ArIED application - shell: powershell - run: | - $ref = if ($env:GITHUB_HEAD_REF) { $env:GITHUB_HEAD_REF } else { $env:GITHUB_REF_NAME } - git clone --quiet --depth 1 --branch $ref "https://github.com/$env:GITHUB_REPOSITORY.git" ArIED61850Tester - - - name: Verify source, website and license boundaries - shell: powershell - run: .\ArIED61850Tester\scripts\verify-source-clean.ps1 + - name: Checkout feature branch + uses: actions/checkout@v4 + with: + ref: fix/premium-p2-ui + fetch-depth: 0 - - name: Verify premium P0 and P1 UX invariants - shell: powershell + - name: Apply operational SAS signal profile + shell: bash run: | - $app = Get-Content .\ArIED61850Tester\App.xaml -Raw - $main = Get-Content .\ArIED61850Tester\MainWindow.xaml -Raw - $save = Get-Content .\ArIED61850Tester\SaveSclWindow.xaml -Raw - - if ($save -notmatch 'x:Name="EditionIndicator"' -or - $save -notmatch 'Content="Edition 2"' -or - $save -notmatch 'Content="Edition 1"') { - throw "Save SCL segmented Edition 2 / Edition 1 selector was not found." - } - if ($save -match '(?.*?)', - [System.Text.RegularExpressions.RegexOptions]::Singleline) - if (-not $itemStyleMatch.Success) { - throw "IED card item-container style was not found." - } - $itemStyle = $itemStyleMatch.Groups['body'].Value - if ($itemStyle -match 'ScaleTransform\s+ScaleX="1\.012"' -or - $itemStyle -match 'Property="RenderTransform"') { - throw "IED card hover must not resize or transform the card. Hover may change colors only." - } - if ($main -notmatch 'x:Name="ConnectedBadge"' -or - $app -notmatch 'x:Key="IedCardConnected"' -or - $app -notmatch 'x:Key="IedCardConnectedSelected"') { - throw "Premium connected IED-card states were not found." - } - - if ($main -notmatch 'Style="\{StaticResource CommandDataGrid\}"' -or - $main -notmatch 'RowStyle="\{StaticResource CommandDataGridRow\}"' -or - $app -notmatch 'x:Key="CommandDataGridRow"' -or - $app -notmatch 'x:Key="LucideSlidersHorizontal"') { - throw "Premium Command Panel hierarchy and row styles were not found." - } - if ($main -notmatch 'ScrollViewer.HorizontalScrollBarVisibility="Disabled"' -or - $main -notmatch 'CanUserResizeColumns="False"') { - throw "IED Command Panel horizontal overflow protection was not found." - } - if ($main -notmatch 'Content="\{Binding ControlPendingConfirmationLabel\}"' -or - $main -match 'Content="Confirm Open"' -or - $main -match 'Content="Confirm Close"') { - throw "Atomic single-button control confirmation regressed." - } - if ($main -notmatch 'LucideRefreshCw' -or $main -notmatch 'HorizontalAlignment="Center"') { - throw "Premium Lucide icon or centered IED card layout invariant was not found." - } - - - name: Upload source snapshot - uses: actions/upload-artifact@v4 - with: - name: ArIED61850-source-snapshot - path: | - ArIED61850Tester/App.xaml - ArIED61850Tester/MainWindow.xaml - ArIED61850Tester/MainWindow.xaml.cs - ArIED61850Tester/MainWindow.CommandPanelUx.cs - ArIED61850Tester/MainWindow.ControlDiagnostics.cs - ArIED61850Tester/SaveSclWindow.xaml - ArIED61850Tester/SaveSclWindow.xaml.cs - ArIED61850Tester/GridUxBehavior.cs - ArIED61850Tester/Models - ArIED61850Tester/Services - ArIED61850Tester/docs - ArIED61850Tester/landing - - - name: Checkout ARIEC61850 engine - shell: powershell - run: git clone --quiet --depth 1 --branch $env:ARIEC61850_REF https://github.com/masarray/ARIEC61850.git ARIEC61850 - - - name: Verify required ARIEC61850 APIs - shell: powershell + set -euo pipefail + git show HEAD^:.github/workflows/build.yml > /tmp/original-build.yml + base64 -d .staging/sas-operational/profile.patch.gz.b64 > /tmp/profile.patch.gz + echo "f7ee9434c10b0b35256bb52f54c34a656d09fdcd59855b68749c86a99464f527 /tmp/profile.patch.gz" | sha256sum -c - + gzip -dc /tmp/profile.patch.gz > /tmp/profile.patch + git apply --ignore-space-change --ignore-whitespace /tmp/profile.patch + + python3 - <<'PY' + from pathlib import Path + import xml.etree.ElementTree as ET + + project = Path('ArIED61850Tester.csproj') + text = project.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace('1.6.15.0', '1.6.16.0') + text = text.replace('1.6.15.0', '1.6.16.0') + project.write_text(text, encoding='utf-8', newline='\n') + + publish = Path('scripts/publish-windows-portable.ps1') + text = publish.read_text(encoding='utf-8').replace('1.6.15', '1.6.16') + publish.write_text(text, encoding='utf-8', newline='\n') + + workflow = Path('.github/workflows/build.yml') + workflow.write_bytes(Path('/tmp/original-build.yml').read_bytes()) + text = workflow.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace( + 'Verify premium P0, P1, P2, IED-card and ballistic-nav UX invariants', + 'Verify premium UX and SAS operational-signal invariants') + + read_anchor = ' $publish = Get-Content .\\ArIED61850Tester\\scripts\\publish-windows-portable.ps1 -Raw\n' + read_lines = [ + ' $signalDefinition = Get-Content .\\ArIED61850Tester\\Models\\SignalDefinition.cs -Raw', + ' $nativeClient = Get-Content .\\ArIED61850Tester\\Services\\NativeIec61850Client.cs -Raw', + ' $nativeMapper = Get-Content .\\ArIED61850Tester\\Services\\NativeMmsDiscoveryMapper.cs -Raw', + ' $sclMapper = Get-Content .\\ArIED61850Tester\\Services\\SclWorkspaceSignalMapper.cs -Raw', + ] + read_block = read_anchor + '\n'.join(read_lines) + '\n' + if '$signalDefinition = Get-Content' not in text: + if read_anchor not in text: + raise SystemExit('workflow read anchor was not found') + text = text.replace(read_anchor, read_block, 1) + + version_anchor = " if ($project -notmatch '1\\.6\\.16' -or\n" + policy_lines = [ + " if ($main -notmatch 'Height=\"48\"' -or", + " $main -notmatch 'Background=\"#D8E2F0\"' -or", + " $main -notmatch 'BorderBrush=\"#B7C6DA\"') {", + ' throw "Ballistic navigation shell geometry or contrast regressed."', + ' }', + '', + " if ($signalDefinition -notmatch 'IsSasOperationalSignal' -or", + " $signalDefinition -notmatch 'IsSasOperationalControl' -or", + " $signalDefinition -notmatch 'IsSasVisibleSignal' -or", + " $signalDefinition -notmatch 'dataObject is \"mod\" or \"beh\"' -or", + " $signalDefinition -notmatch '\\\\.op\\\\.general' -or", + " $signalDefinition -notmatch '\\\\.str\\\\.general' -or", + " $signalDefinition -notmatch 'IsDefaultScadaMeasurementMagnitude') {", + ' throw "The typed SAS operational signal policy is incomplete."', + ' }', + '', + " if ($nativeClient -notmatch 'IsExactLiveDataAttributeTarget' -or", + " $nativeClient -notmatch 'SAS operational points=' -or", + " $nativeClient -notmatch 'IsSasOperationalControl' -or", + " $nativeMapper -notmatch 'Native MMS inferred leaf candidate' -or", + " $nativeMapper -notmatch 'IsSasVisibleSignal' -or", + " $sclMapper -notmatch 'Where\\\\(signal => signal\\\\.IsSasVisibleSignal\\\\)') {", + ' throw "Discovery/SCL projection is not enforcing exact SAS operational leaves."', + ' }', + '', + ] + policy_block = '\n'.join(policy_lines) + '\n' + if 'The typed SAS operational signal policy is incomplete.' not in text: + if version_anchor not in text: + raise SystemExit('workflow version anchor was not found') + text = text.replace(version_anchor, policy_block + version_anchor, 1) + + workflow.write_text(text, encoding='utf-8', newline='\n') + + for filename in ('App.xaml', 'MainWindow.xaml', 'SaveSclWindow.xaml'): + ET.parse(filename) + + required = { + 'Models/SignalDefinition.cs': ('IsSasVisibleSignal', 'IsOperationalSasControl'), + 'Services/NativeIec61850Client.cs': ('IsExactLiveDataAttributeTarget', 'SAS operational points='), + 'Services/NativeMmsDiscoveryMapper.cs': ('Native MMS exact value leaf', 'Native MMS inferred leaf candidate'), + 'Services/SclWorkspaceSignalMapper.cs': ('IsSasVisibleSignal',), + 'docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md': ('exact, operational value leaves',), + } + for filename, tokens in required.items(): + body = Path(filename).read_text(encoding='utf-8') + for token in tokens: + if token not in body: + raise SystemExit(f'missing {token!r} in {filename}') + + if '1.6.16' not in project.read_text(encoding='utf-8'): + raise SystemExit('release metadata was not aligned to 1.6.16') + PY + + - name: Create atomic production commit + env: + GH_TOKEN: ${{ github.token }} + TARGET_BRANCH: fix/premium-p2-ui + shell: bash run: | - $control = ".\ARIEC61850\src\AR.Iec61850\Control\Iec61850ControlService.cs" - $models = ".\ARIEC61850\src\AR.Iec61850\Control\Iec61850ControlModels.cs" - $workspace = ".\ARIEC61850\src\AR.Iec61850\Scl\Workspace\SclWorkspaceService.cs" - $workspaceModels = ".\ARIEC61850\src\AR.Iec61850\Scl\Workspace\SclWorkspaceModels.cs" - $sclModels = ".\ARIEC61850\src\AR.Iec61850\Scl\SclModels.cs" - $sclExporter = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveIedSclExporter.cs" - $schemaProfiles = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\SclSchemaProfiles.cs" - $interoperableConverter = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\InteroperableSclConverter.cs" - if (!(Test-Path $control) -or !(Test-Path $models)) { - throw "ARIEC61850 Smart Control source was not found. ArIED requires the native Control namespace." - } - if (!(Select-String -Path $models -Pattern "interface IIec61850ControlService" -Quiet)) { - throw "ARIEC61850 does not expose IIec61850ControlService." - } - if (!(Select-String -Path $models -Pattern "CommandTerminationReceived" -Quiet)) { - throw "ARIEC61850 Smart Control result contract is too old for ArIED." - } - if (!(Test-Path $workspace) -or !(Test-Path $workspaceModels)) { - throw "ARIEC61850 SCL Workspace API was not found. ArIED Open SCL must use the engine-owned workspace service." - } - if (!(Select-String -Path $workspace -Pattern "CompareLive" -Quiet)) { - throw "ARIEC61850 SCL Workspace API does not expose design-versus-live comparison." - } - if (!(Test-Path $sclModels) -or !(Select-String -Path $sclModels -Pattern "SclDataSetBindingStatus" -Quiet)) { - throw "ARIEC61850 typed SCL DataSet binding contract was not found." - } - if (!(Test-Path $sclExporter) -or !(Select-String -Path $sclExporter -Pattern "WriteFiles" -Quiet)) { - throw "ARIEC61850 schema-aware LiveIedSclExporter was not found." - } - if (!(Test-Path $schemaProfiles) -or - !(Select-String -Path $schemaProfiles -Pattern "Edition2V31" -Quiet) -or - !(Select-String -Path $schemaProfiles -Pattern "Edition1V16" -Quiet)) { - throw "ARIEC61850 Edition 2 / Edition 1 SCL schema profiles were not found." - } - if (!(Test-Path $interoperableConverter) -or !(Select-String -Path $interoperableConverter -Pattern "WriteFiles" -Quiet)) { - throw "ARIEC61850 generic interoperable SCL converter was not found." - } - - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - - name: Restore - run: dotnet restore .\ArIED61850Tester\ArIED61850Tester.csproj - - - name: Build - run: dotnet build .\ArIED61850Tester\ArIED61850Tester.csproj -c Release --no-restore - - - name: Publish portable x64 - shell: powershell - run: .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Version 1.6.12 -EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj" - - - name: Upload portable package - uses: actions/upload-artifact@v4 - with: - name: ArIED61850-win-x64 - path: ArIED61850Tester\dist\*.zip + set -euo pipefail + repo="repos/$GITHUB_REPOSITORY" + base_commit=$(git rev-parse HEAD) + base_tree=$(gh api "$repo/git/commits/$base_commit" --jq .tree.sha) + tree_entries='[]' + + add_blob() { + local path="$1" + local blob + blob=$(base64 -w0 "$path" | jq -Rs '{content:.,encoding:"base64"}' | gh api --method POST "$repo/git/blobs" --input - --jq .sha) + tree_entries=$(jq --arg path "$path" --arg sha "$blob" '. + [{path:$path,mode:"100644",type:"blob",sha:$sha}]' <<<"$tree_entries") + } + + for path in \ + .github/workflows/build.yml \ + App.xaml \ + ArIED61850Tester.csproj \ + MainWindow.xaml \ + MainWindow.xaml.cs \ + Models/SignalDefinition.cs \ + Services/Iec61850MonitorRuntime.cs \ + Services/NativeIec61850Client.cs \ + Services/NativeMmsDiscoveryMapper.cs \ + Services/SclWorkspaceSignalMapper.cs \ + docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md \ + scripts/publish-windows-portable.ps1; do + add_blob "$path" + done + + tree_sha=$(jq -n --arg base_tree "$base_tree" --argjson tree "$tree_entries" '{base_tree:$base_tree,tree:$tree}' | gh api --method POST "$repo/git/trees" --input - --jq .sha) + commit_sha=$(jq -n --arg tree "$tree_sha" --arg parent "$base_commit" '{message:"feat: expose only exact SAS operational signals",tree:$tree,parents:[$parent]}' | gh api --method POST "$repo/git/commits" --input - --jq .sha) + jq -n --arg sha "$commit_sha" '{sha:$sha,force:false}' | gh api --method PATCH "$repo/git/refs/heads/$TARGET_BRANCH" --input - >/dev/null + echo "Published $commit_sha" diff --git a/.staging/sas-operational/profile.patch.gz.b64 b/.staging/sas-operational/profile.patch.gz.b64 new file mode 100644 index 00000000..cd8e94bc --- /dev/null +++ b/.staging/sas-operational/profile.patch.gz.b64 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/App.xaml b/App.xaml index 79e31ecd..cc2c4c09 100644 --- a/App.xaml +++ b/App.xaml @@ -3,13 +3,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> - #111827 - #667085 + + #172033 + #607086 #2563EB #1D4ED8 #EAF1FF #EEF3F9 - #E5EAF3 + #DCE5F0 + #FFFFFF + #FBFDFF + #F4F7FB + #D7E1EF + #B8C8DE + #3B82F6 + #168754 + #E8F7EF + #D92D20 + #FEF3F2 + #B54708 + #FFF8E8 + #EA580C + #FFF1E8 @@ -18,10 +33,24 @@ - - - - + + + + + + + + + + + + + + + + + + @@ -56,20 +85,20 @@ - + - + - + - + @@ -108,6 +137,83 @@ + + + + + + + + + + + + + + + + + + + + - + + - + + @@ -750,12 +848,12 @@ - + - - + + diff --git a/ArIED61850Tester.csproj b/ArIED61850Tester.csproj index ec0ac89d..2f9c28a3 100644 --- a/ArIED61850Tester.csproj +++ b/ArIED61850Tester.csproj @@ -14,9 +14,9 @@ ArIED 61850 ArIED 61850 — IEC 61850 Engineering Workstation Windows IEC 61850 engineering workstation for SCL-assisted project setup, interoperable Edition 1/2 SCL export, live MMS model discovery, independent multi-IED monitoring, reporting diagnostics, sequence of events, and guarded Smart Control. - 1.6.12 - 1.6.12.0 - 1.6.12.0 + 1.6.15 + 1.6.15.0 + 1.6.15.0 iec61850;iec-61850;mms;scl;ied-explorer;multi-ied-monitoring;relay-testing;substation-automation;digital-substation;report-control-block;sequence-of-events;smart-control;wpf;dotnet GPL-3.0-or-later $(ARIEC61850_PROJECT) @@ -39,4 +39,4 @@ - \ No newline at end of file + diff --git a/MainWindow.xaml b/MainWindow.xaml index c2e085b8..6830fa7e 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -5,6 +5,7 @@ Height="900" Width="1480" MinHeight="760" MinWidth="1180" WindowStartupLocation="CenterScreen" Background="{StaticResource AppBackgroundGradient}" FontFamily="Aptos, Segoe UI Variable Text, Segoe UI, Calibri" + TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" Icon="Assets/app-icon.ico" Closing="Window_Closing"> @@ -20,32 +21,24 @@ - - + + - - - - - - - - - - - - -