Derive queue recovery action from coach memory
This commit is contained in:
parent
f494f0707d
commit
42a4f48198
5 changed files with 142 additions and 16 deletions
|
|
@ -274,6 +274,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
|
|||
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
|
||||
const bool bPrefersRecommended =
|
||||
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended;
|
||||
const bool bUpstreamPrefersLaunchRecovery =
|
||||
CachedCoachPanelState.PreferredQueueRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
|
||||
const bool bCanLaunchQueueRecovery =
|
||||
CachedCoachPanelState.bCanStartQueuedRun
|
||||
|| (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
|
||||
|
|
@ -281,8 +284,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
|
|||
const bool bPreferLaunchBeforePromote =
|
||||
!DeferredEntryId.IsEmpty()
|
||||
&& bCanLaunchQueueRecovery
|
||||
&& QueueRecoveryWeighting.PreferredActionKind
|
||||
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery;
|
||||
&& (QueueRecoveryWeighting.PreferredActionKind
|
||||
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery
|
||||
|| (!QueueRecoveryWeighting.IsStructurallyValid() && bUpstreamPrefersLaunchRecovery));
|
||||
|
||||
auto StartRunAndRefresh = [this](const FHyperTwistTrainingRunState& RunState)
|
||||
{
|
||||
|
|
@ -3858,16 +3862,27 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
const FString QueueRecoverySourceLabel = CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: CachedCoachPanelState.PreferredGuidanceSourceLabel;
|
||||
const FString QueueRecoveryActionSourceLabel =
|
||||
CachedCoachPanelState.PreferredQueueRecoveryActionSourceLabel.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: CachedCoachPanelState.PreferredQueueRecoveryActionSourceLabel;
|
||||
const FHyperTwistCoachDashboardQueueRecoveryWeighting QueueRecoveryWeighting = BuildQueueRecoveryWeighting();
|
||||
const bool bCanLaunchQueueRecovery =
|
||||
CachedCoachPanelState.bCanStartQueuedRun
|
||||
|| (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
|
||||
|| CachedCoachPanelState.bCanStartCoachRecommendedRun;
|
||||
const bool bUpstreamPrefersPromoteRecovery =
|
||||
CachedCoachPanelState.PreferredQueueRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred;
|
||||
const bool bUpstreamPrefersLaunchRecovery =
|
||||
CachedCoachPanelState.PreferredQueueRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
|
||||
const bool bPreferLaunchRecoveryAction =
|
||||
!FindFirstDeferredQueueEntryId().IsEmpty()
|
||||
&& bCanLaunchQueueRecovery
|
||||
&& QueueRecoveryWeighting.PreferredActionKind
|
||||
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery;
|
||||
&& (QueueRecoveryWeighting.PreferredActionKind
|
||||
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery
|
||||
|| (!QueueRecoveryWeighting.IsStructurallyValid() && bUpstreamPrefersLaunchRecovery));
|
||||
const FString QueueRecoveryWeightingLabel =
|
||||
QueueRecoveryWeighting.PreferredActionKind
|
||||
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::PromoteDeferred
|
||||
|
|
@ -3876,6 +3891,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery
|
||||
? TEXT("prefer launch-recovery")
|
||||
: TEXT("no learned preference"));
|
||||
const FString QueueRecoveryUpstreamActionLabel = bUpstreamPrefersPromoteRecovery
|
||||
? TEXT("prefer promote-deferred recovery")
|
||||
: (bUpstreamPrefersLaunchRecovery
|
||||
? TEXT("prefer launch-recovery")
|
||||
: TEXT("no upstream preference"));
|
||||
const bool bHasQueueRecoveryFriction =
|
||||
CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals
|
||||
|| CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals
|
||||
|
|
@ -3884,7 +3904,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
|| CachedCoachQueueExecutionSummary.bHasCompletionGap;
|
||||
const FString QueueRecoveryStatusLine = bQueueRecoveryMemorySource
|
||||
? FString::Printf(
|
||||
TEXT("Recovery: %s lane | Source: %s | Action: %s | Mode: %s | Selection: %s | Learned: %s"),
|
||||
TEXT("Recovery: %s lane | Source: %s | Action: %s | Mode: %s | Selection: %s | Upstream: %s | Learned: %s"),
|
||||
*QueueRecoveryLaneLabel,
|
||||
*QueueRecoverySourceLabel,
|
||||
CachedCoachPanelState.PrimaryActionLabel.IsEmpty()
|
||||
|
|
@ -3892,19 +3912,22 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
: *CachedCoachPanelState.PrimaryActionLabel,
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(CachedCoachPanelState.SuggestedMode),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(CachedCoachPanelState.SuggestedSelectionPolicy),
|
||||
*QueueRecoveryUpstreamActionLabel,
|
||||
*QueueRecoveryWeightingLabel
|
||||
)
|
||||
: (bHasQueueRecoveryFriction
|
||||
? FString::Printf(
|
||||
TEXT("Recovery: queue friction is present, but no queue-memory recovery override is currently active | Learned: %s"),
|
||||
TEXT("Recovery: queue friction is present, but no queue-memory recovery override is currently active | Upstream: %s | Learned: %s"),
|
||||
*QueueRecoveryUpstreamActionLabel,
|
||||
*QueueRecoveryWeightingLabel
|
||||
)
|
||||
: FString::Printf(
|
||||
TEXT("Recovery: no queue-memory recovery override is currently active | Learned: %s"),
|
||||
TEXT("Recovery: no queue-memory recovery override is currently active | Upstream: %s | Learned: %s"),
|
||||
*QueueRecoveryUpstreamActionLabel,
|
||||
*QueueRecoveryWeightingLabel
|
||||
));
|
||||
const FString QueueRecoveryDetailLine = FString::Printf(
|
||||
TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s || Learned promote %d/%d reduced, %d failed, avg delta %.2f | launch %d/%d reduced, %d failed, avg delta %.2f"),
|
||||
TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s | Upstream action source: %s || Learned promote %d/%d reduced, %d failed, avg delta %.2f | launch %d/%d reduced, %d failed, avg delta %.2f"),
|
||||
CachedCoachQueueExecutionSummary.DeferredEventCount,
|
||||
CachedCoachQueueExecutionSummary.PromotedEventCount,
|
||||
CachedCoachQueueExecutionSummary.ManualRescheduleEventCount,
|
||||
|
|
@ -3915,6 +3938,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals ? TEXT("yes") : TEXT("no"),
|
||||
CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals ? TEXT("yes") : TEXT("no"),
|
||||
CachedCoachQueueExecutionSummary.bHasCompletionGap ? TEXT("yes") : TEXT("no"),
|
||||
*QueueRecoveryActionSourceLabel,
|
||||
QueueRecoveryWeighting.PromoteReducedCount,
|
||||
QueueRecoveryWeighting.PromoteCount,
|
||||
QueueRecoveryWeighting.PromoteFailureCount,
|
||||
|
|
|
|||
|
|
@ -352,6 +352,12 @@ TArray<FHyperTwistCoachSignal> UHyperTwistTrainingCoachLibrary::DeriveCoachSigna
|
|||
const bool bQueueRecoveryRecommended =
|
||||
bQueueRecoverySource
|
||||
&& CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended;
|
||||
const bool bPromoteQueueRecovery =
|
||||
CoachMemorySnapshot.PreferredQueueRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred;
|
||||
const bool bLaunchQueueRecovery =
|
||||
CoachMemorySnapshot.PreferredQueueRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
|
||||
const float Score = 66.0f
|
||||
+ FMath::Min(CoachMemorySnapshot.ScheduleExecutionPressureScore * 0.25f, 20.0f)
|
||||
+ (bArchiveAware ? 5.0f : 0.0f)
|
||||
|
|
@ -364,24 +370,47 @@ TArray<FHyperTwistCoachSignal> UHyperTwistTrainingCoachLibrary::DeriveCoachSigna
|
|||
? EHyperTwistCoachFocusLane::Stability
|
||||
: EHyperTwistCoachFocusLane::Cadence;
|
||||
const FString Headline = bQueueRecoveryFollowUp
|
||||
? TEXT("Recover the deferred follow-up lane before the queue drifts further")
|
||||
? (bPromoteQueueRecovery
|
||||
? TEXT("Promote the deferred follow-up lane back into active queue flow")
|
||||
: TEXT("Recover the deferred follow-up lane before the queue drifts further"))
|
||||
: (bQueueRecoveryRecommended
|
||||
? TEXT("Recover the deferred recommended lane and restore queue cadence")
|
||||
? (bPromoteQueueRecovery
|
||||
? TEXT("Promote the deferred recommended lane back into the active queue")
|
||||
: TEXT("Recover the deferred recommended lane and restore queue cadence"))
|
||||
: (bBlockedFollowUp
|
||||
? TEXT("Follow-up scheduling friction is stalling the queue")
|
||||
? (bPromoteQueueRecovery
|
||||
? TEXT("Promote deferred work to relieve follow-up scheduling friction")
|
||||
: TEXT("Follow-up scheduling friction is stalling the queue"))
|
||||
: (bArchiveAware
|
||||
? TEXT("Archive-aware scheduling friction needs cleanup")
|
||||
: TEXT("Coaching queue scheduling needs intervention"))));
|
||||
: (bLaunchQueueRecovery
|
||||
? TEXT("Launch recovery work to restore coaching queue cadence")
|
||||
: TEXT("Coaching queue scheduling needs intervention")))));
|
||||
const FString DeferralReasonLabel = !CoachMemorySnapshot.DominantScheduleDeferralReasonLabel.IsEmpty()
|
||||
? CoachMemorySnapshot.DominantScheduleDeferralReasonLabel
|
||||
: TEXT("none");
|
||||
const FString RecoverySourceLabel = bQueueRecoverySource
|
||||
? CoachMemorySnapshot.PreferredGuidanceSourceLabel
|
||||
: TEXT("none");
|
||||
const FString RecoveryActionLabel =
|
||||
CoachMemorySnapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred
|
||||
? TEXT("promote-deferred")
|
||||
: (CoachMemorySnapshot.PreferredQueueRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery
|
||||
? TEXT("launch-recovery")
|
||||
: TEXT("none"));
|
||||
const FString RecoveryActionSourceLabel =
|
||||
CoachMemorySnapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty()
|
||||
? TEXT("none")
|
||||
: CoachMemorySnapshot.PreferredQueueRecoveryActionSourceLabel;
|
||||
const FString SignalId = bQueueRecoveryFollowUp
|
||||
? TEXT("schedule_friction_follow_up_recovery")
|
||||
? (bPromoteQueueRecovery
|
||||
? TEXT("schedule_friction_follow_up_promote_recovery")
|
||||
: TEXT("schedule_friction_follow_up_recovery"))
|
||||
: (bQueueRecoveryRecommended
|
||||
? TEXT("schedule_friction_recommended_recovery")
|
||||
? (bPromoteQueueRecovery
|
||||
? TEXT("schedule_friction_recommended_promote_recovery")
|
||||
: TEXT("schedule_friction_recommended_recovery"))
|
||||
: TEXT("schedule_friction"));
|
||||
const EHyperTwistTrainingDeliveryMode SuggestedMode = bQueueRecoveryFollowUp
|
||||
? EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
|
|
@ -404,14 +433,16 @@ TArray<FHyperTwistCoachSignal> UHyperTwistTrainingCoachLibrary::DeriveCoachSigna
|
|||
Score,
|
||||
Headline,
|
||||
FString::Printf(
|
||||
TEXT("deferred=%d, manual=%d, archiveAware=%d, blockedFollowUp=%d, completionGap=%d, deferralReason=%s, recoverySource=%s"),
|
||||
TEXT("deferred=%d, manual=%d, archiveAware=%d, blockedFollowUp=%d, completionGap=%d, deferralReason=%s, recoverySource=%s, recoveryAction=%s, recoveryActionSource=%s"),
|
||||
CoachMemorySnapshot.ScheduleDeferredEventCount,
|
||||
CoachMemorySnapshot.ScheduleManualRescheduleEventCount,
|
||||
CoachMemorySnapshot.ScheduleArchiveAwareEventCount,
|
||||
CoachMemorySnapshot.ScheduleBlockedFollowUpEventCount,
|
||||
CoachMemorySnapshot.bHasQueueExecutionCompletionGap ? 1 : 0,
|
||||
*DeferralReasonLabel,
|
||||
*RecoverySourceLabel),
|
||||
*RecoverySourceLabel,
|
||||
*RecoveryActionLabel,
|
||||
*RecoveryActionSourceLabel),
|
||||
SuggestedMode,
|
||||
SuggestedSelectionPolicy);
|
||||
Signal.SuggestedDeckId = CoachMemorySnapshot.FocusDeckId;
|
||||
|
|
|
|||
|
|
@ -5217,6 +5217,43 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
|
|||
DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
|
||||
|| (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
|
||||
&& QueueExecutionSummary.PromotedEventCount > 0);
|
||||
const float PromoteRecoveryActionPressure =
|
||||
(QueueExecutionSummary.bHasOutstandingDeferredWork ? 4.0f : 0.0f)
|
||||
+ FMath::Min(static_cast<float>(QueueExecutionSummary.DeferredEventCount) * 0.75f, 3.0f)
|
||||
+ FMath::Min(static_cast<float>(QueueExecutionSummary.PromotedEventCount), 3.0f)
|
||||
+ (DominantDeferredLane != EHyperTwistTrainingCoachGuidanceLane::None ? 1.5f : 0.0f)
|
||||
- (QueueExecutionSummary.bHasCompletionGap ? 1.0f : 0.0f);
|
||||
const float LaunchRecoveryActionPressure =
|
||||
(QueueExecutionSummary.bHasCompletionGap ? 4.0f : 0.0f)
|
||||
+ (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 2.5f : 0.0f)
|
||||
+ (QueueExecutionSummary.bHasRepeatManualRescheduleFriction ? 2.0f : 0.0f)
|
||||
+ (QueueExecutionSummary.bHasArchiveAwareFriction ? 1.0f : 0.0f)
|
||||
+ FMath::Min(static_cast<float>(QueueExecutionSummary.CompletedEventCount), 3.0f)
|
||||
+ FMath::Min(static_cast<float>(QueueExecutionSummary.ManualRescheduleEventCount) * 0.5f, 2.0f);
|
||||
if (PromoteRecoveryActionPressure > 0.0f || LaunchRecoveryActionPressure > 0.0f)
|
||||
{
|
||||
if (PromoteRecoveryActionPressure > LaunchRecoveryActionPressure)
|
||||
{
|
||||
Snapshot.PreferredQueueRecoveryAction =
|
||||
EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred;
|
||||
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("queue-history-promote-pressure");
|
||||
}
|
||||
else if (LaunchRecoveryActionPressure > PromoteRecoveryActionPressure)
|
||||
{
|
||||
Snapshot.PreferredQueueRecoveryAction =
|
||||
EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
|
||||
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("queue-history-launch-pressure");
|
||||
}
|
||||
else
|
||||
{
|
||||
Snapshot.PreferredQueueRecoveryAction =
|
||||
QueueExecutionSummary.bHasOutstandingDeferredWork
|
||||
&& !QueueExecutionSummary.bHasCompletionGap
|
||||
? EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred
|
||||
: EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
|
||||
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("queue-history-balanced-pressure");
|
||||
}
|
||||
}
|
||||
|
||||
if (bQueueHistoryPrefersFollowUp)
|
||||
{
|
||||
|
|
@ -5364,6 +5401,15 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
|
|||
: EHyperTwistTrainingCoachGuidanceLane::FollowUp;
|
||||
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-default");
|
||||
}
|
||||
if (Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::None
|
||||
&& Snapshot.bNeedsScheduleFrictionRecovery)
|
||||
{
|
||||
Snapshot.PreferredQueueRecoveryAction = Snapshot.bHasOutstandingDeferredScheduleWork
|
||||
&& !Snapshot.bHasQueueExecutionCompletionGap
|
||||
? EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred
|
||||
: EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
|
||||
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("coach-memory-schedule-default");
|
||||
}
|
||||
|
||||
return Snapshot;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,6 +322,9 @@ FHyperTwistTrainingCoachPanelState UHyperTwistTrainingSubsystem::GetActiveCoachP
|
|||
PanelState.SignalCount = ActiveCoachSignals.Num();
|
||||
PanelState.PreferredGuidanceLane = ActiveCoachMemorySnapshot.PreferredGuidanceLane;
|
||||
PanelState.PreferredGuidanceSourceLabel = ActiveCoachMemorySnapshot.PreferredGuidanceSourceLabel;
|
||||
PanelState.PreferredQueueRecoveryAction = ActiveCoachMemorySnapshot.PreferredQueueRecoveryAction;
|
||||
PanelState.PreferredQueueRecoveryActionSourceLabel =
|
||||
ActiveCoachMemorySnapshot.PreferredQueueRecoveryActionSourceLabel;
|
||||
PanelState.bRepositoryVerificationPassed = ActiveRepositoryRoundTripVerification.bDidRoundTripPass;
|
||||
PanelState.bRepositoryVerificationNeedsAttention = ActiveRepositoryRoundTripVerification.ResolvedPath.IsEmpty()
|
||||
|| !ActiveRepositoryRoundTripVerification.bDidRoundTripPass;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ enum class EHyperTwistTrainingCoachGuidanceLane : uint8
|
|||
FollowUp UMETA(DisplayName = "Follow-Up")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingQueueRecoveryAction : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "None"),
|
||||
PromoteDeferred UMETA(DisplayName = "Promote Deferred"),
|
||||
LaunchRecovery UMETA(DisplayName = "Launch Recovery")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingPromptKind : uint8
|
||||
{
|
||||
|
|
@ -1837,6 +1845,13 @@ struct FHyperTwistTrainingCoachMemorySnapshot
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PreferredGuidanceSourceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingQueueRecoveryAction PreferredQueueRecoveryAction =
|
||||
EHyperTwistTrainingQueueRecoveryAction::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PreferredQueueRecoveryActionSourceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 RecommendedGuidancePlanCount = 0;
|
||||
|
||||
|
|
@ -4281,6 +4296,13 @@ struct FHyperTwistTrainingCoachPanelState
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PreferredGuidanceSourceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingQueueRecoveryAction PreferredQueueRecoveryAction =
|
||||
EHyperTwistTrainingQueueRecoveryAction::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PreferredQueueRecoveryActionSourceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SignalCount = 0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue