Files
XRLib/Assets/Scripts/Simulator/UI/PropertyWindow/ProcessorProperty.cs

1083 lines
45 KiB
C#
Raw Normal View History

2025-12-24 17:36:01 +09:00
using Simulator.Data;
2026-01-16 11:36:54 +09:00
using System;
2025-12-24 17:36:01 +09:00
using System.Collections.Generic;
using UnityEngine;
using UVC.UI.Window.PropertyWindow;
public class ProcessorProperty : MonoBehaviour
{
[SerializeField]
private PropertyWindow propertyWindow;
2026-01-16 11:36:54 +09:00
ProcessorDataClass processor;
2025-12-24 17:36:01 +09:00
private void Awake()
{
propertyWindow.PropertyValueChanged += OnPropertyValueChanged;
}
2026-02-25 16:30:12 +09:00
public void SetPropertyWindow(ComponentType type, ComponentDataBase data)
2025-12-24 17:36:01 +09:00
{
2026-02-10 17:01:25 +09:00
if (PlayerPropertyDataBase.isPlaying)
{
return;
}
2025-12-24 17:36:01 +09:00
switch (type)
{
case ComponentType.Processor:
2026-01-16 11:36:54 +09:00
processor = data as ProcessorDataClass;
2026-02-03 11:40:26 +09:00
InitProcessorProperty(data as ProcessorDataClass);
2025-12-24 17:36:01 +09:00
break;
}
}
void SaveChange(object source, object value, string name)
{
var path = PathIndexer.GetNodePath(source);
Patch updateData = new Patch();
updateData.value = value;
UpdateValueStack.AddPatch($"{path}.{name}", value);
}
2026-02-03 11:40:26 +09:00
public void InitProcessorProperty(ProcessorDataClass processor)
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
var entries = new List<IPropertyEntry>();
entries.Add(new StringProperty("name", "이름", processor.name) { IsReadOnly = true });
entries.Add(
new StringProperty("label", "라벨", processor.label) { IsReadOnly = false }
.Bind(setter: v => { processor.label = v; SaveChange(processor, v, "label"); })
);
2026-02-03 11:40:26 +09:00
entries.Add(CreatePositionGroup(processor));
2026-01-16 11:36:54 +09:00
2026-02-10 17:01:25 +09:00
entries.Add(
new ListProperty("model_type", "모델 타입",
new List<string>() { "processor_workbench", "processor_worker", "processor_conveyor_port", "processor_conveyor_roller" }, processor.model_type)
{ IsReadOnly = false }
.Bind(setter: v => { processor.model_type = v; SaveChange(processor, v, "model_type"); })
);
2026-01-16 11:36:54 +09:00
entries.Add(
new ListProperty("processing_type", "처리 유형",
new List<string>() { "combine", "seperate", "transform" }, processor.processor_type)
{ IsReadOnly = false }
.Bind(setter: v => { processor.processor_type = v; SaveChange(processor, v, "processing_type"); })
);
entries.Add(
new ListProperty("input_selection_policy", "투입 정책",
new List<string>() { "round_robin", "random" }, processor.input_selection_policy)
{ IsReadOnly = false }
.Bind(setter: v => { processor.input_selection_policy = v; SaveChange(processor, v, "input_selection_policy"); })
);
entries.Add(CreateInputPrefabToggleGroup(processor));
foreach (var prefab in PrefabManager.Instance.prefabDict.Keys)
entries.Add(CreateInputPrefabDetailGroup(processor, prefab));
entries.Add(
new ListProperty("output_selection_policy", "출고 정책",
new List<string>() { "round_robin", "random" }, processor.output_selection_policy)
{ IsReadOnly = false }
.Bind(setter: v => { processor.output_selection_policy = v; SaveChange(processor, v, "output_selection_policy"); })
);
entries.Add(CreateOutputPrefabToggleGroup(processor));
foreach (var prefab in PrefabManager.Instance.prefabDict.Keys)
entries.Add(CreateOutputPrefabDetailGroup(processor, prefab));
entries.Add(new EnumProperty("display_mode_ProcessingTimePolicy", "처리 시간 정책", processor.processing_time_policy.policy).Bind(
setter: v =>
{
2026-02-04 15:51:59 +09:00
processor.processing_time_policy = PolicyFactory.Create(v.ToString()); SaveChange(processor, PolicyFactory.Create(v.ToString()), "processing_time_policy");
2026-01-16 11:36:54 +09:00
}));
entries.Add(CreateProcessingTimePolicy_Constant(processor));
entries.Add(CreateProcessingTimePolicy_Normal(processor));
entries.Add(CreateProcessingTimePolicy_Uniform(processor));
entries.Add(CreateProcessingTimePolicy_Exponential(processor));
entries.Add(CreateProcessingTimePolicy_Triangular(processor));
2026-02-03 11:40:26 +09:00
entries.Add(BuildOutputTagChanges());
entries.Add(new ListProperty("outputTagType_string", "", new List<string> { "set" }, "set"));
entries.Add(new StringProperty("outputTagValue_string", "", ""));
entries.Add(new ListProperty("outputTagType_number", "", new List<string> { "set", "increment", "decrement" }, "값 설정"));
entries.Add(new IntProperty("outputTagValue_number", "", 0));
entries.Add(new ButtonProperty("AddTagChangeButton", "태그 변경 추가", "").BindClick(SaveOutputTag));
entries.Add(CreateOutputTags(processor));
2026-01-16 11:36:54 +09:00
entries.Add(new EnumProperty("display_mode_ReadyTimePolicy", "준비 시간 정책", processor.ready_time_policy.policy).Bind(
2026-02-03 11:40:26 +09:00
setter: v =>
{
2026-02-04 15:51:59 +09:00
processor.ready_time_policy = PolicyFactory.Create(v.ToString()); SaveChange(processor, PolicyFactory.Create(v.ToString()), "ready_time_policy");
2026-01-16 11:36:54 +09:00
}));
entries.Add(CreateReadyTimePolicy_Constant(processor));
entries.Add(CreateReadyTimePolicy_Normal(processor));
entries.Add(CreateReadyTimePolicy_Uniform(processor));
entries.Add(CreateReadyTimePolicy_Exponential(processor));
entries.Add(CreateReadyTimePolicy_Triangular(processor));
entries.Add(new EnumProperty("display_mode_DefectTimePolicy", "불량률 정책", processor.defect_rate_policy.policy).Bind(
2026-02-03 11:40:26 +09:00
setter: v =>
{
2026-02-04 15:51:59 +09:00
processor.defect_rate_policy = PolicyFactory.Create(v.ToString()); SaveChange(processor, PolicyFactory.Create(v.ToString()), "defect_rate_policy");
2026-01-16 11:36:54 +09:00
}));
entries.Add(CreateDefectRatePolicy_Constant(processor));
entries.Add(CreateDefectRatePolicy_Normal(processor));
entries.Add(CreateDefectRatePolicy_Uniform(processor));
entries.Add(CreateDefectRatePolicy_Exponential(processor));
entries.Add(CreateDefectRatePolicy_Triangular(processor));
entries.Add(CreateRequireResourceGroup(processor));
entries.Add(new BoolProperty("fms_input_enabled", "입력에 FMS 사용", processor.fms_input_enabled) { IsReadOnly = true });
entries.Add(new BoolProperty("fms_output_enabled", "출력에 FMS 사용", processor.fms_output_enabled) { IsReadOnly = true });
propertyWindow.LoadMixedProperties(entries);
HandleDisplayModeChanged_ProcessingTimePolicy(processor.processing_time_policy.policy.ToString());
HandleDisplayModeChanged_ReadyTimePolicy(processor.ready_time_policy.policy.ToString());
HandleDisplayModeChanged_DefectRatePolicy(processor.defect_rate_policy.policy.ToString());
SyncInputPrefabDetailVisibility(processor);
SyncOutputPrefabDetailVisibility(processor);
2026-02-03 11:40:26 +09:00
SyncOutputTagType(ComponentsManager.webconfigData.tags[0].type);
2026-01-16 11:36:54 +09:00
}
2026-02-03 11:40:26 +09:00
2026-01-16 11:36:54 +09:00
private void OnPropertyValueChanged(object sender, PropertyValueChangedEventArgs e)
{
// 동적 그룹 표시/비표시 테스트
if (e.PropertyId == "display_mode_ProcessingTimePolicy")
{
string selectedMode = e.NewValue.ToString();
HandleDisplayModeChanged_ProcessingTimePolicy(selectedMode);
}
if (e.PropertyId == "display_mode_ReadyTimePolicy")
{
string selectedMode = e.NewValue.ToString();
HandleDisplayModeChanged_ReadyTimePolicy(selectedMode);
}
if (e.PropertyId == "display_mode_DefectPolicy")
{
string selectedMode = e.NewValue.ToString();
HandleDisplayModeChanged_DefectRatePolicy(selectedMode);
}
if (e.PropertyId == "input_selection_policy")
{
SyncInputPrefabDetailVisibility(processor);
}
if (e.PropertyId == "output_selection_policy")
{
SyncOutputPrefabDetailVisibility(processor);
}
}
2026-02-03 11:40:26 +09:00
private PropertyGroup CreatePositionGroup(ProcessorDataClass processor)
{
var group = new PropertyGroup("processor_position", "위치 및 회전", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("x_position", "X 좌표(m)", processor.physical.position.x)
{
}.Bind(
setter: v => {processor.physical.position.x = v;SaveChange(processor,v,"physical.position.x"); }
),
new FloatProperty("y_position", "y 좌표(m)", processor.physical.position.y)
{
}.Bind(
setter: v => {processor.physical.position.y = v;SaveChange(processor,v,"physical.position.y"); }
),
new FloatProperty("orientation", "회전(°)", processor.physical.orientation)
{
}.Bind(
2026-02-25 16:30:12 +09:00
setter: v => {processor.physical.orientation = v;SaveChange(processor,v,"physical.orientation");propertyWindow.ApplyExternalValue("orientation",RotationSnap.SnapOrientation(v),false); }
2026-02-03 11:40:26 +09:00
),
});
return group;
}
#region
public ListProperty BuildOutputTagChanges()
{
List<string> tagKeyList = new List<string>();
foreach (var tag in ComponentsManager.webconfigData.tags)
{
tagKeyList.Add(tag.key);
}
var property = new ListProperty("output_tag_keys", "처리 완료 태그 설정", tagKeyList, tagKeyList[0]);
property.Bind(setter: v => { SyncOutputTagType(ComponentsManager.webconfigData.tags.Find(tag => tag.key == v).type); });
return property;
}
public PropertyGroup CreateOutputTags(ProcessorDataClass processor)
{
var group = new PropertyGroup("output_tags", "", isExpanded: true);
foreach (var tag in processor.output_tag_changes)
{
var property = new ChangeTagProperty($"tag{tag.key}", tag.key, tag.operation, tag.value.ToString());
property.BindClick(() => { processor.output_tag_changes.Remove(tag); SaveChange(processor, processor.output_tag_changes, "output_tag_changes"); InitProcessorProperty(processor); });
property.Bind(setter: v => { tag.value = v; SaveChange(tag, v, "value"); });
group.AddItem(property);
}
return group;
}
public void SyncOutputTagType(string tagType)
{
propertyWindow.SetPropertyVisibility("outputTagType_string", false);
propertyWindow.SetPropertyVisibility("outputTagValue_string", false);
propertyWindow.SetPropertyVisibility("outputTagType_number", false);
propertyWindow.SetPropertyVisibility("outputTagValue_number", false);
propertyWindow.SetPropertyVisibility("AddTagChangeButton", false);
switch (tagType)
{
case "string":
propertyWindow.SetPropertyVisibility("outputTagType_string", true);
propertyWindow.SetPropertyVisibility("outputTagValue_string", true);
propertyWindow.SetPropertyVisibility("outputTagType_number", false);
propertyWindow.SetPropertyVisibility("outputTagValue_number", false);
propertyWindow.SetPropertyVisibility("AddTagChangeButton", true);
break;
case "number":
propertyWindow.SetPropertyVisibility("outputTagType_string", false);
propertyWindow.SetPropertyVisibility("outputTagValue_string", false);
propertyWindow.SetPropertyVisibility("outputTagType_number", true);
propertyWindow.SetPropertyVisibility("outputTagValue_number", true);
propertyWindow.SetPropertyVisibility("AddTagChangeButton", true);
break;
}
}
public void SaveOutputTag()
{
string key = propertyWindow.GetProperty("output_tag_keys").GetValue() as string;
string operation = "";
object value = null;
if (processor.output_tag_changes.Exists(x => x != null && x.key == key))
{
Debug.LogWarning($"이미 '{key}' 태그가 존재합니다.");
return;
}
var tagtype = ComponentsManager.webconfigData.tags?.Find(t => t.key == key);
switch (tagtype.type)
{
case "string":
operation = propertyWindow.GetProperty("outputTagType_string").GetValue() as string;
value = propertyWindow.GetProperty("outputTagValue_string").GetValue();
break;
case "number":
operation = propertyWindow.GetProperty("outputTagValue_number").GetValue() as string;
value = propertyWindow.GetProperty("outputTagValue_number").GetValue();
break;
}
processor.output_tag_changes.Add(new output_tag_changes_base
{
key = key,
operation = operation,
value = value,
});
SaveChange(processor, processor.output_tag_changes, "output_tag_changes");
InitProcessorProperty(processor);
}
#endregion
2026-01-16 11:36:54 +09:00
#region
private PropertyGroup CreateInputPrefabToggleGroup(ProcessorDataClass processor)
{
var group = new PropertyGroup("InputPrefab", "투입 개체", isExpanded: true);
foreach (var prefab in PrefabManager.Instance.prefabDict.Keys)
{
bool enabled = processor.inputs != null && processor.inputs.Exists(x => x.prefab == prefab);
string itemId = $"input_prefab.toggle.{prefab}";
string detailGroupId = GetInputPrefabDetailGroupId(prefab);
var toggle = new BoolProperty($"input_{itemId}", prefab, enabled)
.Bind(setter: v =>
{
if (v) EnsureInputPrefab(processor, prefab);
else RemoveInputPrefab(processor, prefab);
2026-02-03 11:40:26 +09:00
SaveChange(processor, processor.inputs, "inputs");
InitProcessorProperty(processor);
2026-01-16 11:36:54 +09:00
});
group.AddItem(toggle);
}
return group;
}
private static string GetInputPrefabDetailGroupId(string prefabKey)
=> $"InputPrefabDetail.{prefabKey}";
private PropertyGroup CreateInputPrefabDetailGroup(ProcessorDataClass processor, string prefabKey)
{
string groupId = GetInputPrefabDetailGroupId(prefabKey);
var group = new PropertyGroup(groupId, $"{prefabKey} 연결", isExpanded: true);
var entry = GetInputEntry(processor, prefabKey);
if (entry == null)
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
group.AddItem(new StringProperty($"{groupId}.empty", "연결", "투입 개체가 선택되지 않았습니다.")
2025-12-24 17:36:01 +09:00
{
IsReadOnly = true
2026-01-16 11:36:54 +09:00
});
return group;
}
entry.locations ??= new List<Prefab_location>();
string totalId = $"{groupId}.total_count";
var totalProp = new IntProperty(
totalId,
"총 개수",
2026-02-03 11:40:26 +09:00
entry.total_count
2026-01-16 11:36:54 +09:00
)
.Bind(setter: v =>
{
2026-02-03 11:40:26 +09:00
entry.total_count = v;
2026-01-16 11:36:54 +09:00
SaveChange(processor, entry.total_count, $"inputs[{prefabKey}].total_count");
});
group.AddItem(totalProp);
if (entry.locations.Count == 0)
{
group.AddItem(new StringProperty($"{groupId}.no_locs", "연결", "연결된 항목이 없습니다.")
{
IsReadOnly = true
});
return group;
}
foreach (var loc in entry.locations)
{
string locId = $"{groupId}.count.{loc.target}";
var countProp = new IntProperty(
locId,
loc.target,
Mathf.Max(0, loc.count),
isSlider: false,
minValue: 0,
maxValue: 9999
)
.Bind(setter: v =>
{
loc.count = Mathf.Max(0, v);
SaveChange(processor, loc.count, $"inputs[{prefabKey}].locations[target={loc.target}].count");
});
group.AddItem(countProp);
}
return group;
}
private void EnsureInputPrefab(ProcessorDataClass processor, string prefabKey)
{
processor.inputs ??= new List<Prefab_Data>();
if (processor.inputs.Exists(x => x.prefab == prefabKey))
return;
2026-02-03 11:40:26 +09:00
var locations = new List<Prefab_location>();
var workbench = ComponentsManager.webconfigData.property.Processor.workbench.Find(x => x.name == processor.name);
var input = workbench.input_items.Find(x => x.prefab == prefabKey);
if (input != null)
{
foreach (var location in input.locationCounts)
{
var prefablocation = new Prefab_location();
prefablocation.type = location.type;
prefablocation.count = location.count;
prefablocation.target = location.location;
locations.Add(prefablocation);
}
}
2026-01-16 11:36:54 +09:00
processor.inputs.Add(new Prefab_Data
{
prefab = prefabKey,
2026-02-03 11:40:26 +09:00
locations = locations,
2026-01-16 11:36:54 +09:00
total_count = 0
});
}
private void RemoveInputPrefab(ProcessorDataClass processor, string prefabKey)
{
if (processor.inputs == null) return;
int idx = processor.inputs.FindIndex(x => x.prefab == prefabKey);
if (idx >= 0) processor.inputs.RemoveAt(idx);
}
private Prefab_Data GetInputEntry(ProcessorDataClass processor, string prefabKey)
{
if (processor.inputs == null) return null;
return processor.inputs.Find(x => x.prefab == prefabKey);
}
private void SyncInputPrefabDetailVisibility(ProcessorDataClass processor)
{
bool isRandom = string.Equals(processor.input_selection_policy, "random", StringComparison.OrdinalIgnoreCase);
foreach (var prefabKey in PrefabManager.Instance.prefabDict.Keys)
{
bool enabled = processor.inputs != null && processor.inputs.Exists(x => x != null && x.prefab == prefabKey);
string groupId = GetInputPrefabDetailGroupId(prefabKey);
propertyWindow.SetGroupVisibility(groupId, enabled);
if (!enabled)
continue;
var entry = GetInputEntry(processor, prefabKey);
string totalId = $"{groupId}.total_count";
if (propertyWindow.GetProperty(totalId) != null)
propertyWindow.SetPropertyVisibility(totalId, isRandom);
if (entry?.locations != null)
{
foreach (var loc in entry.locations)
{
string locId = $"{groupId}.count.{loc.target}";
if (propertyWindow.GetProperty(locId) != null)
propertyWindow.SetPropertyVisibility(locId, !isRandom);
}
}
}
}
#endregion
#region
private PropertyGroup CreateOutputPrefabToggleGroup(ProcessorDataClass processor)
{
var group = new PropertyGroup("OutputPrefab", "출고 개체", isExpanded: true);
foreach (var prefab in PrefabManager.Instance.prefabDict.Keys)
{
bool enabled = processor.outputs != null && processor.outputs.Exists(x => x.prefab == prefab);
string itemId = $"output_prefab.toggle.{prefab}";
string detailGroupId = GetOutputPrefabDetailGroupId(prefab);
var toggle = new BoolProperty($"output_{itemId}", prefab, enabled)
.Bind(setter: v =>
{
if (v) EnsureOutputPrefab(processor, prefab);
else RemoveOutputPrefab(processor, prefab);
2026-02-03 11:40:26 +09:00
SaveChange(processor, processor.outputs, "outputs");
InitProcessorProperty(processor);
2026-01-16 11:36:54 +09:00
});
group.AddItem(toggle);
}
return group;
}
private static string GetOutputPrefabDetailGroupId(string prefabKey)
=> $"OutputPrefabDetail.{prefabKey}";
private PropertyGroup CreateOutputPrefabDetailGroup(ProcessorDataClass processor, string prefabKey)
{
string groupId = GetOutputPrefabDetailGroupId(prefabKey);
var group = new PropertyGroup(groupId, $"{prefabKey} 연결", isExpanded: true);
2026-02-03 11:40:26 +09:00
var entry = GetOutputEntry(processor, prefabKey);
2026-01-16 11:36:54 +09:00
if (entry == null)
{
group.AddItem(new StringProperty($"{groupId}.empty", "연결", "투입 개체가 선택되지 않았습니다.")
{
IsReadOnly = true
});
return group;
}
entry.locations ??= new List<Prefab_location>();
string totalId = $"{groupId}.total_count";
var totalProp = new IntProperty(
totalId,
"총 개수",
Mathf.Max(0, entry.total_count),
isSlider: false,
minValue: 0,
maxValue: 9999
)
.Bind(setter: v =>
{
entry.total_count = Mathf.Max(0, v);
SaveChange(processor, entry.total_count, $"outputs[{prefabKey}].total_count");
});
group.AddItem(totalProp);
if (entry.locations.Count == 0)
{
group.AddItem(new StringProperty($"{groupId}.no_locs", "연결", "연결된 항목이 없습니다.")
{
IsReadOnly = true
});
return group;
}
foreach (var loc in entry.locations)
{
string locId = $"{groupId}.count.{loc.target}";
var countProp = new IntProperty(
locId,
loc.target,
2026-02-03 11:40:26 +09:00
loc.count
2026-01-16 11:36:54 +09:00
)
.Bind(setter: v =>
{
2026-02-03 11:40:26 +09:00
loc.count = v;
2026-01-16 11:36:54 +09:00
SaveChange(processor, loc.count, $"outputs[{prefabKey}].locations[target={loc.target}].count");
});
group.AddItem(countProp);
}
return group;
}
private void EnsureOutputPrefab(ProcessorDataClass processor, string prefabKey)
{
processor.outputs ??= new List<Prefab_Data>();
if (processor.outputs.Exists(x => x.prefab == prefabKey))
return;
2026-02-03 11:40:26 +09:00
var locations = new List<Prefab_location>();
var workbench = ComponentsManager.webconfigData.property.Processor.workbench.Find(x => x.name == processor.name);
var output = workbench.output_items.Find(x => x.prefab == prefabKey);
if (output != null)
{
foreach (var location in output.locationCounts)
{
var prefablocation = new Prefab_location();
prefablocation.type = location.type;
prefablocation.count = location.count;
prefablocation.target = location.location;
locations.Add(prefablocation);
}
}
2026-01-16 11:36:54 +09:00
processor.outputs.Add(new Prefab_Data
{
prefab = prefabKey,
2026-02-03 11:40:26 +09:00
locations = locations,
2026-01-16 11:36:54 +09:00
total_count = 0
});
}
private void RemoveOutputPrefab(ProcessorDataClass processor, string prefabKey)
{
if (processor.outputs == null) return;
int idx = processor.outputs.FindIndex(x => x.prefab == prefabKey);
if (idx >= 0) processor.outputs.RemoveAt(idx);
}
private Prefab_Data GetOutputEntry(ProcessorDataClass processor, string prefabKey)
{
if (processor.outputs == null) return null;
return processor.outputs.Find(x => x.prefab == prefabKey);
}
private void SyncOutputPrefabDetailVisibility(ProcessorDataClass processor)
{
bool isRandom = string.Equals(processor.output_selection_policy, "random", StringComparison.OrdinalIgnoreCase);
foreach (var prefabKey in PrefabManager.Instance.prefabDict.Keys)
{
bool enabled = processor.outputs != null && processor.outputs.Exists(x => x != null && x.prefab == prefabKey);
string groupId = GetOutputPrefabDetailGroupId(prefabKey);
propertyWindow.SetGroupVisibility(groupId, enabled);
if (!enabled)
continue;
var entry = GetOutputEntry(processor, prefabKey);
string totalId = $"{groupId}.total_count";
if (propertyWindow.GetProperty(totalId) != null)
propertyWindow.SetPropertyVisibility(totalId, isRandom);
if (entry?.locations != null)
{
foreach (var loc in entry.locations)
{
string locId = $"{groupId}.count.{loc.target}";
if (propertyWindow.GetProperty(locId) != null)
propertyWindow.SetPropertyVisibility(locId, !isRandom);
}
}
}
}
#endregion
private PropertyGroup CreateRequireResourceGroup(ProcessorDataClass processor)
{
var group = new PropertyGroup("RequireResource", "자원 할당", isExpanded: true);
// 1) required_resources 인덱스화
var requiredIndex = BuildRequiredIndex(processor);
int count = 0;
foreach (var resource in ComponentsManager.logicDetailData.infrastructure.resources)
{
if (!string.Equals(resource.type, "worker"))
continue;
// 2) 매칭 키 결정: 여기서는 resource.name을 키로 쓴다고 가정
var key = resource.name; // ★ label 말고 name/id 권장
bool isRequired = key != null && requiredIndex.ContainsKey(key);
// 3) 초기값을 isRequired로
var property = new BoolProperty($"required_resource[{count++}]", resource.label, isRequired);
// 4) (선택) 변경 시 processor.required_resources 업데이트 + Patch 저장까지 Bind로 연결
property.Bind(setter: v =>
{
SetRequiredResource(processor, key, v);
// SaveChange(processor or sourceRoot, processorRequiredPath...) 형태로 패치 기록
});
group.AddItem(property);
}
return group;
}
private void SetRequiredResource(ProcessorDataClass processor, string resourceName, bool required)
{
if (string.IsNullOrEmpty(resourceName)) return;
processor.required_resources ??= new List<Required_Resources>();
int idx = processor.required_resources.FindIndex(x => x != null && x.name == resourceName);
if (required)
{
if (idx < 0)
{
processor.required_resources.Add(new Required_Resources
{
name = resourceName,
count = 1,
selection_policy = "random" // 기본값 정책은 규칙에 맞춰 결정
});
}
else
{
// 이미 있으면 유지 (필요 시 count/policy 기본 보정)
}
}
else
{
if (idx >= 0)
processor.required_resources.RemoveAt(idx);
}
}
#region
private PropertyGroup CreateProcessingTimePolicy_Constant(ProcessorDataClass processor)
{
var group = new PropertyGroup("ProcessingTimePolicy_Constant", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ProcessingTimePolicy_Constant_Value", "상수 값", (processor.processing_time_policy as Policy_Constant) ?.value ?? 0f)
2025-12-24 17:36:01 +09:00
{
}.Bind(
2026-01-16 11:36:54 +09:00
setter: v => {(processor.processing_time_policy as Policy_Constant).value = v;SaveChange(processor,v,"processing_time_policy.value"); }
2025-12-24 17:36:01 +09:00
),
2026-01-16 11:36:54 +09:00
});
return group;
}
private PropertyGroup CreateProcessingTimePolicy_Normal(ProcessorDataClass processor)
{
var group = new PropertyGroup("ProcessingTimePolicy_Normal", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ProcessingTimePolicy_Normal_Mean", "정규 분포 표준치", (processor.processing_time_policy as Policy_Normal) ?.mean ?? 0f)
2025-12-24 17:36:01 +09:00
{
}.Bind(
2026-01-16 11:36:54 +09:00
setter: v => {(processor.processing_time_policy as Policy_Normal).mean = v;SaveChange(processor,v,"processing_time_policy.mean"); }
2025-12-24 17:36:01 +09:00
),
2026-01-16 11:36:54 +09:00
new FloatProperty("ProcessingTimePolicy_Normal_Gap", "정규 분포 표준 편차", (processor.processing_time_policy as Policy_Normal) ?.stddev ?? 0f)
2025-12-24 17:36:01 +09:00
{
}.Bind(
2026-01-16 11:36:54 +09:00
setter: v => {(processor.processing_time_policy as Policy_Normal).stddev = v;SaveChange(processor,v,"processing_time_policy.stddev"); }
2025-12-24 17:36:01 +09:00
),
2026-01-16 11:36:54 +09:00
});
return group;
}
2025-12-24 17:36:01 +09:00
2026-01-16 11:36:54 +09:00
private PropertyGroup CreateProcessingTimePolicy_Uniform(ProcessorDataClass processor)
{
var group = new PropertyGroup("ProcessingTimePolicy_Uniform", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ProcessingTimePolicy_Uniform_Min", "균등 분포 최소값", (processor.processing_time_policy as Policy_Uniform) ?.min_val ?? 0f)
{
}.Bind(
setter: v => {(processor.processing_time_policy as Policy_Uniform).min_val = v;SaveChange(processor,v,"processing_time_policy.min_val"); }
),
new FloatProperty("ProcessingTimePolicy_Uniform_Max", "균등 분포 최대값", (processor.processing_time_policy as Policy_Uniform) ?.max_val ?? 0f)
{
}.Bind(
setter: v => {(processor.processing_time_policy as Policy_Uniform).max_val = v;SaveChange(processor,v,"processing_time_policy.max_val"); }
),
});
return group;
}
2025-12-24 17:36:01 +09:00
2026-01-16 11:36:54 +09:00
private PropertyGroup CreateProcessingTimePolicy_Exponential(ProcessorDataClass processor)
{
var group = new PropertyGroup("ProcessingTimePolicy_Exponential", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ProcessingTimePolicy_Exponential_Mean", "지수 분포 평균치", (processor.processing_time_policy as Policy_Exponential) ?.mean ?? 0f)
{
}.Bind(
setter: v => {(processor.processing_time_policy as Policy_Exponential).mean = v;SaveChange(processor,v,"processing_time_policy.mean"); }
),
});
return group;
2025-12-24 17:36:01 +09:00
}
2026-01-16 11:36:54 +09:00
private PropertyGroup CreateProcessingTimePolicy_Triangular(ProcessorDataClass processor)
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
var group = new PropertyGroup("ProcessingTimePolicy_Triangular", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ProcessingTimePolicy_Triangular_Min", "지수 분포 최소값", (processor.processing_time_policy as Policy_Triangular) ?.min_val ?? 0f)
{
}.Bind(
setter: v => {(processor.processing_time_policy as Policy_Triangular).min_val = v;SaveChange(processor,v,"processing_time_policy.min_val"); }
),
new FloatProperty("ProcessingTimePolicy_Triangular_Mean", "지수 분포 최빈값", (processor.processing_time_policy as Policy_Triangular) ?.mode ?? 0f)
{
}.Bind(
setter: v => {(processor.processing_time_policy as Policy_Triangular).mode = v;SaveChange(processor,v,"processing_time_policy.mode"); }
),
new FloatProperty("ProcessingTimePolicy_Triangular_Max", "지수 분포 최대값", (processor.processing_time_policy as Policy_Triangular) ?.max_val ?? 0f)
{
}.Bind(
setter: v => {(processor.processing_time_policy as Policy_Triangular).max_val = v;SaveChange(processor,v,"processing_time_policy.max_val"); }
),
});
return group;
}
2025-12-24 17:36:01 +09:00
2026-01-16 11:36:54 +09:00
private void HandleDisplayModeChanged_ProcessingTimePolicy(string mode)
{
// 모든 조건부 그룹 숨김
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Constant", false);
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Normal", false);
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Uniform", false);
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Exponential", false);
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Triangular", false);
// 선택된 모드에 따라 해당 그룹만 표시
switch (mode)
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
case "Constant":
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Constant", true);
break;
case "Normal":
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Normal", true);
break;
case "Uniform":
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Uniform", true);
break;
case "Exponential":
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Exponential", true);
break;
case "Triangular":
propertyWindow.SetGroupVisibility("ProcessingTimePolicy_Triangular", true);
break;
2025-12-24 17:36:01 +09:00
}
2026-01-16 11:36:54 +09:00
}
#endregion
#region
private PropertyGroup CreateReadyTimePolicy_Constant(ProcessorDataClass processor)
{
var group = new PropertyGroup("ReadyTimePolicy_Constant", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
new FloatProperty("ReadyTimePolicy_Constant_Value", "상수 값", (processor.ready_time_policy as Policy_Constant) ?.value ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Constant).value = v;SaveChange(processor,v,"ready_time_policy.value"); }
),
});
return group;
}
private PropertyGroup CreateReadyTimePolicy_Normal(ProcessorDataClass processor)
{
var group = new PropertyGroup("ReadyTimePolicy_Normal", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ReadyTimePolicy_Normal_Mean", "정규 분포 표준치", (processor.ready_time_policy as Policy_Normal) ?.mean ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Normal).mean = v;SaveChange(processor,v,"ready_time_policy.mean"); }
),
new FloatProperty("ReadyTimePolicy_Normal_Gap", "정규 분포 표준 편차", (processor.ready_time_policy as Policy_Normal) ?.stddev ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Normal).stddev = v;SaveChange(processor,v,"ready_time_policy.stddev"); }
),
});
return group;
}
private PropertyGroup CreateReadyTimePolicy_Uniform(ProcessorDataClass processor)
{
var group = new PropertyGroup("ReadyTimePolicy_Uniform", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ReadyTimePolicy_Uniform_Min", "균등 분포 최소값", (processor.ready_time_policy as Policy_Uniform) ?.min_val ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Uniform).min_val = v;SaveChange(processor,v,"ready_time_policy.min_val"); }
),
new FloatProperty("ReadyTimePolicy_Uniform_Max", "균등 분포 최대값", (processor.ready_time_policy as Policy_Uniform) ?.max_val ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Uniform).max_val = v;SaveChange(processor,v,"ready_time_policy.max_val"); }
),
});
return group;
}
private PropertyGroup CreateReadyTimePolicy_Exponential(ProcessorDataClass processor)
{
var group = new PropertyGroup("ReadyTimePolicy_Exponential", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ReadyTimePolicy_Exponential_Mean", "지수 분포 평균치", (processor.ready_time_policy as Policy_Exponential) ?.mean ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Exponential).mean = v;SaveChange(processor,v,"ready_time_policy.mean"); }
),
});
return group;
}
private PropertyGroup CreateReadyTimePolicy_Triangular(ProcessorDataClass processor)
{
var group = new PropertyGroup("ReadyTimePolicy_Triangular", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("ReadyTimePolicy_Triangular_Min", "지수 분포 최소값", (processor.ready_time_policy as Policy_Triangular) ?.min_val ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Triangular).min_val = v;SaveChange(processor,v,"ready_time_policy.min_val"); }
),
new FloatProperty("ReadyTimePolicy_Triangular_Mean", "지수 분포 최빈값", (processor.ready_time_policy as Policy_Triangular) ?.mode ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Triangular).mode = v;SaveChange(processor,v,"ready_time_policy.mode"); }
),
new FloatProperty("ReadyTimePolicy_Triangular_Max", "지수 분포 최대값", (processor.ready_time_policy as Policy_Triangular) ?.max_val ?? 0f)
{
}.Bind(
setter: v => {(processor.ready_time_policy as Policy_Triangular).max_val = v;SaveChange(processor,v,"ready_time_policy.max_val"); }
),
});
return group;
}
private void HandleDisplayModeChanged_ReadyTimePolicy(string mode)
{
// 모든 조건부 그룹 숨김
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Constant", false);
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Normal", false);
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Uniform", false);
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Exponential", false);
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Triangular", false);
// 선택된 모드에 따라 해당 그룹만 표시
switch (mode)
{
case "Constant":
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Constant", true);
break;
case "Normal":
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Normal", true);
break;
case "Uniform":
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Uniform", true);
break;
case "Exponential":
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Exponential", true);
break;
case "Triangular":
propertyWindow.SetGroupVisibility("ReadyTimePolicy_Triangular", true);
break;
2025-12-24 17:36:01 +09:00
}
2026-01-16 11:36:54 +09:00
}
#endregion
#region
private PropertyGroup CreateDefectRatePolicy_Constant(ProcessorDataClass processor)
{
var group = new PropertyGroup("DefectRatePolicy_Constant", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
new FloatProperty("DefectRatePolicy_Constant_Value", "상수 값", (processor.defect_rate_policy as Policy_Constant) ?.value ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Constant).value = v;SaveChange(processor,v,"defect_rate_policy.value"); }
),
});
return group;
}
private PropertyGroup CreateDefectRatePolicy_Normal(ProcessorDataClass processor)
{
var group = new PropertyGroup("DefectRatePolicy_Normal", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("DefectRatePolicy_Normal_Mean", "정규 분포 표준치", (processor.defect_rate_policy as Policy_Normal) ?.mean ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Normal).mean = v;SaveChange(processor,v,"defect_rate_policy.mean"); }
),
new FloatProperty("DefectRatePolicy_Normal_Gap", "정규 분포 표준 편차", (processor.defect_rate_policy as Policy_Normal) ?.stddev ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Normal).stddev = v;SaveChange(processor,v,"defect_rate_policy.stddev"); }
),
});
return group;
}
private PropertyGroup CreateDefectRatePolicy_Uniform(ProcessorDataClass processor)
{
var group = new PropertyGroup("DefectRatePolicy_Uniform", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("DefectRatePolicy_Uniform_Min", "균등 분포 최소값", (processor.defect_rate_policy as Policy_Uniform) ?.min_val ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Uniform).min_val = v;SaveChange(processor,v,"defect_rate_policy.min_val"); }
),
new FloatProperty("DefectRatePolicy_Uniform_Max", "균등 분포 최대값", (processor.defect_rate_policy as Policy_Uniform) ?.max_val ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Uniform).max_val = v;SaveChange(processor,v,"defect_rate_policy.max_val"); }
),
});
return group;
}
private PropertyGroup CreateDefectRatePolicy_Exponential(ProcessorDataClass processor)
{
var group = new PropertyGroup("DefectRatePolicy_Exponential", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("DefectRatePolicy_Exponential_Mean", "지수 분포 평균치", (processor.defect_rate_policy as Policy_Exponential) ?.mean ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Exponential).mean = v;SaveChange(processor,v,"defect_rate_policy.mean"); }
),
});
return group;
}
private PropertyGroup CreateDefectRatePolicy_Triangular(ProcessorDataClass processor)
{
var group = new PropertyGroup("DefectRatePolicy_Triangular", "", isExpanded: true);
group.AddItems(new IPropertyItem[]
{
new FloatProperty("DefectRatePolicy_Triangular_Min", "지수 분포 최소값", (processor.defect_rate_policy as Policy_Triangular) ?.min_val ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Triangular).min_val = v;SaveChange(processor,v,"defect_rate_policy.min_val"); }
),
new FloatProperty("DefectRatePolicy_Triangular_Mean", "지수 분포 최빈값", (processor.defect_rate_policy as Policy_Triangular) ?.mode ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Triangular).mode = v;SaveChange(processor,v,"defect_rate_policy.mode"); }
),
new FloatProperty("DefectRatePolicy_Triangular_Max", "지수 분포 최대값", (processor.defect_rate_policy as Policy_Triangular) ?.max_val ?? 0f)
{
}.Bind(
setter: v => {(processor.defect_rate_policy as Policy_Triangular).max_val = v;SaveChange(processor,v,"defect_rate_policy.max_val"); }
),
});
return group;
}
private void HandleDisplayModeChanged_DefectRatePolicy(string mode)
{
// 모든 조건부 그룹 숨김
propertyWindow.SetGroupVisibility("DefectRatePolicy_Constant", false);
propertyWindow.SetGroupVisibility("DefectRatePolicy_Normal", false);
propertyWindow.SetGroupVisibility("DefectRatePolicy_Uniform", false);
propertyWindow.SetGroupVisibility("DefectRatePolicy_Exponential", false);
propertyWindow.SetGroupVisibility("DefectRatePolicy_Triangular", false);
// 선택된 모드에 따라 해당 그룹만 표시
switch (mode)
{
case "Constant":
propertyWindow.SetGroupVisibility("DefectRatePolicy_Constant", true);
break;
case "Normal":
propertyWindow.SetGroupVisibility("DefectRatePolicy_Normal", true);
break;
case "Uniform":
propertyWindow.SetGroupVisibility("DefectRatePolicy_Uniform", true);
break;
case "Exponential":
propertyWindow.SetGroupVisibility("DefectRatePolicy_Exponential", true);
break;
case "Triangular":
propertyWindow.SetGroupVisibility("DefectRatePolicy_Triangular", true);
break;
2025-12-24 17:36:01 +09:00
}
}
2026-01-16 11:36:54 +09:00
#endregion
2025-12-24 17:36:01 +09:00
2026-01-16 11:36:54 +09:00
private Dictionary<string, Required_Resources> BuildRequiredIndex(ProcessorDataClass processor)
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
var dict = new Dictionary<string, Required_Resources>(StringComparer.Ordinal);
if (processor.required_resources == null) return dict;
foreach (var rr in processor.required_resources)
2025-12-24 17:36:01 +09:00
{
2026-01-16 11:36:54 +09:00
if (rr?.name == null) continue;
dict[rr.name] = rr;
2025-12-24 17:36:01 +09:00
}
2026-01-16 11:36:54 +09:00
return dict;
2025-12-24 17:36:01 +09:00
}
2026-02-25 16:30:12 +09:00
private void OnDestroy()
{
if (propertyWindow != null)
propertyWindow.PropertyValueChanged -= OnPropertyValueChanged;
}
2026-01-16 11:36:54 +09:00
}