/// <summary> /// Resumes a paused experiment by re-enabling the Client state change listener and /// starting a new trial.If the experiment is uninitialized, it will initialize it. /// /// </summary> public void Resume() { // Skip initialization if the experiment has already started isPaused = false; if (hasStarted) { client.onStatusChanged += OnStatusChanged; if (config != null) { AEPsychClient.Log("Resuming prior trial: " + config); SetState(ExperimentState.ConfigReady); if (!client.finished) { ShowStimuli(config); } } else { StartCoroutine(AskForNewConfig(strategy)); } } else { StartCoroutine(ConnectAndGenerateStrategies()); } }
public virtual void PauseExperiment() { AEPsychClient.Log("Pausing " + name); isPaused = true; Disconnect(); gameObject.SetActive(false); }
// ______________________________ Section 5 ________________________________ // // Unity Built-in Methods // _________________________________________________________________________ #region private void Awake() { // Find Query Model Canvas Group, if model querying is enabled in Inspector if (useModelExploration) { queryModel = FindObjectOfType <QueryModel>(true); if (queryModel == null) { ModelExplorerPrefab = AssetDatabase.LoadAssetAtPath <GameObject>(modelExplorerPath); if (ModelExplorerPrefab != null) { queryModel = Instantiate(ModelExplorerPrefab).GetComponent <QueryModel>(); } else { Debug.LogError(string.Format("Prefab not found at: {0}. Please Manually add the ModelExploreInterface prefab to use model exploration.", modelExplorerPath)); } Debug.Log("No Query Model game object detected in scene. Loading default ModelExploreInterface Prefab."); } // Add Event System if there isn't one already if (FindObjectOfType <EventSystem>() == null) { new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule)); } } // Find client within Unity scene client = FindObjectOfType <AEPsychClient>(); if (client == null) { Debug.LogError("AEPsych Client is missing. Please add the AEPsych " + "Client to the scene and ensure that it is enabled."); TerminateExperiment(); } configGenerator = GetComponent <ConfigGenerator>(); if (configGenerator == null) { Debug.LogError("Experiement script running without a ConfigGenerator attached. " + "Please attach a ConfigGenerator component to " + GetName()); TerminateExperiment(); } if (configGenerator.isAutomatic) { //AEPsychClient.Log("Automatic Config Generated:\n" + PlayerPrefs.GetString(configGenerator.experimentName + "config")); AEPsychClient.Log("Automatic Config Generated:\n" + configGenerator.GetConfigText()); } if (useDefaultUI) { defaultUI = FindObjectOfType <DefaultUI>(); if (defaultUI == null) { Debug.LogError("Using default UI, but DefaultUI not found in scene. " + "Please add the DefaultUI component to your scene or uncheck \"Use " + "Default UI\" for your Experiment."); TerminateExperiment(); } } }
void Disconnect() { AEPsychClient.Log("Disconnecting " + GetName()); StopAllCoroutines(); if (client) { client.onStatusChanged -= OnStatusChanged; } SetState(ExperimentState.NotConnected); }
// ______________________________ Section 2 ________________________________ // // Virtual Methods that you may want to override in child class // _________________________________________________________________________ #region // SetText (optional) // Helper function to increase default visibility of trial text public virtual void SetText(string text) { if (useDefaultUI) { defaultUI.SetText(text); } else { AEPsychClient.Log(text); } }
/// <summary> /// Starts experiment by beginning the first sample request /// </summary> public virtual void BeginExperiment() { AEPsychClient.Log("Starting Experiment: Strategy " + strategy.stratId); hasStarted = true; if (strategy != null) { StartCoroutine(AskForNewConfig(strategy)); } else { Debug.LogError("Manual config selected, but config file field is empty. " + "To use manual config files, assign one in the AEPsychClient inspector " + "window. To use automatic Configs, toggle \"Use Automatic Config " + "Generation\" in the AEPsychClient inspector window."); TerminateExperiment(); } }
/// <summary> /// Called when user is ready for the next trial. Queries AEPsych server for the optimal test case. /// </summary> /// <param name="strategyId"></param> IEnumerator AskForNewConfig(AEPsychStrategy strat) { // Initialize strat if we haven't yet if (strat.stratId == -1) { yield return(StartCoroutine(strat.InitStrat(client, configPath: configPath, true))); } // Resume this strat if AEPsych Client was previously handling a different one if (client.currentStrat != strat.stratId) { SetState(ExperimentState.WaitingForResumeResponse); yield return(StartCoroutine(client.Resume(strat.stratId))); } strategy.currentTrial++; AEPsychClient.Log("strat " + strategy.stratId + " trial# " + strat.currentTrial); SetState(ExperimentState.WaitingForAskResponse); yield return(StartCoroutine(client.Ask())); }
/// <summary> /// Called when user makes a judgement and selects a response. Updates the AEPsych model with a new data point. /// </summary> public void ReportResultToServer(int outcome, TrialMetadata metadata = null) { if (recordToCSV) { TrialData trial = new TrialData(DateTime.Now.ToString("hh:mm:ss"), config, outcome, metadata); csvFile.WriteTrial(trial); } AEPsychClient.Log(string.Format("ReportResult: {0}", outcome)); SetState(ExperimentState.WaitingForTellResponse); if (metadata != null) { StartCoroutine(client.Tell(config, outcome, metadata)); } else { StartCoroutine(client.Tell(config, outcome)); } if (useModelExploration) { queryModel.QueryEnabled(false); } }
/// <summary> /// Callback for AEPsych Client Status Changes. This syncs the Experiment class with the AEPsychClient class and manages state transitions. /// // Expected order of transitions: /// NotConnected => occurs before the experiment begins. /// WaitingForResumeResponse => occurs after sending Resume() query to server. /// WaitingForAsk => occurs when resume confirmation is received. /// WaitingForAskResponse => occurs while client awaits AEPsych server's test case selection /// ConfigReady => occurs when suggested test case is received from server /// WaitingForTell => occurs while waiting for the user to respond to stimulus /// WaitingForTellResponse => occurs as soon as user response is sent to server /// WaitingForAsk => ... /// </summary> /// <param name="oldStatus"></param> /// <param name="newStatus"></param> void OnStatusChanged(AEPsychClient.ClientStatus oldStatus, AEPsychClient.ClientStatus newStatus) { AEPsychClient.Log(string.Format("OnStatusChanged Callback. Experiment status: {0}, " + "old client: {1}, new client: {2}", _experimentState, oldStatus, newStatus)); if (_experimentState == ExperimentState.NotConnected) { if (newStatus == AEPsychClient.ClientStatus.GotResponse) { OnConnectToServer(); } } else if (_experimentState == ExperimentState.Exploring) { if (newStatus == AEPsychClient.ClientStatus.QuerySent) { SetState(ExperimentState.WaitingForQueryResponse); } } else if (_experimentState == ExperimentState.WaitingForTellResponse) { if (newStatus == AEPsychClient.ClientStatus.GotResponse) { if (useModelExploration && !readyToQuery) { // Check if the model is built and ready for queries StartCoroutine(CheckQueryReady()); } else { StartCoroutine(EndTrial()); } } } else if (_experimentState == ExperimentState.WaitingForResumeResponse) { if (newStatus == AEPsychClient.ClientStatus.GotResponse) { SetState(ExperimentState.WaitingForAsk); } } else if (_experimentState == ExperimentState.WaitingForAskResponse) { if (newStatus == AEPsychClient.ClientStatus.GotResponse) { // We recieved a new config. Store it. config = client.GetConfig(); SetState(ExperimentState.ConfigReady); if (!client.finished) { ShowStimuli(config); } else { isDone = true; StartCoroutine(EndTrial()); } } } else if (_experimentState == ExperimentState.ConfigReady) { if (newStatus == AEPsychClient.ClientStatus.Ready) { SetState(ExperimentState.WaitingForTell); // Enable or Disable Model Querying based on client status CheckUserResponse(); // Should call ReportResultToServer() // when response is collected } } else if (_experimentState == ExperimentState.WaitingForAsk) { if (newStatus == AEPsychClient.ClientStatus.QuerySent) { SetState(ExperimentState.WaitingForAskResponse); } } else if (_experimentState == ExperimentState.WaitingForTell) { if (newStatus == AEPsychClient.ClientStatus.QuerySent) { SetState(ExperimentState.WaitingForTellResponse); } } else if (_experimentState == ExperimentState.WaitingForQueryResponse) { if (newStatus == AEPsychClient.ClientStatus.GotResponse) { //QueryMessage m = client.GetQueryResponse(); //ReceiveExplorationQuery(m.x); SetState(ExperimentState.Exploring); } } else if (_experimentState == ExperimentState.WaitingForCanModelResponse) { if (newStatus == AEPsychClient.ClientStatus.GotResponse) { readyToQuery = client.GetModelResponse(); StartCoroutine(EndTrial()); } } }