using TMPro; using UnityEngine; using UnityEngine.UI; using VMdemo.Core; using System; namespace VMdemo.UI { public class SimulationConfigInputView : MonoBehaviour { [Header("Fixed Core Spec")] [SerializeField] private TMP_Text fixedCoreSpecText; [SerializeField] private TMP_InputField accessCountInput; [SerializeField] private TMP_InputField pageFaultPenaltyInput; [SerializeField] private TMP_Dropdown addressModeDropdown; [SerializeField] private TMP_InputField arrayLengthBytesInput; [SerializeField] private TMP_InputField arrayElementBytesInput; [SerializeField] private TMP_InputField arrayBaseAddressInput; [SerializeField] private Button saveButton; [SerializeField] private Button cancelButton; [SerializeField] private GameObject panelRoot; public event Action SaveClicked; public event Action CancelClicked; public bool IsPanelVisible => panelRoot != null ? panelRoot.activeInHierarchy : gameObject.activeInHierarchy; private void Awake() { BindButtons(); ApplyFixedCoreSpecVisual(); } public bool TryBuildConfig(out SimulationConfig config, out string errorMessage) { config = new SimulationConfig { machineBits = SimulationConfig.FixedMachineBits, vaBits = SimulationConfig.FixedVaBits, pageSizeKB = SimulationConfig.FixedPageSizeKB, physicalMemoryMB = SimulationConfig.FixedPhysicalMemoryMB, tlbEntries = SimulationConfig.FixedTlbEntries, accessCount = ParseInt(accessCountInput, 100), pageFaultPenalty = ParseInt(pageFaultPenaltyInput, 100), addressGenerationMode = ReadAddressGenerationMode(), arrayLengthBytes = ParseInt(arrayLengthBytesInput, 256), arrayElementBytes = ParseInt(arrayElementBytesInput, 4), arrayBaseAddress = ParseInt(arrayBaseAddressInput, 0) }; config.ApplyFixedCoreSpec(); return ConfigValidator.TryValidate(config, out errorMessage); } public void PopulateFromConfig(SimulationConfig config) { if (config == null) { return; } SetInputText(accessCountInput, config.accessCount); SetInputText(pageFaultPenaltyInput, config.pageFaultPenalty); SetAddressGenerationMode(config.addressGenerationMode); SetInputText(arrayLengthBytesInput, config.arrayLengthBytes); SetInputText(arrayElementBytesInput, config.arrayElementBytes); SetInputText(arrayBaseAddressInput, config.arrayBaseAddress); ApplyFixedCoreSpecVisual(); } public void SetPanelVisible(bool visible) { if (panelRoot != null) { panelRoot.SetActive(visible); if (visible) { ApplyFixedCoreSpecVisual(); } return; } gameObject.SetActive(visible); if (visible) { ApplyFixedCoreSpecVisual(); } } private void BindButtons() { if (saveButton != null) { saveButton.onClick.RemoveAllListeners(); saveButton.onClick.AddListener(() => SaveClicked?.Invoke()); } if (cancelButton != null) { cancelButton.onClick.RemoveAllListeners(); cancelButton.onClick.AddListener(() => CancelClicked?.Invoke()); } } private void ApplyFixedCoreSpecVisual() { if (fixedCoreSpecText != null) { fixedCoreSpecText.text = $"{SimulationConfig.FixedMachineBits}位机\n" + $"VA {SimulationConfig.FixedVaBits}位\n" + $"页大小 {SimulationConfig.FixedPageSizeKB}KB\n" + $"物理内存 {SimulationConfig.FixedPhysicalMemoryMB}MB\n" + $"TLB {SimulationConfig.FixedTlbEntries}条全相联"; } } private static void SetInputText(TMP_InputField input, int value) { if (input != null) { input.text = value.ToString(); } } private static void SetInteractable(Selectable selectable, bool interactable) { if (selectable != null) { selectable.interactable = interactable; } } private AddressGenerationMode ReadAddressGenerationMode() { if (addressModeDropdown == null || addressModeDropdown.options == null || addressModeDropdown.options.Count == 0) { return AddressGenerationMode.SequentialArrayLoop; } // Preferred convention: // index 0 = RandomUniform, index 1 = SequentialArrayLoop. if (addressModeDropdown.options.Count >= 2) { return addressModeDropdown.value == 0 ? AddressGenerationMode.RandomUniform : AddressGenerationMode.SequentialArrayLoop; } var option = addressModeDropdown.options[addressModeDropdown.value].text; if (IsRandomOption(option)) { return AddressGenerationMode.RandomUniform; } if (IsSequentialOption(option)) { return AddressGenerationMode.SequentialArrayLoop; } return AddressGenerationMode.SequentialArrayLoop; } private void SetAddressGenerationMode(AddressGenerationMode mode) { if (addressModeDropdown == null || addressModeDropdown.options == null || addressModeDropdown.options.Count == 0) { return; } if (addressModeDropdown.options.Count >= 2) { addressModeDropdown.value = mode == AddressGenerationMode.RandomUniform ? 0 : 1; addressModeDropdown.RefreshShownValue(); return; } for (var i = 0; i < addressModeDropdown.options.Count; i++) { var option = addressModeDropdown.options[i].text; if ((mode == AddressGenerationMode.RandomUniform && IsRandomOption(option)) || (mode == AddressGenerationMode.SequentialArrayLoop && IsSequentialOption(option))) { addressModeDropdown.value = i; addressModeDropdown.RefreshShownValue(); return; } } } private static bool IsRandomOption(string optionText) { if (string.IsNullOrEmpty(optionText)) { return false; } return optionText.Contains("Random") || optionText.Contains("随机") || optionText.Contains("rand"); } private static bool IsSequentialOption(string optionText) { if (string.IsNullOrEmpty(optionText)) { return false; } return optionText.Contains("Sequential") || optionText.Contains("ArrayLoop") || optionText.Contains("数组") || optionText.Contains("顺序") || optionText.Contains("for"); } private static int ParseInt(TMP_InputField input, int fallback) { if (input == null) { return fallback; } return int.TryParse(input.text, out var value) ? value : fallback; } } }