using Simulator.Data; using System; using System.Collections.Generic; using UnityEngine; using UVC.UI.Window.PropertyWindow; public class ProcessorProperty : MonoBehaviour { [SerializeField] private PropertyWindow propertyWindow; ProcessorDataClass processor; private void Awake() { propertyWindow.PropertyValueChanged += OnPropertyValueChanged; } public void SetPropertyWindow(ComponentType type, ComponentDataBase data) { if (PlayerPropertyDataBase.isPlaying) { return; } switch (type) { case ComponentType.Processor: processor = data as ProcessorDataClass; InitProcessorProperty(data as ProcessorDataClass); 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); } public void InitProcessorProperty(ProcessorDataClass processor) { var entries = new List(); 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"); }) ); entries.Add(CreatePositionGroup(processor)); entries.Add( new ListProperty("model_type", "모델 타입", new List() { "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"); }) ); entries.Add( new ListProperty("processing_type", "처리 유형", new List() { "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() { "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() { "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 => { processor.processing_time_policy = PolicyFactory.Create(v.ToString()); SaveChange(processor, PolicyFactory.Create(v.ToString()), "processing_time_policy"); })); 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)); entries.Add(BuildOutputTagChanges()); entries.Add(new ListProperty("outputTagType_string", "", new List { "set" }, "set")); entries.Add(new StringProperty("outputTagValue_string", "", "")); entries.Add(new ListProperty("outputTagType_number", "", new List { "set", "increment", "decrement" }, "값 설정")); entries.Add(new IntProperty("outputTagValue_number", "", 0)); entries.Add(new ButtonProperty("AddTagChangeButton", "태그 변경 추가", "").BindClick(SaveOutputTag)); entries.Add(CreateOutputTags(processor)); entries.Add(new EnumProperty("display_mode_ReadyTimePolicy", "준비 시간 정책", processor.ready_time_policy.policy).Bind( setter: v => { processor.ready_time_policy = PolicyFactory.Create(v.ToString()); SaveChange(processor, PolicyFactory.Create(v.ToString()), "ready_time_policy"); })); 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( setter: v => { processor.defect_rate_policy = PolicyFactory.Create(v.ToString()); SaveChange(processor, PolicyFactory.Create(v.ToString()), "defect_rate_policy"); })); 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); SyncOutputTagType(ComponentsManager.webconfigData.tags[0].type); } 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); } } 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( setter: v => {processor.physical.orientation = v;SaveChange(processor,v,"physical.orientation");propertyWindow.ApplyExternalValue("orientation",RotationSnap.SnapOrientation(v),false); } ), }); return group; } #region public ListProperty BuildOutputTagChanges() { List tagKeyList = new List(); 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 #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); SaveChange(processor, processor.inputs, "inputs"); InitProcessorProperty(processor); }); 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) { group.AddItem(new StringProperty($"{groupId}.empty", "연결", "투입 개체가 선택되지 않았습니다.") { IsReadOnly = true }); return group; } entry.locations ??= new List(); string totalId = $"{groupId}.total_count"; var totalProp = new IntProperty( totalId, "총 개수", entry.total_count ) .Bind(setter: v => { entry.total_count = v; 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(); if (processor.inputs.Exists(x => x.prefab == prefabKey)) return; var locations = new List(); 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); } } processor.inputs.Add(new Prefab_Data { prefab = prefabKey, locations = locations, 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); SaveChange(processor, processor.outputs, "outputs"); InitProcessorProperty(processor); }); 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); var entry = GetOutputEntry(processor, prefabKey); if (entry == null) { group.AddItem(new StringProperty($"{groupId}.empty", "연결", "투입 개체가 선택되지 않았습니다.") { IsReadOnly = true }); return group; } entry.locations ??= new List(); 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, loc.count ) .Bind(setter: v => { loc.count = v; 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(); if (processor.outputs.Exists(x => x.prefab == prefabKey)) return; var locations = new List(); 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); } } processor.outputs.Add(new Prefab_Data { prefab = prefabKey, locations = locations, 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(); 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) { }.Bind( setter: v => {(processor.processing_time_policy as Policy_Constant).value = v;SaveChange(processor,v,"processing_time_policy.value"); } ), }); 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) { }.Bind( setter: v => {(processor.processing_time_policy as Policy_Normal).mean = v;SaveChange(processor,v,"processing_time_policy.mean"); } ), new FloatProperty("ProcessingTimePolicy_Normal_Gap", "정규 분포 표준 편차", (processor.processing_time_policy as Policy_Normal) ?.stddev ?? 0f) { }.Bind( setter: v => {(processor.processing_time_policy as Policy_Normal).stddev = v;SaveChange(processor,v,"processing_time_policy.stddev"); } ), }); return group; } 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; } 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; } private PropertyGroup CreateProcessingTimePolicy_Triangular(ProcessorDataClass processor) { 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; } 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) { 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; } } #endregion #region 준비 시간 정책 private PropertyGroup CreateReadyTimePolicy_Constant(ProcessorDataClass processor) { var group = new PropertyGroup("ReadyTimePolicy_Constant", "", isExpanded: true); group.AddItems(new IPropertyItem[] { 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; } } #endregion #region 불량률 정책 private PropertyGroup CreateDefectRatePolicy_Constant(ProcessorDataClass processor) { var group = new PropertyGroup("DefectRatePolicy_Constant", "", isExpanded: true); group.AddItems(new IPropertyItem[] { 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; } } #endregion private Dictionary BuildRequiredIndex(ProcessorDataClass processor) { var dict = new Dictionary(StringComparer.Ordinal); if (processor.required_resources == null) return dict; foreach (var rr in processor.required_resources) { if (rr?.name == null) continue; dict[rr.name] = rr; } return dict; } private void OnDestroy() { if (propertyWindow != null) propertyWindow.PropertyValueChanged -= OnPropertyValueChanged; } }