diff --git a/InstallForge/2021_1_InstallProject.ifp b/InstallForge/2021_1_InstallProject.ifp new file mode 100644 index 0000000..2d5a883 Binary files /dev/null and b/InstallForge/2021_1_InstallProject.ifp differ diff --git a/McIntyreAFC/McIntyreAFC.csproj b/McIntyreAFC/McIntyreAFC.csproj index c579dd6..fe614c3 100644 --- a/McIntyreAFC/McIntyreAFC.csproj +++ b/McIntyreAFC/McIntyreAFC.csproj @@ -1,23 +1,28 @@  - - netcoreapp3.1 - + + netcoreapp3.1 + - - ..\ProtocolMasterWPF\bin\Debug\netcoreapp3.1\Extensions\ - false - 5 - + + ..\ProtocolMasterWPF\bin\Debug\netcoreapp3.1\Extensions\ + false + 5 + - - - + + ..\ProtocolMasterWPF\bin\Release\netcoreapp3.1\Extensions\ + false + - - - false - - + + + + + + + false + + diff --git a/ProtocolMasterCore/Protocol/Driver/DriverMeta.cs b/ProtocolMasterCore/Protocol/Driver/DriverMeta.cs index 9789f38..7f5c356 100644 --- a/ProtocolMasterCore/Protocol/Driver/DriverMeta.cs +++ b/ProtocolMasterCore/Protocol/Driver/DriverMeta.cs @@ -19,6 +19,9 @@ public DriverMeta(string name, string version, params string[] eventNames) : bas this.Version = version; this.HandlerLabels = eventNames; } + public DriverMeta() + { + } public DriverMeta(IDictionary inputs) { @@ -34,7 +37,7 @@ public override string ToString() public bool Equals([AllowNull] IExtensionMeta other) { - return this.Name == other.Name && this.Version == other.Version; + return this.Name == other.Name && this.Version == other.Version; } } } diff --git a/ProtocolMasterCore/Protocol/ExtensionManager.cs b/ProtocolMasterCore/Protocol/ExtensionManager.cs index 5110ac1..b7f498d 100644 --- a/ProtocolMasterCore/Protocol/ExtensionManager.cs +++ b/ProtocolMasterCore/Protocol/ExtensionManager.cs @@ -50,13 +50,13 @@ public IExtensionMeta Selected { foreach (ExportFactory i in Extensions) { - if ((IExtensionMeta)i.Metadata == value) + if (i.Metadata.Equals(value)) { extensionFactory = i; return; } } - throw new Exception($"No extension of type {typeof(E)} of with metadata {value} is loaded"); + throw new ArgumentException($"No extension of type {typeof(E)} of with metadata {value} is loaded"); } } public IEnumerable Options diff --git a/ProtocolMasterCore/Protocol/IExtensionMeta.cs b/ProtocolMasterCore/Protocol/IExtensionMeta.cs index 1b7c824..7bd48c6 100644 --- a/ProtocolMasterCore/Protocol/IExtensionMeta.cs +++ b/ProtocolMasterCore/Protocol/IExtensionMeta.cs @@ -1,4 +1,7 @@ using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Xml.Serialization; namespace ProtocolMasterCore.Protocol { @@ -7,4 +10,5 @@ public interface IExtensionMeta : IEquatable string Name { get; } string Version { get; } } + } diff --git a/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs b/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs index 2708dcd..0766dc8 100644 --- a/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs +++ b/ProtocolMasterWPF/Helpers/NotNullToBoolConverter.cs @@ -13,7 +13,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - return value != null; + throw new NotSupportedException(); } } } diff --git a/ProtocolMasterWPF/Model/Camera.cs b/ProtocolMasterWPF/Model/Camera.cs index f8341bf..184bd5a 100644 --- a/ProtocolMasterWPF/Model/Camera.cs +++ b/ProtocolMasterWPF/Model/Camera.cs @@ -30,18 +30,38 @@ private async void InitVideoStore() { videoStore = await StorageFolder.GetFolderFromPathAsync(storagePath); } - public async void InitializeCap(DeviceInformation videoDevice, DeviceInformation audioDevice) + public void InitializeCap(DeviceInformation videoDevice, DeviceInformation audioDevice) { - try + if(videoDevice == null) + Log.Error($"Video Device null"); + if (audioDevice == null) { - await MediaCap.InitializeAsync(new MediaCaptureInitializationSettings() { - VideoDeviceId = videoDevice.Id, - AudioDeviceId = audioDevice.Id - }); + try + { + MediaCap.InitializeAsync(new MediaCaptureInitializationSettings() + { + VideoDeviceId = videoDevice.Id + }).AsTask().Wait(); + } + catch (UnauthorizedAccessException ex) + { + Log.Error($"The app was denied access to the camera: {ex}"); + } } - catch (UnauthorizedAccessException ex) + else { - Log.Error($"The app was denied access to the camera:\t{ex}"); + try + { + MediaCap.InitializeAsync(new MediaCaptureInitializationSettings() + { + VideoDeviceId = videoDevice.Id, + AudioDeviceId = audioDevice.Id + }).AsTask().Wait(); + } + catch (UnauthorizedAccessException ex) + { + Log.Error($"The app was denied access to the camera: {ex}"); + } } } public async void StartPreview() diff --git a/ProtocolMasterWPF/Model/CameraContainer.cs b/ProtocolMasterWPF/Model/CameraContainer.cs index e69229b..a51e9c5 100644 --- a/ProtocolMasterWPF/Model/CameraContainer.cs +++ b/ProtocolMasterWPF/Model/CameraContainer.cs @@ -1,4 +1,6 @@ -using System; +using ProtocolMasterCore.Utility; +using ProtocolMasterWPF.Properties; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,53 +13,79 @@ internal class CameraContainer : Observable { public CameraContainer() { - RefreshDevices(); - _videoDevice = VideoDevices.First(); - _audioDevice = AudioDevices.First(); + InitDefaultDevices(); ResetCam(); } - public Camera Cam { get => _cam; private set { _cam = value; NotifyProperty(); } } - private Camera _cam; - public DeviceInformation VideoDevice { get => _videoDevice; set { _videoDevice = value; NotifyProperty(); ResetCam(); } } - private DeviceInformation _videoDevice; - public DeviceInformation AudioDevice { get => _audioDevice; set { _audioDevice = value; NotifyProperty(); ResetCam(); } } - private DeviceInformation _audioDevice; - public void StartRecord() => Cam.StartRecord(); - public void StopRecord() => Cam.StopRecord(); - private DeviceInformationCollection _videoDevices; - public DeviceInformationCollection VideoDevices + public Camera Cam { - get => _videoDevices; - set + get => _cam; + private set { - _videoDevices = value; + _cam = value; NotifyProperty(); } } - public void RefreshDevices() + private Camera _cam; + public DeviceInformation VideoDevice { - Task task = DeviceInformation.FindAllAsync(DeviceClass.VideoCapture).AsTask(); - task.Wait(); - VideoDevices = task.Result; - task.Dispose(); - - task = DeviceInformation.FindAllAsync(DeviceClass.AudioCapture).AsTask(); - task.Wait(); - AudioDevices = task.Result; + get => _videoDevice; + set + { + _videoDevice = value; + NotifyProperty(); + ResetCam(); + if (value.Id != Settings.Default.CameraID) + { + Settings.Default.CameraID = value.Id; + Settings.Default.Save(); + } + } } - private DeviceInformationCollection _audioDevices; - public DeviceInformationCollection AudioDevices + private DeviceInformation _videoDevice; + public DeviceInformation AudioDevice { - get => _audioDevices; + get => _audioDevice; set { - _audioDevices = value; + _audioDevice = value; NotifyProperty(); + ResetCam(); + if(value.Id != Settings.Default.MicrophoneID) + { + Settings.Default.MicrophoneID = value.Id; + Settings.Default.Save(); + } } } + private DeviceInformation _audioDevice; + public void StartRecord() => Cam.StartRecord(); + public void StopRecord() => Cam.StopRecord(); + private void ResetCam() { Cam = new Camera(VideoDevice, AudioDevice); } + + public void InitDefaultDevices() + { + try + { + _videoDevice = MediaDevices.Instance.VideoDeviceByID(Settings.Default.CameraID); + } + catch (Exception e) + { + Log.Error($"Default Camera could not be seleted, exception: {e}"); + VideoDevice = MediaDevices.Instance.VideoDevices.First(); + } + try + { + _audioDevice = MediaDevices.Instance.AudioDeviceByID(Settings.Default.MicrophoneID); + } + catch (Exception e) + { + Log.Error($"Default Microphone could not be seleted, exception: {e}"); + AudioDevice = MediaDevices.Instance.AudioDevices.First(); + } + } } } diff --git a/ProtocolMasterWPF/Model/ExtensionMetaSetting.cs b/ProtocolMasterWPF/Model/ExtensionMetaSetting.cs new file mode 100644 index 0000000..1d6318d --- /dev/null +++ b/ProtocolMasterWPF/Model/ExtensionMetaSetting.cs @@ -0,0 +1,27 @@ +using ProtocolMasterCore.Protocol; +using ProtocolMasterCore.Protocol.Driver; +using ProtocolMasterCore.Protocol.Interpreter; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +namespace ProtocolMasterWPF.Model +{ + [Serializable] + public sealed class ExtensionMetaSetting : IExtensionMeta + { + public string Name { get; set; } + public string Version { get; set; } + public ExtensionMetaSetting() { } + public ExtensionMetaSetting(IExtensionMeta from) + { + Name = from.Name; Version = from.Version; + } + public bool Equals([AllowNull] IExtensionMeta other) + { + return this.Name == other.Name && this.Version == other.Version; + } + } +} diff --git a/ProtocolMasterWPF/Model/MediaDevices.cs b/ProtocolMasterWPF/Model/MediaDevices.cs new file mode 100644 index 0000000..de01729 --- /dev/null +++ b/ProtocolMasterWPF/Model/MediaDevices.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Devices.Enumeration; + +namespace ProtocolMasterWPF.Model +{ + internal class MediaDevices : Observable + { + private static MediaDevices instance = new MediaDevices(); + public static MediaDevices Instance { get => instance; } + static MediaDevices() { } + private MediaDevices() + { + RefreshDevices(); + } + private DeviceInformationCollection _videoDevices; + + public DeviceInformationCollection VideoDevices + { + get => _videoDevices; + set + { + _videoDevices = value; + NotifyProperty(); + } + } + public void RefreshDevices() + { + Task task = DeviceInformation.FindAllAsync(DeviceClass.VideoCapture).AsTask(); + task.Wait(); + VideoDevices = task.Result; + task.Dispose(); + + task = DeviceInformation.FindAllAsync(DeviceClass.AudioCapture).AsTask(); + task.Wait(); + AudioDevices = task.Result; + } + private DeviceInformationCollection _audioDevices; + public DeviceInformationCollection AudioDevices + { + get => _audioDevices; + set + { + _audioDevices = value; + NotifyProperty(); + } + } + public DeviceInformation AudioDeviceByID(string id) + { + return AudioDevices.First(a => a.Id == id); + } + public DeviceInformation VideoDeviceByID(string id) + { + return VideoDevices.First(a => a.Id == id); + } + } +} diff --git a/ProtocolMasterWPF/Model/Session.cs b/ProtocolMasterWPF/Model/Session.cs index 610562e..3e26241 100644 --- a/ProtocolMasterWPF/Model/Session.cs +++ b/ProtocolMasterWPF/Model/Session.cs @@ -1,6 +1,7 @@ using ProtocolMasterCore.Protocol; using ProtocolMasterCore.Utility; using ProtocolMasterWPF.Helpers; +using ProtocolMasterWPF.Properties; using System; using System.Collections.Generic; using System.Linq; @@ -29,10 +30,38 @@ internal class Session : Observable CancellationToken CancelToken { get; set; } public List InterpreterOptions { get => _interpreterOptions; private set { _interpreterOptions = value; NotifyProperty(); } } private List _interpreterOptions; - public IExtensionMeta SelectedInterpreter { get => Protocol.InterpreterManager.Selected; set { Protocol.InterpreterManager.Selected = value; NotifyProperty(); } } + public IExtensionMeta SelectedInterpreter + { + get => Protocol.InterpreterManager.Selected; + set + { + if (value == null) return; + Protocol.InterpreterManager.Selected = value; + NotifyProperty(); + if (Settings.Default.InterpreterMeta != value) + { + Settings.Default.InterpreterMeta = new ExtensionMetaSetting(value); + Settings.Default.Save(); + } + } + } public List DriverOptions { get => _driverOptions; private set { _driverOptions = value; NotifyProperty(); } } private List _driverOptions; - public IExtensionMeta SelectedDriver { get => Protocol.DriverManager.Selected; set { Protocol.DriverManager.Selected = value; NotifyProperty(); } } + public IExtensionMeta SelectedDriver + { + get => Protocol.DriverManager.Selected; + set + { + if (value == null) return; + Protocol.DriverManager.Selected = value; + NotifyProperty(); + if (Settings.Default.DriverMeta != value) + { + Settings.Default.DriverMeta = new ExtensionMetaSetting(value); + Settings.Default.Save(); + } + } + } public ClockAnimator Animator { get; private set; } public CameraContainer Cam { get; private set; } SessionState State { get => _state; set { _state = value; NotifyStateProperties(); } } @@ -69,11 +98,44 @@ public Session() Protocol.InterpreterManager.OnEventsLoaded += Animator.FindMaxTime; Protocol.DriverManager.OnProtocolStart += Animator.StartAnimatorNow; Protocol.DriverManager.OnProtocolEnd += Animator.StopAnimator; - Protocol.LoadExtensions(); + + InitDefaultExtensions(); + + Cam = new CameraContainer(); OnStart += Cam.StartRecord; OnStop += Cam.StopRecord; } + public void InitDefaultExtensions() + { + Protocol.LoadExtensions(); + try + { + SelectedDriver = Settings.Default.DriverMeta; + } + catch (ArgumentException e) + { + Log.Error($"Default Driver could not be seleted, exception: {e}"); + SelectedDriver = Protocol.DriverManager.Selected; + } + catch (NullReferenceException) + { + SelectedDriver = Protocol.DriverManager.Selected; + } + try + { + SelectedInterpreter = Settings.Default.InterpreterMeta; + } + catch (ArgumentException e) + { + Log.Error($"Default Interpreter could not be seleted, exception: {e}"); + SelectedInterpreter = Protocol.InterpreterManager.Selected; + } + catch (NullReferenceException) + { + SelectedDriver = Protocol.DriverManager.Selected; + } + } private void LoadInterpreterOptions(List options) => InterpreterOptions = options; private void LoadDriverOptions(List options) => DriverOptions = options; private void NotifyStateProperties() @@ -105,7 +167,7 @@ public void Start(bool overrideCheck = false) OnRun?.Invoke(); Protocol.Run(); } - if(!CancelToken.IsCancellationRequested) + if (!CancelToken.IsCancellationRequested) Stop(); }, CancelSource.Token); SessionTask.Start(); diff --git a/ProtocolMasterWPF/Properties/Settings.Designer.cs b/ProtocolMasterWPF/Properties/Settings.Designer.cs index 723439d..9abb4c8 100644 --- a/ProtocolMasterWPF/Properties/Settings.Designer.cs +++ b/ProtocolMasterWPF/Properties/Settings.Designer.cs @@ -26,12 +26,58 @@ public static Settings Default { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("")] - public string SessionConfig { + public string CameraID { get { - return ((string)(this["SessionConfig"])); + return ((string)(this["CameraID"])); } set { - this["SessionConfig"] = value; + this["CameraID"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string MicrophoneID { + get { + return ((string)(this["MicrophoneID"])); + } + set { + this["MicrophoneID"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::ProtocolMasterWPF.Model.ExtensionMetaSetting DriverMeta { + get { + return ((global::ProtocolMasterWPF.Model.ExtensionMetaSetting)(this["DriverMeta"])); + } + set { + this["DriverMeta"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::ProtocolMasterWPF.Model.ExtensionMetaSetting InterpreterMeta { + get { + return ((global::ProtocolMasterWPF.Model.ExtensionMetaSetting)(this["InterpreterMeta"])); + } + set { + this["InterpreterMeta"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ExperimentDefaultTab { + get { + return ((string)(this["ExperimentDefaultTab"])); + } + set { + this["ExperimentDefaultTab"] = value; } } } diff --git a/ProtocolMasterWPF/Properties/Settings.settings b/ProtocolMasterWPF/Properties/Settings.settings index 5e7af51..6a6e335 100644 --- a/ProtocolMasterWPF/Properties/Settings.settings +++ b/ProtocolMasterWPF/Properties/Settings.settings @@ -2,7 +2,19 @@ - + + + + + + + + + + + + + diff --git a/ProtocolMasterWPF/ProtocolMasterWPF.csproj b/ProtocolMasterWPF/ProtocolMasterWPF.csproj index 54ee78b..7cea15d 100644 --- a/ProtocolMasterWPF/ProtocolMasterWPF.csproj +++ b/ProtocolMasterWPF/ProtocolMasterWPF.csproj @@ -6,6 +6,9 @@ true Assets\Logo\LogoSquareIcon.ico app.manifest + 2021.1 + false + false @@ -15,6 +18,7 @@ x64 + false diff --git a/ProtocolMasterWPF/Settings.cs b/ProtocolMasterWPF/Settings.cs new file mode 100644 index 0000000..aac3792 --- /dev/null +++ b/ProtocolMasterWPF/Settings.cs @@ -0,0 +1,28 @@ +namespace ProtocolMasterWPF.Properties { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Settings { + + public Settings() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/ProtocolMasterWPF/View/ProtocolSelectView.xaml b/ProtocolMasterWPF/View/ProtocolSelectView.xaml index f58f0d8..ad899c2 100644 --- a/ProtocolMasterWPF/View/ProtocolSelectView.xaml +++ b/ProtocolMasterWPF/View/ProtocolSelectView.xaml @@ -8,7 +8,7 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - @@ -70,7 +70,7 @@