Merge branch 'feature/ubisam-pptx-integration' of https://gitea.ubisampaju.synology.me/RobotAutomationDevelopment-Dept/LGES-OC5-PtoP-Welder into feature/ubisam-pptx-integration
# Conflicts: # WeldingComponents/WeldingCommon/Utils/RecipeHelper.cs
This commit is contained in:
commit
a9d9e4f4d6
@ -65,6 +65,9 @@ namespace DB
|
||||
if (json != null && json != "") jsonData = System.Text.Json.JsonSerializer.Deserialize<List<FieldScanData>>(json);
|
||||
else ;
|
||||
|
||||
// [추가 2026-08-04] Clear 와 짝을 맞춘다. 이 아래에서 예외가 나면(:77 f.PageName,
|
||||
// :84 js.PageName 의 NRE 등) 리스트는 빈 채 남으므로 플래그도 false 여야 한다.
|
||||
loadedOnce = false;
|
||||
fieldScanDatas.Clear();
|
||||
|
||||
// default 값 써주기
|
||||
@ -84,6 +87,11 @@ namespace DB
|
||||
else AddFieldScanData(js.PageName, defaultFieldScan.FileName, defaultFieldScan.FieldScans);
|
||||
}
|
||||
}
|
||||
|
||||
// [추가 2026-08-04] 완주했더라도 결과가 0건이면(pageCount==0 등) 로드된 것으로 보지 않는다.
|
||||
// ReloadSetting(MainPage.cs:448/452)/UpdateScoutField(:502/:505)가 Save 를 연속 2회 하므로,
|
||||
// 여기서 true 로 굳히면 두 번째 Save 가 가드를 통과해 "[]" 를 기록한다.
|
||||
loadedOnce = fieldScanDatas.Count > 0;
|
||||
} catch (Exception ex) {
|
||||
throw ex;
|
||||
}
|
||||
@ -95,6 +103,19 @@ namespace DB
|
||||
/// <param name="defaultFileStr"></param>
|
||||
public void SaveFieldHelper(string fileName, string defaultFileStr = null)
|
||||
{
|
||||
// [추가 2026-08-04] 완주한 로드의 결과가 아닌 빈 상태를 파일에 확정하지 않는다.
|
||||
// fieldScanDatas 는 프로세스 시작 시 빈 리스트인데
|
||||
// SaveAndLoadFieldScanFromLasaDb 는 Save 를 먼저 하므로(MainPage.cs:539 -> :540)
|
||||
// 기동 중 첫 호출이 대상 파일을 "[]" 로 밀어버린다. 현장 3건 발생 확인.
|
||||
// Save 만 건너뛰고 :540 Load 는 그대로 돌아 파일의 실제 데이터로 싱글톤을 채운다.
|
||||
// loadedOnce 로 게이트하는 이유: 사용자가 페이지를 지워 정당하게 비운 경우
|
||||
// (DeleteFieldScanData / 호출부 MainPage.cs:904-905)는 저장돼야 하기 때문이다.
|
||||
if (!loadedOnce && (fieldScanDatas == null || fieldScanDatas.Count == 0)) {
|
||||
SkippedEmptySaveCount++;
|
||||
SkippedFiles.Add(fileName ?? "(null)");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
using (MemoryStream stream = new MemoryStream()) {
|
||||
XmlTextWriter wr = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
@ -111,9 +132,15 @@ namespace DB
|
||||
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(fieldScanDatas, new JsonSerializerOptions { WriteIndented = true });
|
||||
File.WriteAllText(fileName, json);
|
||||
LastSaveError = null; // [추가 2026-08-04] 성공했으면 이전 오류를 남기지 않는다
|
||||
}
|
||||
} catch {
|
||||
|
||||
} catch (Exception ex) {
|
||||
// [변경 2026-08-04] 쓰기 실패가 로그 없이 사라져 데이터 소실을 관측할 수 없었다.
|
||||
// 단 throw 하지 않는다 - ReloadSetting(MainPage.cs:448/452) 경로가
|
||||
// Rx OnNext 안이라(UCScannerObjectTeachingViewModel.cs:55-57 Subscribe)
|
||||
// 전파되면 구독이 끊기고 티칭 화면 갱신이 영구 정지한다.
|
||||
// MainPage.cs:1433 콤보박스 핸들러에는 try/catch 가 아예 없다.
|
||||
LastSaveError = $"{fileName} :: {ex.Message}";
|
||||
}
|
||||
}
|
||||
public List<ChildBufRowCol> GetPageFieldScan(string pageName)
|
||||
@ -172,6 +199,17 @@ namespace DB
|
||||
|
||||
#region 데이터를 위한 Class
|
||||
private List<FieldScanData> fieldScanDatas = new List<FieldScanData>();
|
||||
|
||||
// [추가 2026-08-04] 빈 상태 저장 차단 및 관측용.
|
||||
// 이 프로젝트(LASAGUI)에는 Aurora 참조가 없어 여기서 로그를 남길 수 없으므로
|
||||
// 상태만 노출하고, WeldingCommon 쪽(RecipeHelper)에서 Aurora Logger 로 보고한다.
|
||||
// loadedOnce 의 의미: "지금 fieldScanDatas 가 완주한 로드의 결과인가".
|
||||
// 한 번이라도 성공했는가(write-once)가 아니다 - Clear 이후 예외가 나면 다시 false 가 된다.
|
||||
private bool loadedOnce = false;
|
||||
public static int SkippedEmptySaveCount = 0;
|
||||
public static readonly HashSet<string> SkippedFiles =
|
||||
new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
public static string LastSaveError = null;
|
||||
public class FieldScanData
|
||||
{
|
||||
public string PageName { get; set; }
|
||||
|
||||
@ -3,6 +3,7 @@ using Aurora.Api;
|
||||
using Aurora.Api.Manager;
|
||||
using DB;
|
||||
using DB.Recipe;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
@ -15,22 +16,8 @@ namespace WeldingCommon.Utils
|
||||
{
|
||||
public static class RecipeHelper
|
||||
{
|
||||
// ═══ [진단계측 2026-08-04] 시작 ═══════════════════════════════════════
|
||||
// 원본 1줄 : public static (int id, string name) SelectedRecipe;
|
||||
// 대상(SelectedRecipe)이 바뀌는 순간을 잡기 위해 필드 → 프로퍼티로 전환.
|
||||
// 외부에서의 읽기/쓰기 방식은 동일하다(대입 4곳, 나머지는 .name/.id 읽기뿐).
|
||||
// 원복 : 아래 블록 전체를 위 원본 1줄로 되돌린다.
|
||||
static (int id, string name) _SelectedRecipe;
|
||||
public static (int id, string name) SelectedRecipe {
|
||||
get { return _SelectedRecipe; }
|
||||
set {
|
||||
var old = _SelectedRecipe;
|
||||
_SelectedRecipe = value;
|
||||
RecipeTrace.Mark("대상변경 SELECTED",
|
||||
$"[{old.id}][{old.name}] → [{value.id}][{value.name}]");
|
||||
}
|
||||
}
|
||||
// ═══ [진단계측 2026-08-04] 끝 ═════════════════════════════════════════
|
||||
public static (int id, string name) SelectedRecipe;
|
||||
static Logger Log = new Logger("RecipeHelper"); // [추가 2026-08-04]
|
||||
static IRecipeManager mRecipeManager = Application.Current.AuroraEnv().RecipeManager;
|
||||
|
||||
static string GetTeachingEditorFullPath(string recipeName)
|
||||
@ -50,18 +37,35 @@ namespace WeldingCommon.Utils
|
||||
|
||||
public static void SaveRecipe(string recipeName, bool includeModel = true)
|
||||
{
|
||||
// ═══ [진단계측 2026-08-04] 시작 ═══════════════════════════════════
|
||||
// 원본 1줄 : CommonHelper.GetMainPage().SaveRecipe(GetTeachingEditorFullPath(recipeName), includeModel);
|
||||
// 저장 전/후를 모두 찍어 무엇이 무엇으로 바뀌었는지 확인한다. 저장 자체는 막지 않는다.
|
||||
// 원복 : 이 블록 전체를 위 원본 1줄로 되돌린다.
|
||||
var targetPath = GetTeachingEditorFullPath(recipeName);
|
||||
|
||||
var snap = RecipeTrace.BeforeSave(recipeName, targetPath);
|
||||
// [추가 2026-08-04] 관측 전용. 저장을 막지 않는다.
|
||||
// 기준은 DB.Recipe.Recipe.SelectedRecipeFile - Recipe.Load(string) 성공 시에만
|
||||
// Recipe.cs:69 에서 세팅되며 디스크를 거치지 않는다.
|
||||
// (Dao.SystemRecipe.WorkRecipeFile 은 SystemParameterILSOS.json 에 영속되고
|
||||
// Aurora SystemParameter 와 파일을 공유하므로 신원 기준으로 쓸 수 없다)
|
||||
// 두 조건은 독립 신호이므로 else if 로 묶지 않는다.
|
||||
var loaded = DB.Recipe.Recipe.SelectedRecipeFile;
|
||||
if (string.IsNullOrWhiteSpace(loaded)) {
|
||||
Log.W($"[관측] 메모리 신원 없음(로드 이력 없음). target=[{targetPath}]");
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(loaded) &&
|
||||
!string.Equals(loaded, targetPath, StringComparison.OrdinalIgnoreCase)) {
|
||||
Log.W($"[관측] 메모리/대상 불일치. loaded=[{loaded}] target=[{targetPath}]");
|
||||
}
|
||||
|
||||
CommonHelper.GetMainPage().SaveRecipe(targetPath, includeModel);
|
||||
|
||||
RecipeTrace.AfterSave(snap);
|
||||
// ═══ [진단계측 2026-08-04] 끝 ═════════════════════════════════════
|
||||
// 아래는 이번 저장과 무관한 다른 경로(ReloadSetting/UpdateScoutField/콤보박스)에서
|
||||
// 누적된 것일 수 있다. 경로는 각 항목의 파일명으로 판독한다.
|
||||
if (DB.RecipeFieldScan.SkippedEmptySaveCount > 0 || DB.RecipeFieldScan.LastSaveError != null) {
|
||||
Log.W($"[관측] FieldHelper 저장 생략 {DB.RecipeFieldScan.SkippedEmptySaveCount}회 " +
|
||||
$"(대상 {DB.RecipeFieldScan.SkippedFiles.Count}종: {string.Join(" | ", DB.RecipeFieldScan.SkippedFiles)}), " +
|
||||
$"최근오류=[{DB.RecipeFieldScan.LastSaveError}] ※직전 저장과 무관할 수 있음");
|
||||
DB.RecipeFieldScan.SkippedEmptySaveCount = 0;
|
||||
DB.RecipeFieldScan.SkippedFiles.Clear();
|
||||
DB.RecipeFieldScan.LastSaveError = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnSaveRecipeForEditing()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user