Neue Projekte & Optimierungen in TaskFlow
Es wurden zwei neue Projekte (`EDMI.API` und `EDMIService`) zur Solution hinzugefügt und in die Build-Konfiguration integriert. Die Benutzeroberfläche (`frmAdmin_AppserviceTest.Designer.vb`) wurde angepasst, einschließlich Änderungen an Button-Größen, Positionen und `NumericUpDown1`-Werten. Die Logik für Benutzer- und Sitzungsinformationen wurde verbessert, um Null-Checks und die Anzeige kürzlich aktiver Benutzer zu unterstützen. Der Lasttest wurde optimiert: Verwendung von `ConcurrentBag`, Einführung von `IProgress` für UI-Feedback und Begrenzung der Parallelität. Beim Schließen des Validators (`frmMain.vb`) wurde ein DevExpress-Overlay hinzugefügt, um den Ladezustand anzuzeigen, und die Performance beim Neuaufbau des Grids verbessert. Allgemeine Verbesserungen umfassen explizite Typkonvertierungen, bessere Fehlerbehandlung und Logging.
This commit is contained in:
@@ -9,6 +9,10 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "taskFLOW", "TaskFlow\taskFL
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SnapPanel", "..\..\..\2_DLL Projekte\DDMonorepo\Controls.SnapPanel\SnapPanel.vbproj", "{3E19C413-3197-4635-944E-558FC04C3475}"
|
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SnapPanel", "..\..\..\2_DLL Projekte\DDMonorepo\Controls.SnapPanel\SnapPanel.vbproj", "{3E19C413-3197-4635-944E-558FC04C3475}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EDMI.API", "..\..\..\2_DLL Projekte\DDModules\EDMIAPI\EDMI.API.vbproj", "{25017513-0D97-49D3-98D7-BA76D9B251B0}"
|
||||||
|
EndProject
|
||||||
|
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EDMIService", "..\..\..\2_DLL Projekte\DDMonorepo\Service.EDMIService\EDMIService.vbproj", "{A8C3F298-76AB-4359-AB3C-986E313B4336}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
CD_ROM|Any CPU = CD_ROM|Any CPU
|
CD_ROM|Any CPU = CD_ROM|Any CPU
|
||||||
@@ -83,6 +87,46 @@ Global
|
|||||||
{3E19C413-3197-4635-944E-558FC04C3475}.SingleImage|Any CPU.Build.0 = Release|Any CPU
|
{3E19C413-3197-4635-944E-558FC04C3475}.SingleImage|Any CPU.Build.0 = Release|Any CPU
|
||||||
{3E19C413-3197-4635-944E-558FC04C3475}.SingleImage|x86.ActiveCfg = Release|Any CPU
|
{3E19C413-3197-4635-944E-558FC04C3475}.SingleImage|x86.ActiveCfg = Release|Any CPU
|
||||||
{3E19C413-3197-4635-944E-558FC04C3475}.SingleImage|x86.Build.0 = Release|Any CPU
|
{3E19C413-3197-4635-944E-558FC04C3475}.SingleImage|x86.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.CD_ROM|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.CD_ROM|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.CD_ROM|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.CD_ROM|x86.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.DVD-5|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.DVD-5|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.DVD-5|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.DVD-5|x86.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.SingleImage|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.SingleImage|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.SingleImage|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{25017513-0D97-49D3-98D7-BA76D9B251B0}.SingleImage|x86.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.CD_ROM|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.CD_ROM|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.CD_ROM|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.CD_ROM|x86.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.DVD-5|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.DVD-5|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.DVD-5|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.DVD-5|x86.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
16
app/TaskFlow/frmAdmin_AppserviceTest.Designer.vb
generated
16
app/TaskFlow/frmAdmin_AppserviceTest.Designer.vb
generated
@@ -48,7 +48,7 @@ Partial Class frmAdmin_AppserviceTest
|
|||||||
'
|
'
|
||||||
Me.SimpleButton1.Location = New System.Drawing.Point(12, 1)
|
Me.SimpleButton1.Location = New System.Drawing.Point(12, 1)
|
||||||
Me.SimpleButton1.Name = "SimpleButton1"
|
Me.SimpleButton1.Name = "SimpleButton1"
|
||||||
Me.SimpleButton1.Size = New System.Drawing.Size(196, 32)
|
Me.SimpleButton1.Size = New System.Drawing.Size(240, 32)
|
||||||
Me.SimpleButton1.TabIndex = 1
|
Me.SimpleButton1.TabIndex = 1
|
||||||
Me.SimpleButton1.Text = "Prüfe Status EDMI Service"
|
Me.SimpleButton1.Text = "Prüfe Status EDMI Service"
|
||||||
'
|
'
|
||||||
@@ -78,7 +78,7 @@ Partial Class frmAdmin_AppserviceTest
|
|||||||
'
|
'
|
||||||
'btnLoadStart
|
'btnLoadStart
|
||||||
'
|
'
|
||||||
Me.btnLoadStart.Location = New System.Drawing.Point(12, 150)
|
Me.btnLoadStart.Location = New System.Drawing.Point(12, 261)
|
||||||
Me.btnLoadStart.Name = "btnLoadStart"
|
Me.btnLoadStart.Name = "btnLoadStart"
|
||||||
Me.btnLoadStart.Size = New System.Drawing.Size(250, 38)
|
Me.btnLoadStart.Size = New System.Drawing.Size(250, 38)
|
||||||
Me.btnLoadStart.TabIndex = 5
|
Me.btnLoadStart.TabIndex = 5
|
||||||
@@ -86,7 +86,7 @@ Partial Class frmAdmin_AppserviceTest
|
|||||||
'
|
'
|
||||||
'btnLoadStop
|
'btnLoadStop
|
||||||
'
|
'
|
||||||
Me.btnLoadStop.Location = New System.Drawing.Point(12, 223)
|
Me.btnLoadStop.Location = New System.Drawing.Point(12, 334)
|
||||||
Me.btnLoadStop.Name = "btnLoadStop"
|
Me.btnLoadStop.Name = "btnLoadStop"
|
||||||
Me.btnLoadStop.Size = New System.Drawing.Size(250, 32)
|
Me.btnLoadStop.Size = New System.Drawing.Size(250, 32)
|
||||||
Me.btnLoadStop.TabIndex = 6
|
Me.btnLoadStop.TabIndex = 6
|
||||||
@@ -95,7 +95,7 @@ Partial Class frmAdmin_AppserviceTest
|
|||||||
'
|
'
|
||||||
'LabelControl1
|
'LabelControl1
|
||||||
'
|
'
|
||||||
Me.LabelControl1.Location = New System.Drawing.Point(12, 197)
|
Me.LabelControl1.Location = New System.Drawing.Point(12, 308)
|
||||||
Me.LabelControl1.Name = "LabelControl1"
|
Me.LabelControl1.Name = "LabelControl1"
|
||||||
Me.LabelControl1.Size = New System.Drawing.Size(143, 13)
|
Me.LabelControl1.Size = New System.Drawing.Size(143, 13)
|
||||||
Me.LabelControl1.TabIndex = 10
|
Me.LabelControl1.TabIndex = 10
|
||||||
@@ -104,14 +104,14 @@ Partial Class frmAdmin_AppserviceTest
|
|||||||
'NumericUpDown1
|
'NumericUpDown1
|
||||||
'
|
'
|
||||||
Me.NumericUpDown1.Font = New System.Drawing.Font("Segoe UI", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
Me.NumericUpDown1.Font = New System.Drawing.Font("Segoe UI", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
Me.NumericUpDown1.Increment = New Decimal(New Integer() {50, 0, 0, 0})
|
Me.NumericUpDown1.Increment = New Decimal(New Integer() {10, 0, 0, 0})
|
||||||
Me.NumericUpDown1.Location = New System.Drawing.Point(208, 192)
|
Me.NumericUpDown1.Location = New System.Drawing.Point(208, 303)
|
||||||
Me.NumericUpDown1.Maximum = New Decimal(New Integer() {1000, 0, 0, 0})
|
Me.NumericUpDown1.Maximum = New Decimal(New Integer() {1000, 0, 0, 0})
|
||||||
Me.NumericUpDown1.Minimum = New Decimal(New Integer() {100, 0, 0, 0})
|
Me.NumericUpDown1.Minimum = New Decimal(New Integer() {10, 0, 0, 0})
|
||||||
Me.NumericUpDown1.Name = "NumericUpDown1"
|
Me.NumericUpDown1.Name = "NumericUpDown1"
|
||||||
Me.NumericUpDown1.Size = New System.Drawing.Size(54, 22)
|
Me.NumericUpDown1.Size = New System.Drawing.Size(54, 22)
|
||||||
Me.NumericUpDown1.TabIndex = 11
|
Me.NumericUpDown1.TabIndex = 11
|
||||||
Me.NumericUpDown1.Value = New Decimal(New Integer() {100, 0, 0, 0})
|
Me.NumericUpDown1.Value = New Decimal(New Integer() {10, 0, 0, 0})
|
||||||
'
|
'
|
||||||
'SplitContainerControl1
|
'SplitContainerControl1
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ Public Class frmAdmin_AppserviceTest
|
|||||||
Private _loadLog As New ConcurrentQueue(Of String)
|
Private _loadLog As New ConcurrentQueue(Of String)
|
||||||
Private _loadCts As CancellationTokenSource
|
Private _loadCts As CancellationTokenSource
|
||||||
Private _timerTick As Integer = 0
|
Private _timerTick As Integer = 0
|
||||||
Private LOAD_CLIENT_COUNT As Integer = 100
|
Private LOAD_CLIENT_COUNT As Integer = 10
|
||||||
Private Shared ReadOnly _rnd As New Random() ' ◄ ergänzen
|
Private Shared ReadOnly _rnd As New Random() ' ◄ ergänzen
|
||||||
|
Private Const RECENT_USER_MAX_AGE_MINUTES As Integer = 15 ' ◄ NEU – muss mit Service-Wert übereinstimmen
|
||||||
Private Sub SimpleButton1_Click(sender As Object, e As EventArgs) Handles SimpleButton1.Click
|
Private Sub SimpleButton1_Click(sender As Object, e As EventArgs) Handles SimpleButton1.Click
|
||||||
' Status abrufen
|
' Status abrufen
|
||||||
Dim oStatus = _EDMIClient.GetStatus()
|
Dim oStatus = _EDMIClient.GetStatus()
|
||||||
@@ -59,16 +60,34 @@ Public Class frmAdmin_AppserviceTest
|
|||||||
MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine
|
MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine
|
||||||
MemoEdit1.Text &= "Aktive Benutzer:" & Environment.NewLine
|
MemoEdit1.Text &= "Aktive Benutzer:" & Environment.NewLine
|
||||||
|
|
||||||
|
If oStatus2.ActiveUsers IsNot Nothing AndAlso oStatus2.ActiveUsers.Count > 0 Then
|
||||||
For Each oUser In oStatus2.ActiveUsers
|
For Each oUser In oStatus2.ActiveUsers
|
||||||
MemoEdit1.Text &= $" • {oUser}" & Environment.NewLine
|
MemoEdit1.Text &= $" • {oUser}" & Environment.NewLine
|
||||||
Next
|
Next
|
||||||
|
Else
|
||||||
|
MemoEdit1.Text &= " (keine)" & Environment.NewLine
|
||||||
|
End If
|
||||||
|
|
||||||
MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine
|
MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine
|
||||||
MemoEdit1.Text &= "Sessions (Id → Benutzer):" & Environment.NewLine
|
MemoEdit1.Text &= "Sessions (Id → Benutzer):" & Environment.NewLine
|
||||||
|
|
||||||
|
If oStatus2.ActiveSessions IsNot Nothing AndAlso oStatus2.ActiveSessions.Count > 0 Then
|
||||||
For Each oPair In oStatus2.ActiveSessions
|
For Each oPair In oStatus2.ActiveSessions
|
||||||
MemoEdit1.Text &= $" {oPair.Key.Substring(0, Math.Min(8, oPair.Key.Length))}… → {oPair.Value}" & Environment.NewLine
|
MemoEdit1.Text &= $" {oPair.Key.Substring(0, Math.Min(8, oPair.Key.Length))}… → {oPair.Value}" & Environment.NewLine
|
||||||
Next
|
Next
|
||||||
|
Else
|
||||||
|
MemoEdit1.Text &= " (keine)" & Environment.NewLine
|
||||||
|
End If
|
||||||
|
|
||||||
|
MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine
|
||||||
|
MemoEdit1.Text &= $"Kürzlich aktive Benutzer (letzte {RECENT_USER_MAX_AGE_MINUTES} Min.):" & Environment.NewLine
|
||||||
|
If oStatus2.RecentUsers IsNot Nothing AndAlso oStatus2.RecentUsers.Count > 0 Then
|
||||||
|
For Each oRecentUser In oStatus2.RecentUsers
|
||||||
|
MemoEdit1.Text &= $" • {oRecentUser}" & Environment.NewLine
|
||||||
|
Next
|
||||||
|
Else
|
||||||
|
MemoEdit1.Text &= " (keine)" & Environment.NewLine
|
||||||
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Private Sub SimpleButton2_Click(sender As Object, e As EventArgs) Handles btnLoadStart.Click
|
Private Sub SimpleButton2_Click(sender As Object, e As EventArgs) Handles btnLoadStart.Click
|
||||||
@@ -76,52 +95,85 @@ Public Class frmAdmin_AppserviceTest
|
|||||||
MessageBox.Show("Lasttest läuft bereits.", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information)
|
MessageBox.Show("Lasttest läuft bereits.", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information)
|
||||||
Return
|
Return
|
||||||
End If
|
End If
|
||||||
LOAD_CLIENT_COUNT = NumericUpDown1.Value
|
|
||||||
|
LOAD_CLIENT_COUNT = CInt(NumericUpDown1.Value)
|
||||||
_loadLog = New ConcurrentQueue(Of String)
|
_loadLog = New ConcurrentQueue(Of String)
|
||||||
_loadCts = New CancellationTokenSource()
|
_loadCts = New CancellationTokenSource()
|
||||||
_timerTick = 0
|
_timerTick = 0
|
||||||
|
|
||||||
' 150 Client-Verbindungen parallel aufbauen
|
|
||||||
btnLoadStart.Enabled = False
|
btnLoadStart.Enabled = False
|
||||||
btnLoadStop.Enabled = True
|
btnLoadStop.Enabled = True
|
||||||
btnLoadStop.Visible = True
|
btnLoadStop.Visible = True
|
||||||
MemoEdit2.Text = ""
|
MemoEdit2.Text = ""
|
||||||
MemoEdit2.Text &= $"[{Now:HH:mm:ss}] Baue {LOAD_CLIENT_COUNT} Verbindungen auf…" & Environment.NewLine
|
MemoEdit2.Text &= $"[{Now:HH:mm:ss}] Baue {LOAD_CLIENT_COUNT} Verbindungen auf…" & Environment.NewLine
|
||||||
|
|
||||||
Task.Run(Sub()
|
' ► IProgress für thread-sicheres UI-Feedback während des Aufbaus
|
||||||
_loadClients = New List(Of Client)
|
Dim progress As New Progress(Of String)(Sub(msg)
|
||||||
Dim errors As Integer = 0
|
MemoEdit2.Text &= msg & Environment.NewLine
|
||||||
Parallel.For(0, LOAD_CLIENT_COUNT,
|
MemoEdit2.SelectionStart = MemoEdit2.Text.Length
|
||||||
New ParallelOptions With {.CancellationToken = _loadCts.Token},
|
MemoEdit2.ScrollToCaret()
|
||||||
Sub(i)
|
End Sub)
|
||||||
Try
|
|
||||||
' ► Hier ggf. eigene Client-Factory verwenden
|
|
||||||
Dim oClientName = "PerformanceClient_" & (i + 1)
|
|
||||||
Dim oClient = New Client(LOGCONFIG, CONFIG.Config.EDMIAppServer, oClientName)
|
|
||||||
Dim oConnected = oClient.Connect() ' ◄ Connect() muss aufgerufen werden
|
|
||||||
|
|
||||||
SyncLock _loadClients
|
Dim token = _loadCts.Token
|
||||||
If oConnected Then ' ◄ nur verbundene hinzufügen
|
|
||||||
_loadClients.Add(oClient)
|
Task.Run(
|
||||||
|
Async Function()
|
||||||
|
' ► ConcurrentBag ersetzt List(Of Client) + SyncLock beim Hinzufügen
|
||||||
|
Dim bag As New System.Collections.Concurrent.ConcurrentBag(Of Client)
|
||||||
|
Dim errors As Integer = 0
|
||||||
|
|
||||||
|
Dim options As New ParallelOptions With {
|
||||||
|
.CancellationToken = token,
|
||||||
|
.MaxDegreeOfParallelism = Math.Min(LOAD_CLIENT_COUNT, 32) ' ◄ Threadpool schonen
|
||||||
|
}
|
||||||
|
|
||||||
|
Try
|
||||||
|
Parallel.For(0, LOAD_CLIENT_COUNT, options,
|
||||||
|
Sub(i)
|
||||||
|
token.ThrowIfCancellationRequested()
|
||||||
|
Try
|
||||||
|
Dim oClientName = $"PerformanceClient_{i + 1}"
|
||||||
|
Dim oClient = New Client(LOGCONFIG, CONFIG.Config.EDMIAppServer, oClientName)
|
||||||
|
Dim oConnected = oClient.Connect()
|
||||||
|
|
||||||
|
If oConnected Then
|
||||||
|
bag.Add(oClient)
|
||||||
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Client #{i + 1} verbunden")
|
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Client #{i + 1} verbunden")
|
||||||
Else
|
Else
|
||||||
oClient.Dispose()
|
oClient.Dispose()
|
||||||
Interlocked.Increment(errors)
|
Interlocked.Increment(errors)
|
||||||
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Client #{i + 1} Connect() fehlgeschlagen")
|
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Client #{i + 1} Connect() fehlgeschlagen")
|
||||||
End If
|
End If
|
||||||
End SyncLock
|
Catch ex As OperationCanceledException
|
||||||
|
' Abbruch gewünscht – still beenden
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Interlocked.Increment(errors)
|
Interlocked.Increment(errors)
|
||||||
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Client #{i + 1} Fehler: {ex.Message}")
|
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Client #{i + 1} Fehler: {ex.Message}")
|
||||||
End Try
|
End Try
|
||||||
End Sub)
|
End Sub)
|
||||||
|
Catch ex As OperationCanceledException
|
||||||
|
' ◄ Parallel.For selbst kann werfen – abfangen
|
||||||
|
_loadLog.Enqueue($"[{Now:HH:mm:ss}] Verbindungsaufbau abgebrochen.")
|
||||||
|
End Try
|
||||||
|
|
||||||
|
' ► Erst nach Abschluss: thread-sicher in _loadClients übertragen
|
||||||
|
Dim connectedClients = bag.ToList()
|
||||||
|
|
||||||
Me.Invoke(Sub()
|
Me.Invoke(Sub()
|
||||||
|
' ◄ Zuweisung auf UI-Thread – keine Race Condition
|
||||||
|
_loadClients = connectedClients
|
||||||
FlushLoadLog()
|
FlushLoadLog()
|
||||||
MemoEdit2.Text &= $"[{Now:HH:mm:ss}] {_loadClients.Count} Clients aktiv, {errors} Fehler" & Environment.NewLine
|
MemoEdit2.Text &= $"[{Now:HH:mm:ss}] {_loadClients.Count} Clients aktiv, {errors} Fehler" & Environment.NewLine
|
||||||
|
If _loadClients.Count > 0 Then
|
||||||
StartLoadTimer()
|
StartLoadTimer()
|
||||||
|
Else
|
||||||
|
' ◄ Kein einziger Client verbunden → Test nicht starten
|
||||||
|
MemoEdit2.Text &= $"[{Now:HH:mm:ss}] Kein Client verbunden – Lasttest wird nicht gestartet." & Environment.NewLine
|
||||||
|
btnLoadStart.Enabled = True
|
||||||
|
btnLoadStop.Enabled = False
|
||||||
|
End If
|
||||||
End Sub)
|
End Sub)
|
||||||
End Sub)
|
End Function)
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub StopLoadTest()
|
Private Sub StopLoadTest()
|
||||||
_loadCts?.Cancel()
|
_loadCts?.Cancel()
|
||||||
@@ -166,8 +218,12 @@ Public Class frmAdmin_AppserviceTest
|
|||||||
oSnapshot = _loadClients.ToList()
|
oSnapshot = _loadClients.ToList()
|
||||||
End SyncLock
|
End SyncLock
|
||||||
|
|
||||||
|
' ← MaxDegreeOfParallelism begrenzt gleichzeitige Service-Calls
|
||||||
Parallel.ForEach(oSnapshot,
|
Parallel.ForEach(oSnapshot,
|
||||||
New ParallelOptions With {.CancellationToken = _loadCts.Token},
|
New ParallelOptions With {
|
||||||
|
.CancellationToken = _loadCts.Token,
|
||||||
|
.MaxDegreeOfParallelism = 10 ' ← NEU: max. 10 gleichzeitige Calls
|
||||||
|
},
|
||||||
Sub(oClient As Client)
|
Sub(oClient As Client)
|
||||||
Try
|
Try
|
||||||
Select Case _timerTick Mod 4
|
Select Case _timerTick Mod 4
|
||||||
|
|||||||
@@ -2810,15 +2810,38 @@ Public Class frmMain
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
Private Async Sub ValidatorClosed(sender As Object, e As FormClosedEventArgs)
|
Private Async Sub ValidatorClosed(sender As Object, e As FormClosedEventArgs)
|
||||||
|
Dim oGridOverlayHandle As Object = Nothing
|
||||||
Try
|
Try
|
||||||
' LoadingPanel-Verwaltung erfolgt in Decide_Load
|
' Aktuellen Grid-Zustand sichern BEVOR DataSource zurückgesetzt wird
|
||||||
Await Decide_Load(False, True)
|
If GridControlWorkflows.Visible = True AndAlso FormOpenClose = False Then
|
||||||
|
RefreshHelper.SaveViewInfo()
|
||||||
|
End If
|
||||||
|
|
||||||
If GridControlWorkflows.Visible = True And FormOpenClose = False Then
|
' DevExpress-Overlay NUR über dem Grid anzeigen – verhindert leere Bereiche
|
||||||
|
' und signalisiert dem Nutzer den laufenden Ladevorgang direkt im Grid-Bereich.
|
||||||
|
' ShowOverlayForm(Control) ist die empfohlene DevExpress-Methode hierfür.
|
||||||
|
oGridOverlayHandle = SplashScreenManager.ShowOverlayForm(GridControlWorkflows)
|
||||||
|
|
||||||
|
' BeginUpdate verhindert zusätzliche Paint-Events während des Neuaufbaus.
|
||||||
|
GridControlWorkflows.BeginUpdate()
|
||||||
|
GridViewWorkflows.BeginUpdate()
|
||||||
|
Try
|
||||||
|
Await Decide_Load(False, True)
|
||||||
|
Finally
|
||||||
|
GridViewWorkflows.EndUpdate()
|
||||||
|
GridControlWorkflows.EndUpdate()
|
||||||
|
End Try
|
||||||
|
|
||||||
|
If GridControlWorkflows.Visible = True AndAlso FormOpenClose = False Then
|
||||||
RefreshHelper.LoadViewInfo()
|
RefreshHelper.LoadViewInfo()
|
||||||
End If
|
End If
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
LOGGER.Error(ex)
|
LOGGER.Error(ex)
|
||||||
|
Finally
|
||||||
|
' Overlay immer schließen – auch bei Fehlern
|
||||||
|
If oGridOverlayHandle IsNot Nothing Then
|
||||||
|
SplashScreenManager.CloseOverlayForm(oGridOverlayHandle)
|
||||||
|
End If
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Private Function Init_IDB()
|
Private Function Init_IDB()
|
||||||
|
|||||||
Reference in New Issue
Block a user