diff --git a/Assets/Scripts/Log/OutputLog.cs b/Assets/Scripts/Log/OutputLog.cs index ca433b03..ef26a936 100644 --- a/Assets/Scripts/Log/OutputLog.cs +++ b/Assets/Scripts/Log/OutputLog.cs @@ -25,6 +25,12 @@ public class OutputLog : MonoBehaviour private const int MAX_LOG_LINES = 60; private bool _warned; +#if !UNITY_WEBGL + private string logPath = + Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/Pinpoint"; + private DateTime logStartTime = DateTime.Now; +#endif + private void Awake() { if (Instance != null) @@ -35,6 +41,10 @@ private void Awake() #if UNITY_EDITOR _lastLogTime = float.MinValue; +#endif +#if !UNITY_WEBGL + // Create log directory if it doesn't exist. + System.IO.Directory.CreateDirectory(logPath); #endif } @@ -52,9 +62,20 @@ public static void Log(IEnumerable data) } Instance._lastLogTime = Time.realtimeSinceStartup; #endif + var logLine = string.Join(',', data); - Instance._log.Add(string.Join(',', data)); + Instance._log.Add(logLine); Instance.UpdateLogText(); + +#if !UNITY_WEBGL + // Log to file. + var fileName = + Instance.logPath + + "/log_" + + Instance.logStartTime.ToString("yyyy-MM-dd_HH-mm-ss") + + ".csv"; + System.IO.File.AppendAllText(fileName, logLine + "\n"); +#endif } public void UpdateLogText() diff --git a/Assets/Scripts/Pinpoint/Probes/ManipulatorBehaviorController.cs b/Assets/Scripts/Pinpoint/Probes/ManipulatorBehaviorController.cs index c7cc7525..37da33d6 100644 --- a/Assets/Scripts/Pinpoint/Probes/ManipulatorBehaviorController.cs +++ b/Assets/Scripts/Pinpoint/Probes/ManipulatorBehaviorController.cs @@ -99,6 +99,7 @@ public bool IsRightHanded #region Private internal fields private Vector4 _lastManipulatorPosition = Vector4.zero; + private Vector4 _lastLoggedManipulatorPosition = Vector4.zero; private Vector4 _zeroCoordinateOffset = Vector4.zero; private float _brainSurfaceOffset; private bool _isSetToDropToSurfaceWithDepth = true; @@ -542,30 +543,50 @@ private void EchoPosition(Vector4 pos) void LogAndContinue() { - // Log every 5 hz - if (Time.time - _lastLoggedTime >= 0.2) + // Don't log if the last position is the same. + var positionDifference = _lastLoggedManipulatorPosition - pos; + if ( + Mathf.Abs(positionDifference.x) > 0.0001 + || Mathf.Abs(positionDifference.y) > 0.0001 + || Mathf.Abs(positionDifference.z) > 0.0001 + || Mathf.Abs(positionDifference.w) > 0.0001 + ) { - _lastLoggedTime = Time.time; - var tipPos = _probeController.ProbeTipT.position; - - // ["ephys_link", Real time stamp, Manipulator ID, X, Y, Z, W, Phi, Theta, Spin, TipX, TipY, TipZ] - string[] data = + // Log every 4 hz + if (Time.time - _lastLoggedTime >= 0.25) { - "ephys_link", - Time.realtimeSinceStartup.ToString(CultureInfo.InvariantCulture), - ManipulatorID, - pos.x.ToString(CultureInfo.InvariantCulture), - pos.y.ToString(CultureInfo.InvariantCulture), - pos.z.ToString(CultureInfo.InvariantCulture), - pos.w.ToString(CultureInfo.InvariantCulture), - _probeController.Insertion.Yaw.ToString(CultureInfo.InvariantCulture), - _probeController.Insertion.Pitch.ToString(CultureInfo.InvariantCulture), - _probeController.Insertion.Roll.ToString(CultureInfo.InvariantCulture), - tipPos.x.ToString(CultureInfo.InvariantCulture), - tipPos.y.ToString(CultureInfo.InvariantCulture), - tipPos.z.ToString(CultureInfo.InvariantCulture) - }; - OutputLog.Log(data); + _lastLoggedTime = Time.time; + var tipPos = _probeController.ProbeTipT.position; + + // ["ephys_link", Real time stamp, Manipulator ID, X, Y, Z, W, Phi, Theta, Spin, TipX, TipY, TipZ] + OutputLog.Log( + new[] + { + "ephys_link", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + ManipulatorID, + pos.x.ToString(CultureInfo.InvariantCulture), + pos.y.ToString(CultureInfo.InvariantCulture), + pos.z.ToString(CultureInfo.InvariantCulture), + pos.w.ToString(CultureInfo.InvariantCulture), + _probeController.Insertion.Yaw.ToString( + CultureInfo.InvariantCulture + ), + _probeController.Insertion.Pitch.ToString( + CultureInfo.InvariantCulture + ), + _probeController.Insertion.Roll.ToString( + CultureInfo.InvariantCulture + ), + tipPos.x.ToString(CultureInfo.InvariantCulture), + tipPos.y.ToString(CultureInfo.InvariantCulture), + tipPos.z.ToString(CultureInfo.InvariantCulture) + } + ); + + // Update last logged position + _lastLoggedManipulatorPosition = pos; + } } // Continue echoing position diff --git a/Assets/Scripts/Pinpoint/UI/EphysCopilot/DrivePanelHandler.cs b/Assets/Scripts/Pinpoint/UI/EphysCopilot/DrivePanelHandler.cs index 7ebf7358..7d2fb14f 100644 --- a/Assets/Scripts/Pinpoint/UI/EphysCopilot/DrivePanelHandler.cs +++ b/Assets/Scripts/Pinpoint/UI/EphysCopilot/DrivePanelHandler.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Globalization; using BrainAtlas; using EphysLink; using TMPro; @@ -466,6 +468,18 @@ public void Drive() switch (_driveStateManager.State) { case DriveState.DrivingToNearTarget: + // Log start of drive. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Driving to Near Target @ " + _driveBaseSpeed, + } + ); + // Update status text _statusText.text = "Driving to " @@ -492,6 +506,18 @@ public void Drive() CompleteAndAdvance(); break; case DriveState.DrivingToPastTarget: + // Log driving to past target. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Driving to Past Target: " + _pastTargetDepth, + } + ); + // Update status text _statusText.text = "Driving to " @@ -518,6 +544,18 @@ public void Drive() CompleteAndAdvance(); break; case DriveState.ReturningToTarget: + // Log returning to target. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Returning to Target: " + _targetDepth, + } + ); + // Update status text _statusText.text = "Returning to target..."; @@ -532,7 +570,7 @@ public void Drive() _targetDepth, _nearTargetDriveSpeed ), - _ => + finalDepth => { _driveStateManager.CompleteMovement(); @@ -544,6 +582,18 @@ public void Drive() _driveGroup.SetActive(true); _driveButton.interactable = false; _exitButton.SetActive(true); + + // Log end of drive. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "End Drive: " + finalDepth, + } + ); }, Debug.LogError ); @@ -590,6 +640,18 @@ public void Exit() switch (_driveStateManager.State) { case DriveState.ExitingToNearTarget: + // Log start of exit. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Exiting to Near Target: " + _nearTargetDepth, + } + ); + // Update status text _statusText.text = "Returning to surface..."; @@ -613,6 +675,18 @@ public void Exit() CompleteAndAdvance(); break; case DriveState.ExitingToDura: + // Log exiting to dura. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Exiting to Dura: " + _duraDepth, + } + ); + // Update status text _statusText.text = "Returning to surface..."; @@ -638,6 +712,18 @@ public void Exit() CompleteAndAdvance(); break; case DriveState.ExitingToMargin: + // Log exiting to margin. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Exiting to Margin: " + _exitMarginDepth, + } + ); + // Update status text _statusText.text = "Exiting Dura..."; @@ -663,6 +749,18 @@ public void Exit() CompleteAndAdvance(); break; case DriveState.ExitingToOutside: + // Log exiting to outside. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Start Exiting to Outside", + } + ); + // Update status text _statusText.text = "Exiting Dura..."; @@ -734,6 +832,18 @@ void CompleteOutside() _stopButton.SetActive(false); _exitButton.SetActive(false); _driveGroup.SetActive(true); + + // Log end of exit. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "End Exiting to Outside", + } + ); } } @@ -746,6 +856,18 @@ public void Stop() _manipulatorId, () => { + // Log stop event. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "Drive", + _manipulatorId, + "Stop", + } + ); + // Show drive group and hide stop button. _statusText.text = "Stopped"; _driveGroup.SetActive(true); diff --git a/Assets/Scripts/Pinpoint/UI/EphysCopilot/InsertionSelectionPanelHandler.cs b/Assets/Scripts/Pinpoint/UI/EphysCopilot/InsertionSelectionPanelHandler.cs index 606b0d5e..76bfeaf4 100644 --- a/Assets/Scripts/Pinpoint/UI/EphysCopilot/InsertionSelectionPanelHandler.cs +++ b/Assets/Scripts/Pinpoint/UI/EphysCopilot/InsertionSelectionPanelHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using BrainAtlas; using EphysLink; @@ -216,6 +217,19 @@ public void MoveOrStopProbeToInsertionTarget() if (_isMoving) // Movement in progress -> should stop movement { + // Log that movement is stopping. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "MoveToTargetInsertion", + ProbeManager.ManipulatorBehaviorController.ManipulatorID, + "Stop" + } + ); + + // Stop movement. CommunicationManager.Instance.Stop( ProbeManager.ManipulatorBehaviorController.ManipulatorID, () => @@ -228,6 +242,19 @@ public void MoveOrStopProbeToInsertionTarget() } else { + // Log that movement is starting. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "MoveToTargetInsertion", + ProbeManager.ManipulatorBehaviorController.ManipulatorID, + "Start" + } + ); + + // Make movement. MoveToTargetInsertion(); _moveButtonText.text = STOP_MOVEMENT_STR; } @@ -511,6 +538,20 @@ private void MoveToTargetInsertion() // Complete movement EndMovement(); _moveButton.interactable = false; + + // Log movement finish. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "MoveToTargetInsertion", + ProbeManager + .ManipulatorBehaviorController + .ManipulatorID, + "Finish" + } + ); }, error => { diff --git a/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetDuraOffsetPanelHandler.cs b/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetDuraOffsetPanelHandler.cs index 3f1b4791..d23ff5ef 100644 --- a/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetDuraOffsetPanelHandler.cs +++ b/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetDuraOffsetPanelHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using EphysLink; using TMPro; using UnityEngine; @@ -10,7 +11,8 @@ public class ResetDuraOffsetPanelHandler : MonoBehaviour { #region Components - [SerializeField] private TMP_Text _manipulatorIDText; + [SerializeField] + private TMP_Text _manipulatorIDText; public ProbeManager ProbeManager { private get; set; } #endregion @@ -19,16 +21,17 @@ public class ResetDuraOffsetPanelHandler : MonoBehaviour public static readonly Dictionary ManipulatorIdToDuraDepth = new(); public static readonly Dictionary ManipulatorIdToDuraApmldv = new(); - + public Action ResetDriveStateToDura { private get; set; } #endregion - + #region Unity private void Start() { - _manipulatorIDText.text = "Manipulator " + ProbeManager.ManipulatorBehaviorController.ManipulatorID; + _manipulatorIDText.text = + "Manipulator " + ProbeManager.ManipulatorBehaviorController.ManipulatorID; _manipulatorIDText.color = ProbeManager.Color; } @@ -44,16 +47,35 @@ public void ResetDuraOffset() // Reset dura offset ProbeManager.ManipulatorBehaviorController.ComputeBrainSurfaceOffset(); - CommunicationManager.Instance.GetPosition(ProbeManager.ManipulatorBehaviorController.ManipulatorID, + CommunicationManager.Instance.GetPosition( + ProbeManager.ManipulatorBehaviorController.ManipulatorID, pos => { - ManipulatorIdToDuraDepth[ProbeManager.ManipulatorBehaviorController.ManipulatorID] = pos.w; - ManipulatorIdToDuraApmldv[ProbeManager.ManipulatorBehaviorController.ManipulatorID] = - ProbeManager.ProbeController.Insertion.APMLDV; + ManipulatorIdToDuraDepth[ + ProbeManager.ManipulatorBehaviorController.ManipulatorID + ] = pos.w; + ManipulatorIdToDuraApmldv[ + ProbeManager.ManipulatorBehaviorController.ManipulatorID + ] = ProbeManager.ProbeController.Insertion.APMLDV; ResetDriveStateToDura.Invoke(); - }); + } + ); + + // Log event. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "ResetDuraOffset", + ProbeManager.ManipulatorBehaviorController.ManipulatorID, + ProbeManager.ManipulatorBehaviorController.BrainSurfaceOffset.ToString( + CultureInfo.InvariantCulture + ) + } + ); } #endregion } -} \ No newline at end of file +} diff --git a/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetZeroCoordinatePanelHandler.cs b/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetZeroCoordinatePanelHandler.cs index 586a4840..77ac413e 100644 --- a/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetZeroCoordinatePanelHandler.cs +++ b/Assets/Scripts/Pinpoint/UI/EphysCopilot/ResetZeroCoordinatePanelHandler.cs @@ -1,3 +1,5 @@ +using System; +using System.Globalization; using EphysLink; using TMPro; using UnityEngine; @@ -10,7 +12,8 @@ public class ResetZeroCoordinatePanelHandler : MonoBehaviour private void Start() { - _manipulatorIDText.text = "Manipulator " + ProbeManager.ManipulatorBehaviorController.ManipulatorID; + _manipulatorIDText.text = + "Manipulator " + ProbeManager.ManipulatorBehaviorController.ManipulatorID; _manipulatorIDText.color = ProbeManager.Color; } @@ -23,22 +26,38 @@ private void Start() /// public void ResetZeroCoordinate() { - CommunicationManager.Instance.GetPosition(ProbeManager.ManipulatorBehaviorController.ManipulatorID, + CommunicationManager.Instance.GetPosition( + ProbeManager.ManipulatorBehaviorController.ManipulatorID, zeroCoordinate => { - ProbeManager.ManipulatorBehaviorController.ZeroCoordinateOffset = zeroCoordinate; + ProbeManager.ManipulatorBehaviorController.ZeroCoordinateOffset = + zeroCoordinate; ProbeManager.ManipulatorBehaviorController.BrainSurfaceOffset = 0; - }); + } + ); + + // Log event. + OutputLog.Log( + new[] + { + "Copilot", + DateTime.Now.ToString(CultureInfo.InvariantCulture), + "ResetZeroCoordinate", + ProbeManager.ManipulatorBehaviorController.ManipulatorID, + ProbeManager.ManipulatorBehaviorController.ZeroCoordinateOffset.ToString() + } + ); } #endregion #region Components - [SerializeField] private TMP_Text _manipulatorIDText; + [SerializeField] + private TMP_Text _manipulatorIDText; public ProbeManager ProbeManager { private get; set; } #endregion } -} \ No newline at end of file +} diff --git a/Assets/Scripts/Pinpoint/UI/EphysCopilot/trajectoryplanner.ui.ephyscopilot.asmdef b/Assets/Scripts/Pinpoint/UI/EphysCopilot/trajectoryplanner.ui.ephyscopilot.asmdef index 57c39c7a..511182e8 100644 --- a/Assets/Scripts/Pinpoint/UI/EphysCopilot/trajectoryplanner.ui.ephyscopilot.asmdef +++ b/Assets/Scripts/Pinpoint/UI/EphysCopilot/trajectoryplanner.ui.ephyscopilot.asmdef @@ -11,7 +11,8 @@ "GUID:160da71e82fca504abe6ab7833979cdb", "GUID:baba835d3f6de1948879ba12c4c45295", "GUID:0e68f56b71507ea46a7a7e86308f8eef", - "GUID:1487851fb22c2cc4a85babd43c153c20" + "GUID:1487851fb22c2cc4a85babd43c153c20", + "GUID:101fa090a25fae3429dfee1993cb96f8" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Assets/Scripts/Pinpoint/UI/EphysLinkSettings/EphysLinkSettings.cs b/Assets/Scripts/Pinpoint/UI/EphysLinkSettings/EphysLinkSettings.cs index 80302dba..d8b5debf 100644 --- a/Assets/Scripts/Pinpoint/UI/EphysLinkSettings/EphysLinkSettings.cs +++ b/Assets/Scripts/Pinpoint/UI/EphysLinkSettings/EphysLinkSettings.cs @@ -20,7 +20,7 @@ public class EphysLinkSettings : MonoBehaviour { #region Constants - private const string EPHYS_LINK_NAME = "EphysLink-v2.0.0b1"; + private const string EPHYS_LINK_NAME = "EphysLink-v2.0.0b2"; private static string EphysLinkExePath => Path.Combine( Application.streamingAssetsPath,