From bd6a7cd21fa06b60978d70385faa0a35f4b7fe26 Mon Sep 17 00:00:00 2001 From: NohDoyeon241104 Date: Fri, 4 Sep 2026 14:13:44 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=A0=88=EC=8B=9C=ED=94=BC=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B5=90=EC=B0=A8=EC=98=A4=EC=97=BC=20=ED=8A=B8?= =?UTF-8?q?=EB=A6=AC=EA=B1=B0=20=EC=B0=A8=EB=8B=A8=20(ISSUE-260904-02)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OnActivatedRecipe 의 무조건 SaveRecipe 를 활성==선택 조건부로 변경하고 불일치 시 스킵 로그. PreSaved/OnSaved 에 활성!=선택 저장 차단과 안내 모달 추가. ISSUE-260826-04 의 트리거 차단이며 근본 해결은 아님. 현장에서 차단 동작 확인 완료. --- .../UCScannerObjectTeachingViewModel.cs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingViewModel.cs b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingViewModel.cs index a93e2b2..063984d 100644 --- a/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingViewModel.cs +++ b/WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingViewModel.cs @@ -153,7 +153,21 @@ namespace WeldingCommon.Views Log.I($"Activate Model Name : {modelname}"); var activeRecipe = _RecipeManager.ActivatedRecipe(); - RecipeHelper.SaveRecipe(activeRecipe.Name); + + // [레시피 덮어쓰기 이슈] 오토/메뉴얼 전환 시 저장 제거 + // 활성 전환 직후 메모리는 아직 이전 레시피 내용이다(LASA 는 레시피를 메모리에 1개만 유지). + // 그 상태에서 무조건 저장하면 새 활성 레시피 파일에 이전 내용이 덮어써진다(교차오염). + // ※ 완전 제거가 아니라 신원이 일치할 때만 저장하도록 좁혔다. + // OnRecipeEntryChanged 의 Copy/Rename 분기가 비어 있어(:118-121) + // 이 저장이 새 레시피 폴더의 TeachingEditor 파일 생성을 겸했을 가능성이 있기 때문. + // 스킵 로그는 현장 발생 빈도 확인용이다. 정상 운영(활성=선택)에서는 저장이 그대로 수행된다. + var selectedNameOnActivate = RecipeHelper.SelectedRecipe.name; + if (string.Equals(activeRecipe.Name, selectedNameOnActivate, StringComparison.OrdinalIgnoreCase)) { + RecipeHelper.SaveRecipe(activeRecipe.Name); + } else { + Log.W($"[레시피 덮어쓰기 차단] OnActivatedRecipe 자동저장 스킵. " + + $"활성=[{activeRecipe.Name}] 선택=[{selectedNameOnActivate}]"); + } // Aurora에서 제공하는 이벤트(SYS_RECIPE_ACTIVATED)를 체인으로 사용하면 동작(Aurora가 저장 후 LASA가 저장하는)에 대한 우선순위가 보장이 안되기 때문에 _Aurora.EventPublisher.Publish(new EventMessage("OnActivatedRecipe", CommonEventIds.EVENT_CHANGE_RECIPE_INFO, activeRecipe)); @@ -190,6 +204,25 @@ namespace WeldingCommon.Views MessageUtil.ShowErrorToast("Auto 상태에서 레시피 변경 불가", Log); return false; } + + // [레시피 덮어쓰기 이슈] 활성 ≠ 선택이면 저장 차단 + // 저장 대상은 선택 레시피인데 화면 메모리는 활성 레시피 내용이라, + // 그대로 저장하면 엉뚱한 번호 파일에 다른 레시피 내용이 덮어써진다. + // 운영 원칙(활성 레시피 = 선택 레시피)이 지켜지면 이 분기는 발화하지 않는다. + var activeOnSave = _RecipeManager.ActivatedRecipe(); + var selectedOnSave = RecipeHelper.SelectedRecipe.name; + if (activeOnSave != null && !string.IsNullOrWhiteSpace(selectedOnSave) && + !string.Equals(activeOnSave.Name, selectedOnSave, StringComparison.OrdinalIgnoreCase)) { + Log.E($"[레시피 덮어쓰기 차단] PreSaved 저장 차단. " + + $"저장대상=[{recipeName}] 선택=[{selectedOnSave}] 활성=[{activeOnSave.Name}]"); + MessageUtil.CreateAndShowMessageDialog("레시피 저장 차단", + $"활성 레시피와 선택된 레시피가 다릅니다.\n\n" + + $" 선택 레시피 : {selectedOnSave}\n" + + $" 활성 레시피 : {activeOnSave.Name}\n\n" + + $"이대로 저장하면 다른 레시피 내용이 덮어써집니다.\n" + + $"레시피 목록에서 저장할 레시피를 다시 선택한 뒤 저장하십시오."); + return false; + } return true; } catch (Exception ex) { @@ -205,6 +238,23 @@ namespace WeldingCommon.Views return; } + // [레시피 덮어쓰기 이슈] 저장 직전 2중 방어. + // PreSaved 가 먼저 막으므로 정상적으로는 도달하지 않는다. + // 도달했다면 Aurora 가 PreSaved=false 를 무시했다는 뜻이라 이미 이상 상황이다. + // ※ 조용히 스킵하면 사용자가 "저장됐다"고 오인하므로 반드시 알린다. + var activeOnSaved = _RecipeManager.ActivatedRecipe(); + if (activeOnSaved != null && + !string.Equals(activeOnSaved.Name, RecipeHelper.SelectedRecipe.name, StringComparison.OrdinalIgnoreCase)) { + Log.E($"[레시피 덮어쓰기 차단] OnSaved 저장 스킵. " + + $"저장대상=[{recipeName}] 선택=[{RecipeHelper.SelectedRecipe.name}] 활성=[{activeOnSaved.Name}]"); + MessageUtil.CreateAndShowMessageDialog("레시피 저장 차단", + $"활성 레시피와 선택된 레시피가 달라 저장하지 않았습니다.\n\n" + + $" 선택 레시피 : {RecipeHelper.SelectedRecipe.name}\n" + + $" 활성 레시피 : {activeOnSaved.Name}\n\n" + + $"저장되지 않았습니다. 레시피 목록에서 저장할 레시피를 다시 선택한 뒤 저장하십시오."); + return; + } + RecipeHelper.SaveRecipe(RecipeHelper.SelectedRecipe.name); var ChangedRecipe = RecipeHelper.getRecipeInfo(RecipeHelper.SelectedRecipe.name); var diffs = new ObservableCollection();