Fixed bug in caching output values
Fixed VoiceMeeter version selection Added device status indicator
This commit is contained in:
parent
2525fae237
commit
69a93f68d5
Binary file not shown.
@ -80,7 +80,7 @@ namespace MassiveKnob.Plugin.CoreAudio.GetDefault
|
|||||||
|
|
||||||
CheckActive();
|
CheckActive();
|
||||||
|
|
||||||
// TODO default OSD
|
// TODO (should have) OSD for changing default
|
||||||
//if (settings.OSD)
|
//if (settings.OSD)
|
||||||
//OSDManager.Show(args.Device);
|
//OSDManager.Show(args.Device);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ namespace MassiveKnob.Plugin.CoreAudio.SetDefault
|
|||||||
await playbackDevice.SetAsDefaultCommunicationsAsync();
|
await playbackDevice.SetAsDefaultCommunicationsAsync();
|
||||||
|
|
||||||
|
|
||||||
// TODO OSD for default device
|
// TODO (should have) OSD for changing default
|
||||||
//if (settings.OSD)
|
//if (settings.OSD)
|
||||||
//OSDManager.Show(playbackDevice);
|
//OSDManager.Show(playbackDevice);
|
||||||
}
|
}
|
||||||
|
@ -98,12 +98,6 @@ namespace MassiveKnob.Plugin.SerialDevice.Settings
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected virtual void OnOtherPropertyChanged(string propertyName)
|
|
||||||
{
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void OnNext(DeviceNotificationEvent value)
|
public void OnNext(DeviceNotificationEvent value)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace MassiveKnob.Plugin.VoiceMeeter.Base
|
using System;
|
||||||
|
|
||||||
|
namespace MassiveKnob.Plugin.VoiceMeeter.Base
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for BaseVoiceMeeterSettingsView.xaml
|
/// Interaction logic for BaseVoiceMeeterSettingsView.xaml
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Voicemeeter;
|
using Voicemeeter;
|
||||||
|
|
||||||
@ -16,10 +18,11 @@ namespace MassiveKnob.Plugin.VoiceMeeter.Base
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class BaseVoiceMeeterSettingsViewModel : INotifyPropertyChanged
|
public class BaseVoiceMeeterSettingsViewModel : INotifyPropertyChanged, IDisposable
|
||||||
{
|
{
|
||||||
protected readonly BaseVoiceMeeterSettings Settings;
|
protected readonly BaseVoiceMeeterSettings Settings;
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
public event EventHandler Disposed;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||||
public IList<VoiceMeeterVersionViewModel> Versions { get; }
|
public IList<VoiceMeeterVersionViewModel> Versions { get; }
|
||||||
@ -52,6 +55,14 @@ namespace MassiveKnob.Plugin.VoiceMeeter.Base
|
|||||||
new VoiceMeeterVersionViewModel(RunVoicemeeterParam.VoicemeeterBanana, "VoiceMeeter Banana"),
|
new VoiceMeeterVersionViewModel(RunVoicemeeterParam.VoicemeeterBanana, "VoiceMeeter Banana"),
|
||||||
new VoiceMeeterVersionViewModel(RunVoicemeeterParam.VoicemeeterPotato, "VoiceMeeter Potato")
|
new VoiceMeeterVersionViewModel(RunVoicemeeterParam.VoicemeeterPotato, "VoiceMeeter Potato")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
UpdateSelectedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateSelectedVersion()
|
||||||
|
{
|
||||||
|
selectedVersion = Versions.SingleOrDefault(v => v.Version == Settings.Version) ?? Versions.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -63,10 +74,28 @@ namespace MassiveKnob.Plugin.VoiceMeeter.Base
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public virtual void VoiceMeeterVersionChanged()
|
||||||
|
{
|
||||||
|
UpdateSelectedVersion();
|
||||||
|
OnDependantPropertyChanged(nameof(SelectedVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDependantPropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
Disposed?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VoiceMeeterVersionViewModel
|
public class VoiceMeeterVersionViewModel
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Remoting.Channels;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Voicemeeter;
|
using Voicemeeter;
|
||||||
@ -12,18 +13,19 @@ namespace MassiveKnob.Plugin.VoiceMeeter.GetParameter
|
|||||||
public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.OutputDigital;
|
public MassiveKnobActionType ActionType { get; } = MassiveKnobActionType.OutputDigital;
|
||||||
public string Name { get; } = Strings.GetParameterName;
|
public string Name { get; } = Strings.GetParameterName;
|
||||||
public string Description { get; } = Strings.GetParameterDescription;
|
public string Description { get; } = Strings.GetParameterDescription;
|
||||||
|
|
||||||
|
|
||||||
public IMassiveKnobActionInstance Create(ILogger logger)
|
public IMassiveKnobActionInstance Create(ILogger logger)
|
||||||
{
|
{
|
||||||
return new Instance();
|
return new Instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class Instance : IMassiveKnobActionInstance, IVoiceMeeterAction
|
private class Instance : IMassiveKnobActionInstance, IVoiceMeeterAction
|
||||||
{
|
{
|
||||||
private IMassiveKnobActionContext actionContext;
|
private IMassiveKnobActionContext actionContext;
|
||||||
private VoiceMeeterGetParameterActionSettings settings;
|
private VoiceMeeterGetParameterActionSettings settings;
|
||||||
|
private VoiceMeeterGetParameterActionSettingsViewModel viewModel;
|
||||||
private Parameters parameters;
|
private Parameters parameters;
|
||||||
private IDisposable parameterChanged;
|
private IDisposable parameterChanged;
|
||||||
|
|
||||||
@ -48,51 +50,54 @@ namespace MassiveKnob.Plugin.VoiceMeeter.GetParameter
|
|||||||
|
|
||||||
private void ApplySettings()
|
private void ApplySettings()
|
||||||
{
|
{
|
||||||
if (InstanceRegister.Version == RunVoicemeeterParam.None)
|
if (InstanceRegister.Version == RunVoicemeeterParam.None || string.IsNullOrEmpty(settings.Parameter))
|
||||||
|
{
|
||||||
|
parameterChanged?.Dispose();
|
||||||
|
parameterChanged = null;
|
||||||
|
|
||||||
|
parameters?.Dispose();
|
||||||
|
parameters = null;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (parameters == null)
|
if (parameters == null)
|
||||||
parameters = new Parameters();
|
parameters = new Parameters();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(settings.Parameter))
|
|
||||||
{
|
|
||||||
parameterChanged?.Dispose();
|
|
||||||
parameterChanged = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameterChanged == null)
|
if (parameterChanged == null)
|
||||||
parameterChanged = parameters.Subscribe(x => ParametersChanged());
|
parameterChanged = parameters.Subscribe(x => ParametersChanged());
|
||||||
|
|
||||||
// TODO directly update output depending on value
|
ParametersChanged();
|
||||||
/*
|
|
||||||
if (playbackDevice != null)
|
|
||||||
actionContext.SetDigitalOutput(settings.Inverted ? !playbackDevice.IsMuted : playbackDevice.IsMuted);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public UserControl CreateSettingsControl()
|
public UserControl CreateSettingsControl()
|
||||||
{
|
{
|
||||||
var viewModel = new VoiceMeeterGetParameterActionSettingsViewModel(settings);
|
viewModel = new VoiceMeeterGetParameterActionSettingsViewModel(settings);
|
||||||
viewModel.PropertyChanged += (sender, args) =>
|
viewModel.PropertyChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
actionContext.SetSettings(settings);
|
actionContext.SetSettings(settings);
|
||||||
ApplySettings();
|
ApplySettings();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
viewModel.Disposed += (sender, args) =>
|
||||||
|
{
|
||||||
|
if (sender == viewModel)
|
||||||
|
viewModel = null;
|
||||||
|
};
|
||||||
|
|
||||||
return new VoiceMeeterGetParameterActionSettingsView(viewModel);
|
return new VoiceMeeterGetParameterActionSettingsView(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void VoiceMeeterVersionChanged()
|
public void VoiceMeeterVersionChanged()
|
||||||
{
|
{
|
||||||
// TODO update viewModel
|
viewModel?.VoiceMeeterVersionChanged();
|
||||||
// TODO reset parameterChanged subscription
|
|
||||||
|
|
||||||
actionContext.SetSettings(settings);
|
actionContext.SetSettings(settings);
|
||||||
|
ApplySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,11 +106,8 @@ namespace MassiveKnob.Plugin.VoiceMeeter.GetParameter
|
|||||||
if (InstanceRegister.Version == RunVoicemeeterParam.None || string.IsNullOrEmpty(settings.Parameter))
|
if (InstanceRegister.Version == RunVoicemeeterParam.None || string.IsNullOrEmpty(settings.Parameter))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO if another task is already running, wait / chain
|
InstanceRegister.InitializeVoicemeeter().ContinueWith(t =>
|
||||||
// TODO only start task if not yet initialized
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
await InstanceRegister.InitializeVoicemeeter();
|
|
||||||
bool on;
|
bool on;
|
||||||
|
|
||||||
if (float.TryParse(settings.Value, out var settingsFloatValue))
|
if (float.TryParse(settings.Value, out var settingsFloatValue))
|
||||||
@ -130,10 +132,9 @@ namespace MassiveKnob.Plugin.VoiceMeeter.GetParameter
|
|||||||
on = string.Equals(value, settings.Value, StringComparison.InvariantCultureIgnoreCase);
|
on = string.Equals(value, settings.Value, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO check specific parameter for changes, not just any parameter
|
|
||||||
actionContext.SetDigitalOutput(settings.Inverted ? !on : on);
|
actionContext.SetDigitalOutput(settings.Inverted ? !on : on);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,22 @@
|
|||||||
namespace MassiveKnob.Plugin.VoiceMeeter.GetParameter
|
using System;
|
||||||
|
|
||||||
|
namespace MassiveKnob.Plugin.VoiceMeeter.GetParameter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for VoiceMeeterGetParameterActionSettingsView.xaml
|
/// Interaction logic for VoiceMeeterGetParameterActionSettingsView.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class VoiceMeeterGetParameterActionSettingsView
|
public partial class VoiceMeeterGetParameterActionSettingsView : IDisposable
|
||||||
{
|
{
|
||||||
public VoiceMeeterGetParameterActionSettingsView(VoiceMeeterGetParameterActionSettingsViewModel viewModel)
|
public VoiceMeeterGetParameterActionSettingsView(VoiceMeeterGetParameterActionSettingsViewModel viewModel)
|
||||||
{
|
{
|
||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
(DataContext as VoiceMeeterGetParameterActionSettingsViewModel)?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -106,5 +106,10 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Reactive">
|
||||||
|
<Version>5.0.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -24,6 +25,7 @@ namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
|||||||
{
|
{
|
||||||
private IMassiveKnobActionContext actionContext;
|
private IMassiveKnobActionContext actionContext;
|
||||||
private VoiceMeeterRunMacroActionSettings settings;
|
private VoiceMeeterRunMacroActionSettings settings;
|
||||||
|
private VoiceMeeterRunMacroActionSettingsViewModel viewModel;
|
||||||
|
|
||||||
|
|
||||||
public void Initialize(IMassiveKnobActionContext context)
|
public void Initialize(IMassiveKnobActionContext context)
|
||||||
@ -43,7 +45,7 @@ namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
|||||||
|
|
||||||
public UserControl CreateSettingsControl()
|
public UserControl CreateSettingsControl()
|
||||||
{
|
{
|
||||||
var viewModel = new VoiceMeeterRunMacroActionSettingsViewModel(settings);
|
viewModel = new VoiceMeeterRunMacroActionSettingsViewModel(settings);
|
||||||
viewModel.PropertyChanged += (sender, args) =>
|
viewModel.PropertyChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
if (!viewModel.IsSettingsProperty(args.PropertyName))
|
||||||
@ -52,6 +54,12 @@ namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
|||||||
actionContext.SetSettings(settings);
|
actionContext.SetSettings(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
viewModel.Disposed += (sender, args) =>
|
||||||
|
{
|
||||||
|
if (sender == viewModel)
|
||||||
|
viewModel = null;
|
||||||
|
};
|
||||||
|
|
||||||
return new VoiceMeeterRunMacroActionSettingsView(viewModel);
|
return new VoiceMeeterRunMacroActionSettingsView(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,8 +79,7 @@ namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
|||||||
|
|
||||||
public void VoiceMeeterVersionChanged()
|
public void VoiceMeeterVersionChanged()
|
||||||
{
|
{
|
||||||
// TODO update viewModel
|
viewModel?.VoiceMeeterVersionChanged();
|
||||||
|
|
||||||
actionContext.SetSettings(settings);
|
actionContext.SetSettings(settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
using System;
|
||||||
|
|
||||||
|
namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for VoiceMeeterRunMacroActionSettingsView.xaml
|
/// Interaction logic for VoiceMeeterRunMacroActionSettingsView.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class VoiceMeeterRunMacroActionSettingsView
|
public partial class VoiceMeeterRunMacroActionSettingsView : IDisposable
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once SuggestBaseTypeForParameter
|
||||||
public VoiceMeeterRunMacroActionSettingsView(VoiceMeeterRunMacroActionSettingsViewModel viewModel)
|
public VoiceMeeterRunMacroActionSettingsView(VoiceMeeterRunMacroActionSettingsViewModel viewModel)
|
||||||
{
|
{
|
||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
(DataContext as VoiceMeeterRunMacroActionSettingsViewModel)?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
using MassiveKnob.Plugin.VoiceMeeter.Base;
|
using System;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Reactive.Subjects;
|
||||||
|
using MassiveKnob.Plugin.VoiceMeeter.Base;
|
||||||
|
|
||||||
namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
||||||
{
|
{
|
||||||
public class VoiceMeeterRunMacroActionSettingsViewModel : BaseVoiceMeeterSettingsViewModel<VoiceMeeterRunMacroActionSettings>
|
public class VoiceMeeterRunMacroActionSettingsViewModel : BaseVoiceMeeterSettingsViewModel<VoiceMeeterRunMacroActionSettings>, IDisposable
|
||||||
{
|
{
|
||||||
// ReSharper disable UnusedMember.Global - used by WPF Bindingpriv
|
private readonly Subject<bool> throttledScriptChanged = new Subject<bool>();
|
||||||
|
private readonly IDisposable scriptChangedSubscription;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||||
public string Script
|
public string Script
|
||||||
{
|
{
|
||||||
get => Settings.Script;
|
get => Settings.Script;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// TODO timer for change notification
|
|
||||||
if (value == Settings.Script)
|
if (value == Settings.Script)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Settings.Script = value;
|
Settings.Script = value;
|
||||||
OnPropertyChanged();
|
throttledScriptChanged.OnNext(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ReSharper restore UnusedMember.Global
|
// ReSharper restore UnusedMember.Global
|
||||||
@ -24,6 +29,19 @@ namespace MassiveKnob.Plugin.VoiceMeeter.RunMacro
|
|||||||
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
// ReSharper disable once SuggestBaseTypeForParameter - by design
|
||||||
public VoiceMeeterRunMacroActionSettingsViewModel(VoiceMeeterRunMacroActionSettings settings) : base(settings)
|
public VoiceMeeterRunMacroActionSettingsViewModel(VoiceMeeterRunMacroActionSettings settings) : base(settings)
|
||||||
{
|
{
|
||||||
|
scriptChangedSubscription = throttledScriptChanged
|
||||||
|
.Throttle(TimeSpan.FromSeconds(1))
|
||||||
|
.Subscribe(b =>
|
||||||
|
{
|
||||||
|
OnDependantPropertyChanged(nameof(Script));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
scriptChangedSubscription?.Dispose();
|
||||||
|
throttledScriptChanged?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,24 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
MassiveKnobDeviceInfo SetActiveDevice(IMassiveKnobDevice device);
|
MassiveKnobDeviceInfo SetActiveDevice(IMassiveKnobDevice device);
|
||||||
|
|
||||||
|
MassiveKnobDeviceStatus DeviceStatus { get; }
|
||||||
|
IObservable<MassiveKnobDeviceStatus> DeviceStatusSubject { get; }
|
||||||
|
|
||||||
|
|
||||||
MassiveKnobActionInfo GetAction(MassiveKnobActionType actionType, int index);
|
MassiveKnobActionInfo GetAction(MassiveKnobActionType actionType, int index);
|
||||||
MassiveKnobActionInfo SetAction(MassiveKnobActionType actionType, int index, IMassiveKnobAction action);
|
MassiveKnobActionInfo SetAction(MassiveKnobActionType actionType, int index, IMassiveKnobAction action);
|
||||||
|
|
||||||
MassiveKnobSettings GetSettings();
|
MassiveKnobSettings GetSettings();
|
||||||
void UpdateSettings(Action<MassiveKnobSettings> applyChanges);
|
void UpdateSettings(Action<MassiveKnobSettings> applyChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum MassiveKnobDeviceStatus
|
||||||
|
{
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class MassiveKnobDeviceInfo
|
public class MassiveKnobDeviceInfo
|
||||||
|
@ -17,11 +17,12 @@ namespace MassiveKnob.Core
|
|||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
|
||||||
private readonly object settingsLock = new object();
|
private readonly object settingsLock = new object();
|
||||||
private MassiveKnobSettings massiveKnobSettings;
|
private readonly MassiveKnobSettings settings;
|
||||||
private readonly SerialQueue flushSettingsQueue = new SerialQueue();
|
private readonly SerialQueue flushSettingsQueue = new SerialQueue();
|
||||||
|
|
||||||
private MassiveKnobDeviceInfo activeDevice;
|
private MassiveKnobDeviceInfo activeDevice;
|
||||||
private readonly Subject<MassiveKnobDeviceInfo> activeDeviceInfoSubject = new Subject<MassiveKnobDeviceInfo>();
|
private readonly Subject<MassiveKnobDeviceInfo> activeDeviceInfoSubject = new Subject<MassiveKnobDeviceInfo>();
|
||||||
|
private readonly Subject<MassiveKnobDeviceStatus> deviceStatusSubject = new Subject<MassiveKnobDeviceStatus>();
|
||||||
private IMassiveKnobDeviceContext activeDeviceContext;
|
private IMassiveKnobDeviceContext activeDeviceContext;
|
||||||
|
|
||||||
private readonly List<ActionMapping> analogInputs = new List<ActionMapping>();
|
private readonly List<ActionMapping> analogInputs = new List<ActionMapping>();
|
||||||
@ -48,12 +49,15 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
public IObservable<MassiveKnobDeviceInfo> ActiveDeviceSubject => activeDeviceInfoSubject;
|
public IObservable<MassiveKnobDeviceInfo> ActiveDeviceSubject => activeDeviceInfoSubject;
|
||||||
|
|
||||||
|
public MassiveKnobDeviceStatus DeviceStatus { get; private set; } = MassiveKnobDeviceStatus.Disconnected;
|
||||||
|
public IObservable<MassiveKnobDeviceStatus> DeviceStatusSubject => deviceStatusSubject;
|
||||||
|
|
||||||
public MassiveKnobOrchestrator(IPluginManager pluginManager, ILogger logger, MassiveKnobSettings massiveKnobSettings)
|
|
||||||
|
public MassiveKnobOrchestrator(IPluginManager pluginManager, ILogger logger, MassiveKnobSettings settings)
|
||||||
{
|
{
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.massiveKnobSettings = massiveKnobSettings;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -87,11 +91,11 @@ namespace MassiveKnob.Core
|
|||||||
{
|
{
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
if (massiveKnobSettings.Device == null)
|
if (settings.Device == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var allDevices = pluginManager.GetDevicePlugins().SelectMany(dp => dp.Devices);
|
var allDevices = pluginManager.GetDevicePlugins().SelectMany(dp => dp.Devices);
|
||||||
var device = allDevices.FirstOrDefault(d => d.DeviceId == massiveKnobSettings.Device.DeviceId);
|
var device = allDevices.FirstOrDefault(d => d.DeviceId == settings.Device.DeviceId);
|
||||||
|
|
||||||
InternalSetActiveDevice(device, false);
|
InternalSetActiveDevice(device, false);
|
||||||
}
|
}
|
||||||
@ -105,7 +109,7 @@ namespace MassiveKnob.Core
|
|||||||
return InternalSetActiveDevice(device, true);
|
return InternalSetActiveDevice(device, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public MassiveKnobActionInfo GetAction(MassiveKnobActionType actionType, int index)
|
public MassiveKnobActionInfo GetAction(MassiveKnobActionType actionType, int index)
|
||||||
{
|
{
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
@ -166,7 +170,7 @@ namespace MassiveKnob.Core
|
|||||||
{
|
{
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
return massiveKnobSettings.Clone();
|
return settings.Clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +179,7 @@ namespace MassiveKnob.Core
|
|||||||
{
|
{
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
applyChanges(massiveKnobSettings);
|
applyChanges(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlushSettings();
|
FlushSettings();
|
||||||
@ -193,10 +197,10 @@ namespace MassiveKnob.Core
|
|||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
if (device == null)
|
if (device == null)
|
||||||
massiveKnobSettings.Device = null;
|
settings.Device = null;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
massiveKnobSettings.Device = new MassiveKnobSettings.DeviceSettings
|
settings.Device = new MassiveKnobSettings.DeviceSettings
|
||||||
{
|
{
|
||||||
DeviceId = device.DeviceId,
|
DeviceId = device.DeviceId,
|
||||||
Settings = null
|
Settings = null
|
||||||
@ -208,7 +212,10 @@ namespace MassiveKnob.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
ActiveDevice?.Instance.Dispose();
|
ActiveDevice?.Instance.Dispose();
|
||||||
|
SetDeviceStatus(null, MassiveKnobDeviceStatus.Disconnected);
|
||||||
|
|
||||||
|
// TODO (must have) move initialization to separate Task, to prevent issues at startup
|
||||||
|
// TODO (must have) exception handling!
|
||||||
if (device != null)
|
if (device != null)
|
||||||
{
|
{
|
||||||
var instance = device.Create(new SerilogLoggerProvider(logger.ForContext("Context", new { Device = device.DeviceId })).CreateLogger(null));
|
var instance = device.Create(new SerilogLoggerProvider(logger.ForContext("Context", new { Device = device.DeviceId })).CreateLogger(null));
|
||||||
@ -230,11 +237,11 @@ namespace MassiveKnob.Core
|
|||||||
protected T GetDeviceSettings<T>(IMassiveKnobDeviceContext context) where T : class, new()
|
protected T GetDeviceSettings<T>(IMassiveKnobDeviceContext context) where T : class, new()
|
||||||
{
|
{
|
||||||
if (context != activeDeviceContext)
|
if (context != activeDeviceContext)
|
||||||
throw new InvalidOperationException("Caller must be the active device to retrieve the massiveKnobSettings");
|
throw new InvalidOperationException("Caller must be the active device to retrieve the settings");
|
||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
return massiveKnobSettings.Device.Settings?.ToObject<T>() ?? new T();
|
return settings.Device.Settings?.ToObject<T>() ?? new T();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,23 +249,40 @@ namespace MassiveKnob.Core
|
|||||||
protected void SetDeviceSettings<T>(IMassiveKnobDeviceContext context, IMassiveKnobDevice device, T deviceSettings) where T : class, new()
|
protected void SetDeviceSettings<T>(IMassiveKnobDeviceContext context, IMassiveKnobDevice device, T deviceSettings) where T : class, new()
|
||||||
{
|
{
|
||||||
if (context != activeDeviceContext)
|
if (context != activeDeviceContext)
|
||||||
throw new InvalidOperationException("Caller must be the active device to update the massiveKnobSettings");
|
throw new InvalidOperationException("Caller must be the active device to update the settings");
|
||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
if (massiveKnobSettings.Device == null)
|
if (settings.Device == null)
|
||||||
massiveKnobSettings.Device = new MassiveKnobSettings.DeviceSettings
|
settings.Device = new MassiveKnobSettings.DeviceSettings
|
||||||
{
|
{
|
||||||
DeviceId = device.DeviceId
|
DeviceId = device.DeviceId
|
||||||
};
|
};
|
||||||
|
|
||||||
massiveKnobSettings.Device.Settings = JObject.FromObject(deviceSettings);
|
settings.Device.Settings = JObject.FromObject(deviceSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlushSettings();
|
FlushSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void SetDeviceStatus(IMassiveKnobDeviceContext context, MassiveKnobDeviceStatus status)
|
||||||
|
{
|
||||||
|
if (context != null && context != activeDeviceContext)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (settingsLock)
|
||||||
|
{
|
||||||
|
if (status == DeviceStatus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DeviceStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceStatusSubject.OnNext(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected T GetActionSettings<T>(IMassiveKnobActionContext context, IMassiveKnobAction action, int index) where T : class, new()
|
protected T GetActionSettings<T>(IMassiveKnobActionContext context, IMassiveKnobAction action, int index) where T : class, new()
|
||||||
{
|
{
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
@ -268,7 +292,7 @@ namespace MassiveKnob.Core
|
|||||||
return new T();
|
return new T();
|
||||||
|
|
||||||
if (list[index]?.Context != context)
|
if (list[index]?.Context != context)
|
||||||
throw new InvalidOperationException("Caller must be the active action to retrieve the massiveKnobSettings");
|
throw new InvalidOperationException("Caller must be the active action to retrieve the settings");
|
||||||
|
|
||||||
var settingsList = GetActionSettingsList(action.ActionType);
|
var settingsList = GetActionSettingsList(action.ActionType);
|
||||||
if (index >= settingsList.Count)
|
if (index >= settingsList.Count)
|
||||||
@ -288,7 +312,7 @@ namespace MassiveKnob.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (list[index]?.Context != context)
|
if (list[index]?.Context != context)
|
||||||
throw new InvalidOperationException("Caller must be the active action to retrieve the massiveKnobSettings");
|
throw new InvalidOperationException("Caller must be the active action to retrieve the settings");
|
||||||
|
|
||||||
var settingsList = GetActionSettingsList(action.ActionType);
|
var settingsList = GetActionSettingsList(action.ActionType);
|
||||||
|
|
||||||
@ -317,11 +341,6 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
if (analogOutputValues.TryGetValue(analogInputIndex, out var currentValue) && currentValue == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
analogOutputValues[analogInputIndex] = value;
|
|
||||||
|
|
||||||
var mapping = GetActionMappingList(MassiveKnobActionType.InputAnalog);
|
var mapping = GetActionMappingList(MassiveKnobActionType.InputAnalog);
|
||||||
if (mapping == null || analogInputIndex >= mapping.Count || mapping[analogInputIndex] == null)
|
if (mapping == null || analogInputIndex >= mapping.Count || mapping[analogInputIndex] == null)
|
||||||
return;
|
return;
|
||||||
@ -343,12 +362,6 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
if (digitalOutputValues.TryGetValue(digitalInputIndex, out var currentValue) && currentValue == on)
|
|
||||||
return;
|
|
||||||
|
|
||||||
digitalOutputValues[digitalInputIndex] = on;
|
|
||||||
|
|
||||||
|
|
||||||
var mapping = GetActionMappingList(MassiveKnobActionType.InputDigital);
|
var mapping = GetActionMappingList(MassiveKnobActionType.InputDigital);
|
||||||
if (mapping == null || digitalInputIndex >= mapping.Count || mapping[digitalInputIndex] == null)
|
if (mapping == null || digitalInputIndex >= mapping.Count || mapping[digitalInputIndex] == null)
|
||||||
return;
|
return;
|
||||||
@ -362,13 +375,19 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
public void SetAnalogOutput(IMassiveKnobActionContext context, int index, byte value)
|
public void SetAnalogOutput(IMassiveKnobActionContext context, int index, byte value)
|
||||||
{
|
{
|
||||||
if (activeDevice == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IMassiveKnobDeviceInstance deviceInstance;
|
IMassiveKnobDeviceInstance deviceInstance;
|
||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
|
if (analogOutputValues.TryGetValue(index, out var currentValue) && currentValue == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
analogOutputValues[index] = value;
|
||||||
|
|
||||||
|
|
||||||
|
if (activeDevice == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var list = GetActionMappingList(MassiveKnobActionType.OutputAnalog);
|
var list = GetActionMappingList(MassiveKnobActionType.OutputAnalog);
|
||||||
if (index >= list.Count)
|
if (index >= list.Count)
|
||||||
return;
|
return;
|
||||||
@ -385,13 +404,19 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
public void SetDigitalOutput(IMassiveKnobActionContext context, int index, bool on)
|
public void SetDigitalOutput(IMassiveKnobActionContext context, int index, bool on)
|
||||||
{
|
{
|
||||||
if (activeDevice == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IMassiveKnobDeviceInstance deviceInstance;
|
IMassiveKnobDeviceInstance deviceInstance;
|
||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
|
if (digitalOutputValues.TryGetValue(index, out var currentValue) && currentValue == on)
|
||||||
|
return;
|
||||||
|
|
||||||
|
digitalOutputValues[index] = on;
|
||||||
|
|
||||||
|
|
||||||
|
if (activeDevice == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var list = GetActionMappingList(MassiveKnobActionType.OutputDigital);
|
var list = GetActionMappingList(MassiveKnobActionType.OutputDigital);
|
||||||
if (index >= list.Count)
|
if (index >= list.Count)
|
||||||
return;
|
return;
|
||||||
@ -433,16 +458,16 @@ namespace MassiveKnob.Core
|
|||||||
switch (actionType)
|
switch (actionType)
|
||||||
{
|
{
|
||||||
case MassiveKnobActionType.InputAnalog:
|
case MassiveKnobActionType.InputAnalog:
|
||||||
return massiveKnobSettings.AnalogInput;
|
return settings.AnalogInput;
|
||||||
|
|
||||||
case MassiveKnobActionType.InputDigital:
|
case MassiveKnobActionType.InputDigital:
|
||||||
return massiveKnobSettings.DigitalInput;
|
return settings.DigitalInput;
|
||||||
|
|
||||||
case MassiveKnobActionType.OutputAnalog:
|
case MassiveKnobActionType.OutputAnalog:
|
||||||
return massiveKnobSettings.AnalogOutput;
|
return settings.AnalogOutput;
|
||||||
|
|
||||||
case MassiveKnobActionType.OutputDigital:
|
case MassiveKnobActionType.OutputDigital:
|
||||||
return massiveKnobSettings.DigitalOutput;
|
return settings.DigitalOutput;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(actionType), actionType, null);
|
throw new ArgumentOutOfRangeException(nameof(actionType), actionType, null);
|
||||||
@ -455,7 +480,7 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
massiveKnobSettingsSnapshot = massiveKnobSettings.Clone();
|
massiveKnobSettingsSnapshot = settings.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
flushSettingsQueue.Enqueue(async () =>
|
flushSettingsQueue.Enqueue(async () =>
|
||||||
@ -482,10 +507,10 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
lock (settingsLock)
|
lock (settingsLock)
|
||||||
{
|
{
|
||||||
UpdateMapping(analogInputs, specs.AnalogInputCount, massiveKnobSettings.AnalogInput, DelayedInitialize);
|
UpdateMapping(analogInputs, specs.AnalogInputCount, settings.AnalogInput, DelayedInitialize);
|
||||||
UpdateMapping(digitalInputs, specs.DigitalInputCount, massiveKnobSettings.DigitalInput, DelayedInitialize);
|
UpdateMapping(digitalInputs, specs.DigitalInputCount, settings.DigitalInput, DelayedInitialize);
|
||||||
UpdateMapping(analogOutputs, specs.AnalogOutputCount, massiveKnobSettings.AnalogOutput, DelayedInitialize);
|
UpdateMapping(analogOutputs, specs.AnalogOutputCount, settings.AnalogOutput, DelayedInitialize);
|
||||||
UpdateMapping(digitalOutputs, specs.DigitalOutputCount, massiveKnobSettings.DigitalOutput, DelayedInitialize);
|
UpdateMapping(digitalOutputs, specs.DigitalOutputCount, settings.DigitalOutput, DelayedInitialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var delayedInitializeAction in delayedInitializeActions)
|
foreach (var delayedInitializeAction in delayedInitializeActions)
|
||||||
@ -603,21 +628,20 @@ namespace MassiveKnob.Core
|
|||||||
|
|
||||||
public void Connecting()
|
public void Connecting()
|
||||||
{
|
{
|
||||||
// TODO (should have) update status ?
|
owner.SetDeviceStatus(this, MassiveKnobDeviceStatus.Connecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Connected(DeviceSpecs specs)
|
public void Connected(DeviceSpecs specs)
|
||||||
{
|
{
|
||||||
// TODO (should have) update status ?
|
owner.SetDeviceStatus(this, MassiveKnobDeviceStatus.Connected);
|
||||||
|
|
||||||
owner.UpdateActiveDeviceSpecs(this, specs);
|
owner.UpdateActiveDeviceSpecs(this, specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Disconnected()
|
public void Disconnected()
|
||||||
{
|
{
|
||||||
// TODO (should have) update status ?
|
owner.SetDeviceStatus(this, MassiveKnobDeviceStatus.Disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
27
Windows/MassiveKnob/Strings.Designer.cs
generated
27
Windows/MassiveKnob/Strings.Designer.cs
generated
@ -69,6 +69,33 @@ namespace MassiveKnob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Connected.
|
||||||
|
/// </summary>
|
||||||
|
public static string DeviceStatusConnected {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeviceStatusConnected", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Connecting....
|
||||||
|
/// </summary>
|
||||||
|
public static string DeviceStatusConnecting {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeviceStatusConnecting", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Disconnected.
|
||||||
|
/// </summary>
|
||||||
|
public static string DeviceStatusDisconnected {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeviceStatusDisconnected", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Input #{0}.
|
/// Looks up a localized string similar to Input #{0}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -120,6 +120,15 @@
|
|||||||
<data name="ActionNotConfigured" xml:space="preserve">
|
<data name="ActionNotConfigured" xml:space="preserve">
|
||||||
<value>Not configured</value>
|
<value>Not configured</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DeviceStatusConnected" xml:space="preserve">
|
||||||
|
<value>Connected</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeviceStatusConnecting" xml:space="preserve">
|
||||||
|
<value>Connecting...</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeviceStatusDisconnected" xml:space="preserve">
|
||||||
|
<value>Disconnected</value>
|
||||||
|
</data>
|
||||||
<data name="InputHeader" xml:space="preserve">
|
<data name="InputHeader" xml:space="preserve">
|
||||||
<value>Input #{0}</value>
|
<value>Input #{0}</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -39,6 +39,11 @@
|
|||||||
|
|
||||||
|
|
||||||
<ContentControl Focusable="False" Content="{Binding SettingsControl}" Style="{StaticResource SettingsControl}" />
|
<ContentControl Focusable="False" Content="{Binding SettingsControl}" Style="{StaticResource SettingsControl}" />
|
||||||
|
|
||||||
|
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||||
|
<Ellipse Margin="0,0,4,0" Fill="{Binding ConnectionStatusColor}" Height="8" Width="8" />
|
||||||
|
<TextBlock Text="{Binding ConnectionStatusText}" />
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -8,6 +8,7 @@ using System.Reflection;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
using MassiveKnob.Core;
|
using MassiveKnob.Core;
|
||||||
using MassiveKnob.Plugin;
|
using MassiveKnob.Plugin;
|
||||||
using MassiveKnob.Settings;
|
using MassiveKnob.Settings;
|
||||||
@ -18,7 +19,7 @@ using Serilog.Events;
|
|||||||
namespace MassiveKnob.ViewModel
|
namespace MassiveKnob.ViewModel
|
||||||
{
|
{
|
||||||
// TODO (code quality) split ViewModel for individual views, create viewmodel using container
|
// TODO (code quality) split ViewModel for individual views, create viewmodel using container
|
||||||
// TODO (must have) show device status
|
// TODO (nice to have) installed plugins list
|
||||||
public class SettingsViewModel : IDisposable, INotifyPropertyChanged
|
public class SettingsViewModel : IDisposable, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private readonly Dictionary<SettingsMenuItem, Type> menuItemControls = new Dictionary<SettingsMenuItem, Type>
|
private readonly Dictionary<SettingsMenuItem, Type> menuItemControls = new Dictionary<SettingsMenuItem, Type>
|
||||||
@ -47,6 +48,8 @@ namespace MassiveKnob.ViewModel
|
|||||||
private IEnumerable<InputOutputViewModel> analogOutputs;
|
private IEnumerable<InputOutputViewModel> analogOutputs;
|
||||||
private IEnumerable<InputOutputViewModel> digitalOutputs;
|
private IEnumerable<InputOutputViewModel> digitalOutputs;
|
||||||
|
|
||||||
|
private IDisposable activeDeviceSubscription;
|
||||||
|
private IDisposable deviceStatusSubscription;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
// ReSharper disable UnusedMember.Global - used by WPF Binding
|
||||||
public SettingsMenuItem SelectedMenuItem
|
public SettingsMenuItem SelectedMenuItem
|
||||||
@ -77,18 +80,18 @@ namespace MassiveKnob.ViewModel
|
|||||||
{
|
{
|
||||||
if (value == selectedView)
|
if (value == selectedView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
selectedView = value;
|
selectedView = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IList<DeviceViewModel> Devices { get; }
|
public IList<DeviceViewModel> Devices { get; }
|
||||||
public IList<ActionViewModel> Actions { get; }
|
public IList<ActionViewModel> Actions { get; }
|
||||||
|
|
||||||
|
|
||||||
public DeviceViewModel SelectedDevice
|
public DeviceViewModel SelectedDevice
|
||||||
{
|
{
|
||||||
get => selectedDevice;
|
get => selectedDevice;
|
||||||
@ -141,23 +144,27 @@ namespace MassiveKnob.ViewModel
|
|||||||
|
|
||||||
AnalogInputs = Enumerable
|
AnalogInputs = Enumerable
|
||||||
.Range(0, specs?.AnalogInputCount ?? 0)
|
.Range(0, specs?.AnalogInputCount ?? 0)
|
||||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.InputAnalog, i));
|
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.InputAnalog, i))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
DigitalInputs = Enumerable
|
DigitalInputs = Enumerable
|
||||||
.Range(0, specs?.DigitalInputCount ?? 0)
|
.Range(0, specs?.DigitalInputCount ?? 0)
|
||||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.InputDigital, i));
|
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.InputDigital, i))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
AnalogOutputs = Enumerable
|
AnalogOutputs = Enumerable
|
||||||
.Range(0, specs?.AnalogOutputCount ?? 0)
|
.Range(0, specs?.AnalogOutputCount ?? 0)
|
||||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.OutputAnalog, i));
|
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.OutputAnalog, i))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
DigitalOutputs = Enumerable
|
DigitalOutputs = Enumerable
|
||||||
.Range(0, specs?.DigitalOutputCount ?? 0)
|
.Range(0, specs?.DigitalOutputCount ?? 0)
|
||||||
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.OutputDigital, i));
|
.Select(i => new InputOutputViewModel(this, orchestrator, MassiveKnobActionType.OutputDigital, i))
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Visibility AnalogInputVisibility => specs.HasValue && specs.Value.AnalogInputCount > 0
|
public Visibility AnalogInputVisibility => specs.HasValue && specs.Value.AnalogInputCount > 0
|
||||||
? Visibility.Visible
|
? Visibility.Visible
|
||||||
: Visibility.Collapsed;
|
: Visibility.Collapsed;
|
||||||
@ -213,11 +220,12 @@ namespace MassiveKnob.ViewModel
|
|||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public IList<LoggingLevelViewModel> LoggingLevels { get; }
|
public IList<LoggingLevelViewModel> LoggingLevels { get; }
|
||||||
|
|
||||||
private LoggingLevelViewModel selectedLoggingLevel;
|
private LoggingLevelViewModel selectedLoggingLevel;
|
||||||
|
|
||||||
public LoggingLevelViewModel SelectedLoggingLevel
|
public LoggingLevelViewModel SelectedLoggingLevel
|
||||||
{
|
{
|
||||||
get => selectedLoggingLevel;
|
get => selectedLoggingLevel;
|
||||||
@ -225,7 +233,7 @@ namespace MassiveKnob.ViewModel
|
|||||||
{
|
{
|
||||||
if (value == selectedLoggingLevel)
|
if (value == selectedLoggingLevel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
selectedLoggingLevel = value;
|
selectedLoggingLevel = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
|
||||||
@ -235,6 +243,7 @@ namespace MassiveKnob.ViewModel
|
|||||||
|
|
||||||
|
|
||||||
private bool loggingEnabled;
|
private bool loggingEnabled;
|
||||||
|
|
||||||
public bool LoggingEnabled
|
public bool LoggingEnabled
|
||||||
{
|
{
|
||||||
get => loggingEnabled;
|
get => loggingEnabled;
|
||||||
@ -245,14 +254,16 @@ namespace MassiveKnob.ViewModel
|
|||||||
|
|
||||||
loggingEnabled = value;
|
loggingEnabled = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
|
||||||
ApplyLoggingSettings();
|
ApplyLoggingSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO (code quality) do not hardcode path here
|
// TODO (code quality) do not hardcode path here
|
||||||
public string LoggingOutputPath { get; } = string.Format(Strings.LoggingOutputPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"MassiveKnob", @"Logs"));
|
public string LoggingOutputPath { get; } = string.Format(Strings.LoggingOutputPath,
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"MassiveKnob",
|
||||||
|
@"Logs"));
|
||||||
|
|
||||||
|
|
||||||
private bool runAtStartup;
|
private bool runAtStartup;
|
||||||
@ -270,6 +281,55 @@ namespace MassiveKnob.ViewModel
|
|||||||
ApplyRunAtStartup();
|
ApplyRunAtStartup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string ConnectionStatusText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (orchestrator == null)
|
||||||
|
return "Design-time";
|
||||||
|
|
||||||
|
switch (orchestrator.DeviceStatus)
|
||||||
|
{
|
||||||
|
case MassiveKnobDeviceStatus.Disconnected:
|
||||||
|
return Strings.DeviceStatusDisconnected;
|
||||||
|
|
||||||
|
case MassiveKnobDeviceStatus.Connecting:
|
||||||
|
return Strings.DeviceStatusConnecting;
|
||||||
|
|
||||||
|
case MassiveKnobDeviceStatus.Connected:
|
||||||
|
return Strings.DeviceStatusConnected;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Brush ConnectionStatusColor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (orchestrator == null)
|
||||||
|
return Brushes.Fuchsia;
|
||||||
|
|
||||||
|
switch (orchestrator.DeviceStatus)
|
||||||
|
{
|
||||||
|
case MassiveKnobDeviceStatus.Disconnected:
|
||||||
|
return Brushes.DarkRed;
|
||||||
|
|
||||||
|
case MassiveKnobDeviceStatus.Connecting:
|
||||||
|
return Brushes.Orange;
|
||||||
|
|
||||||
|
case MassiveKnobDeviceStatus.Connected:
|
||||||
|
return Brushes.ForestGreen;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// ReSharper restore UnusedMember.Global
|
// ReSharper restore UnusedMember.Global
|
||||||
|
|
||||||
|
|
||||||
@ -288,11 +348,17 @@ namespace MassiveKnob.ViewModel
|
|||||||
|
|
||||||
SelectedMenuItem = activeMenuItem;
|
SelectedMenuItem = activeMenuItem;
|
||||||
|
|
||||||
orchestrator.ActiveDeviceSubject.Subscribe(info => { Specs = info.Specs; });
|
activeDeviceSubscription = orchestrator.ActiveDeviceSubject.Subscribe(info => { Specs = info.Specs; });
|
||||||
|
deviceStatusSubscription = orchestrator.DeviceStatusSubject.Subscribe(status =>
|
||||||
|
{
|
||||||
|
OnDependantPropertyChanged(nameof(ConnectionStatusColor));
|
||||||
|
OnDependantPropertyChanged(nameof(ConnectionStatusText));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Devices = pluginManager.GetDevicePlugins()
|
Devices = pluginManager.GetDevicePlugins()
|
||||||
.SelectMany(dp => dp.Devices.Select(d => new DeviceViewModel(dp, d)))
|
.SelectMany(dp => dp.Devices.Select(d => new DeviceViewModel(dp, d)))
|
||||||
|
.OrderBy(d => d.Name.ToLower())
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var allActions = new List<ActionViewModel>
|
var allActions = new List<ActionViewModel>
|
||||||
@ -302,7 +368,8 @@ namespace MassiveKnob.ViewModel
|
|||||||
|
|
||||||
allActions.AddRange(
|
allActions.AddRange(
|
||||||
pluginManager.GetActionPlugins()
|
pluginManager.GetActionPlugins()
|
||||||
.SelectMany(ap => ap.Actions.Select(a => new ActionViewModel(ap, a))));
|
.SelectMany(ap => ap.Actions.Select(a => new ActionViewModel(ap, a)))
|
||||||
|
.OrderBy(a => a.Name.ToLower()));
|
||||||
|
|
||||||
Actions = allActions;
|
Actions = allActions;
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 5d259cdaee942029487e37a02e9a32ed9833d80c
|
Subproject commit e0e17e56feca7987a567a324132f785f1548a33f
|
Loading…
Reference in New Issue
Block a user