Fix: 상태머신 루프 블로킹 문제 수정 - SPS 이벤트 핸들러 비동기 처리 및 타임아웃 보호 추가

- sm_SPS 이벤트 핸들러에서 장치 연결 및 상태 전송을 비동기로 처리

- DeviceConnectionWorker 스레드로 장치 연결 분리

- SPS(1초), Running(2초) 타임아웃 보호 추가

- 상태머신 모니터링 디버그 창 추가 (fStateMachineDebug)

- F11/F12 단축키로 스레드 덤프 및 디버그 브레이크 지원

- RaiseMessage 이벤트 비동기 처리로 로그 블로킹 방지
This commit is contained in:
backuppc
2025-12-04 14:43:57 +09:00
parent a46d0b526d
commit 34ad1db0e3
8 changed files with 1557 additions and 45 deletions

View File

@@ -48,6 +48,26 @@ namespace Project
}
else if (e1.KeyCode == Keys.F5) btAutoRun.PerformClick();
else if (e1.KeyCode == Keys.F9) btAutoRun.PerformClick();
else if (e1.KeyCode == Keys.F11 && System.Diagnostics.Debugger.IsAttached)
{
// F11: 모든 스레드 상태 덤프
DumpAllThreadsState();
}
else if (e1.KeyCode == Keys.F12 && System.Diagnostics.Debugger.IsAttached)
{
// F12: 다음 sm_Running 호출시 디버거 중단
RequestDebugBreak = true;
var lastCall = DateTime.Now - lastSmRunningTime;
PUB.log.Add("DEBUG", $"F12 pressed - 마지막 sm_Running 호출: {lastCall.TotalSeconds:F1}초 전, IsThreadRun: {PUB.sm.IsThreadRun}");
System.Windows.Forms.MessageBox.Show(
$"다음 sm_Running 호출시 디버거가 중단됩니다.\n\n" +
$"마지막 호출: {lastCall.TotalSeconds:F1}초 전\n" +
$"IsThreadRun: {PUB.sm.IsThreadRun}\n" +
$"Current Step: {PUB.sm.Step}",
"디버그 모드",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Information);
}
if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now;
};
@@ -239,15 +259,67 @@ namespace Project
PUB.sm.SetMsgOptOff(); //모든 메세지출력을 해제한다. (이벤트는 동작함)
PUB.log.Add("State Machine Start");
PUB.sm = new StateMachine.StateMachine();
try
{
PUB.sm = new StateMachine.StateMachine();
PUB.log.Add("StateMachine", $"객체 생성 완료 - Type: {PUB.sm.GetType().FullName}");
// StateMachine 객체의 속성 확인
var smType = PUB.sm.GetType();
var properties = smType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
PUB.log.Add("StateMachine", $"Public Properties: {properties.Length}개");
var methods = smType.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
PUB.log.Add("StateMachine", $"Public Methods: {methods.Length}개");
}
catch (Exception ex)
{
PUB.log.AddE($"StateMachine 객체 생성 실패: {ex.Message}");
PUB.log.AddE($"StackTrace: {ex.StackTrace}");
throw;
}
PUB.sm.StepChanged += sm_StepChanged;
PUB.log.Add("StateMachine", "StepChanged 이벤트 등록 완료");
PUB.sm.Message += sm_Message;
PUB.log.Add("StateMachine", "Message 이벤트 등록 완료");
PUB.sm.Running += sm_Running;
PUB.log.Add("StateMachine", "Running 이벤트 등록 완료");
PUB.sm.SPS += sm_SPS;
PUB.log.Add("StateMachine", "SPS 이벤트 등록 완료");
PUB.sm.Start();
PUB.log.Add("StateMachine", $"Start() 호출 완료 - IsThreadRun:{PUB.sm.IsThreadRun}");
// 스레드 시작 대기 (최대 3초)
for (int i = 0; i < 30; i++)
{
System.Threading.Thread.Sleep(100);
if (PUB.sm.IsThreadRun)
{
PUB.log.Add("StateMachine", $"스레드 시작 확인됨 ({i * 100}ms 소요)");
break;
}
}
if (!PUB.sm.IsThreadRun)
{
PUB.log.AddE( "경고: 3초 대기 후에도 스레드가 시작되지 않음!");
System.Windows.Forms.MessageBox.Show(
"상태머신 스레드가 시작되지 않았습니다.\n" +
"로그 파일을 확인하세요.",
"오류",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Error);
}
tmDisplay.Tick += tmDisplay_Tick;
tmDisplay.Start(); //start Display
PUB.log.Add("Display", "Display Timer 시작 완료");
this.btDebug.Visible = System.Diagnostics.Debugger.IsAttached || PUB.setting.UseDebugMode;
@@ -1015,6 +1087,68 @@ namespace Project
}
}
private void DumpAllThreadsState()
{
try
{
var sb = new System.Text.StringBuilder();
sb.AppendLine("===== 스레드 상태 덤프 =====");
sb.AppendLine($"시간: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
sb.AppendLine($"마지막 sm_Running 호출: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전");
sb.AppendLine($"sm_Running 호출 횟수: {sm_Running_CallCount}");
sb.AppendLine($"IsThreadRun: {PUB.sm.IsThreadRun}");
sb.AppendLine($"Current Step: {PUB.sm.Step}");
sb.AppendLine($"Current RunStep: {PUB.sm.RunStep}");
sb.AppendLine();
var process = System.Diagnostics.Process.GetCurrentProcess();
sb.AppendLine($"총 스레드 수: {process.Threads.Count}");
sb.AppendLine();
foreach (System.Diagnostics.ProcessThread thread in process.Threads)
{
sb.AppendLine($"Thread {thread.Id}:");
sb.AppendLine($" State: {thread.ThreadState}");
sb.AppendLine($" Priority: {thread.PriorityLevel}");
if (thread.ThreadState == System.Diagnostics.ThreadState.Wait)
{
sb.AppendLine($" WaitReason: {thread.WaitReason}");
}
sb.AppendLine();
}
var dump = sb.ToString();
PUB.log.Add("THREAD_DUMP", dump);
Console.WriteLine(dump);
MessageBox.Show(
$"스레드 덤프 완료\n로그 파일에 저장되었습니다.\n\n" +
$"마지막 sm_Running: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전\n" +
$"IsThreadRun: {PUB.sm.IsThreadRun}",
"스레드 덤프",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
PUB.log.AddE($"DumpAllThreadsState 오류: {ex.Message}");
}
}
private void stateMachineDebugToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
var debugForm = new Dialog.fStateMachineDebug();
debugForm.Show();
}
catch (Exception ex)
{
MessageBox.Show($"디버그 창을 열 수 없습니다:\n{ex.Message}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void editorToolStripMenuItem_Click(object sender, EventArgs e)
{
try