diff --git a/BrickController2/BrickController2/DeviceManagement/Lego/RemoteControl.cs b/BrickController2/BrickController2/DeviceManagement/Lego/RemoteControl.cs
index a8f8fb3e..c125b773 100644
--- a/BrickController2/BrickController2/DeviceManagement/Lego/RemoteControl.cs
+++ b/BrickController2/BrickController2/DeviceManagement/Lego/RemoteControl.cs
@@ -15,13 +15,13 @@ namespace BrickController2.DeviceManagement.Lego;
///
/// Represents a LEGO® Powered Up 88010 Remote Control
///
-internal class RemoteControl : BluetoothDevice
+internal class RemoteControl : BluetoothDevice, IDynamicInputDevice
{
private const string ENABLED_SETTING_NAME = "RemoteControlEnabled";
private const bool DEFAULT_ENABLED = false;
private IGattCharacteristic? _characteristic;
- private InputDeviceBase? _inputController;
+ private IInputDeviceConnector? _inputDeviceConnector;
public RemoteControl(string name, string address, IEnumerable settings, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
: base(name, address, deviceRepository, bleService)
@@ -41,14 +41,14 @@ public RemoteControl(string name, string address, IEnumerable sett
public override void SetOutput(int channel, float value) => throw new InvalidOperationException();
- internal void ConnectInputController(TController inputController) where TController : InputDeviceBase
+ public void ConnectInputController(IInputDeviceConnector inputController)
{
- _inputController = inputController;
+ _inputDeviceConnector = inputController;
}
- internal void DisconnectInputController()
+ public void DisconnectInputController()
{
- _inputController = default;
+ _inputDeviceConnector = default;
}
internal void ResetEvents() => RaiseButtonEvents(
@@ -156,16 +156,16 @@ private void OnButtonEvents(string plus, string stop, string minus, ReadOnlySpan
private void RaiseButtonEvents((string eventName, float value)[] buttonEvents)
{
- if (_inputController is null)
+ if (_inputDeviceConnector is null)
{
return;
}
var events = buttonEvents
- .Where(e => _inputController.HasValueChanged(e.eventName, e.value))
+ .Where(e => _inputDeviceConnector.HasValueChanged(e.eventName, e.value))
.ToDictionary(e => (InputDeviceEventType.Button, e.eventName), e => e.value);
- _inputController.RaiseEvent(events);
+ _inputDeviceConnector.RaiseEvent(events);
}
private static float GetButtonValue(byte flag) => flag != 0 ? BUTTON_PRESSED : BUTTON_RELEASED;
diff --git a/BrickController2/BrickController2/PlatformServices/InputDevice/IDynamicInputDevice.cs b/BrickController2/BrickController2/PlatformServices/InputDevice/IDynamicInputDevice.cs
new file mode 100644
index 00000000..3b84c938
--- /dev/null
+++ b/BrickController2/BrickController2/PlatformServices/InputDevice/IDynamicInputDevice.cs
@@ -0,0 +1,7 @@
+namespace BrickController2.PlatformServices.InputDevice;
+
+internal interface IDynamicInputDevice
+{
+ void ConnectInputController(IInputDeviceConnector controller);
+ void DisconnectInputController();
+}
diff --git a/BrickController2/BrickController2/PlatformServices/InputDevice/IInputDeviceConnector.cs b/BrickController2/BrickController2/PlatformServices/InputDevice/IInputDeviceConnector.cs
new file mode 100644
index 00000000..67bc615e
--- /dev/null
+++ b/BrickController2/BrickController2/PlatformServices/InputDevice/IInputDeviceConnector.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace BrickController2.PlatformServices.InputDevice;
+
+internal interface IInputDeviceConnector
+{
+ internal bool HasValueChanged(string axisName, float value);
+
+ internal void RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events);
+}
diff --git a/BrickController2/BrickController2/PlatformServices/InputDevice/InputDeviceBase.cs b/BrickController2/BrickController2/PlatformServices/InputDevice/InputDeviceBase.cs
index eb213228..822485d9 100644
--- a/BrickController2/BrickController2/PlatformServices/InputDevice/InputDeviceBase.cs
+++ b/BrickController2/BrickController2/PlatformServices/InputDevice/InputDeviceBase.cs
@@ -9,7 +9,7 @@ namespace BrickController2.PlatformServices.InputDevice;
/// abstract base class for input devices
///
/// Type of native instance of inputdevice device
-public abstract class InputDeviceBase : IInputDevice
+public abstract class InputDeviceBase : IInputDevice, IInputDeviceConnector
where TInputDeviceDevice : class
{
/// stored last value per axis to detect changes
@@ -66,7 +66,7 @@ public virtual void Stop()
protected bool ContainsAxisValue(string axisName) => _lastAxisValues.ContainsKey(axisName);
- protected internal bool HasValueChanged(string axisName, float value)
+ public bool HasValueChanged(string axisName, float value)
{
// get last reported value or the default one
_lastAxisValues.TryGetValue(axisName, out float lastValue);
@@ -80,7 +80,7 @@ protected internal bool HasValueChanged(string axisName, float value)
return true;
}
- protected internal void RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
+ public void RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
{
if (!events.Any())
{
diff --git a/BrickController2/BrickController2/UI/Pages/DevicePage.xaml b/BrickController2/BrickController2/UI/Pages/DevicePage.xaml
index a65f72db..c5475454 100644
--- a/BrickController2/BrickController2/UI/Pages/DevicePage.xaml
+++ b/BrickController2/BrickController2/UI/Pages/DevicePage.xaml
@@ -133,6 +133,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs b/BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs
index a4515b61..964eed4b 100644
--- a/BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs
+++ b/BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -8,19 +9,22 @@
using BrickController2.CreationManagement;
using BrickController2.DeviceManagement;
using BrickController2.Helpers;
+using BrickController2.PlatformServices.InputDevice;
using BrickController2.UI.Commands;
using BrickController2.UI.Services.Navigation;
using BrickController2.UI.Services.Dialog;
using BrickController2.UI.Services.Translation;
using Device = BrickController2.DeviceManagement.Device;
using static BrickController2.CreationManagement.ControllerDefaults;
+using static BrickController2.PlatformServices.InputDevice.InputDevices;
namespace BrickController2.UI.ViewModels
{
- public class DevicePageViewModel : PageViewModelBase
+ public class DevicePageViewModel : PageViewModelBase, IInputDeviceConnector
{
private readonly IDeviceManager _deviceManager;
private readonly IDialogService _dialogService;
+ private readonly Dictionary _lastAxisValues = [];
private CancellationTokenSource? _connectionTokenSource;
private Task? _connectionTask;
@@ -32,6 +36,7 @@ public DevicePageViewModel(
ITranslationService translationService,
IDeviceManager deviceManager,
IDialogService dialogService,
+ IServiceProvider serviceProvider,
NavigationParameters parameters)
: base(navigationService, translationService)
{
@@ -57,6 +62,7 @@ public DevicePageViewModel(
}
public Device Device { get; }
+ internal IDynamicInputDevice? InputDevice => Device as IDynamicInputDevice;
public bool IsBuWizzDevice => Device.DeviceType == DeviceType.BuWizz;
public bool IsBuWizz2Device => Device.DeviceType == DeviceType.BuWizz2;
public bool CanBePowerSource => Device.CanBePowerSource;
@@ -70,6 +76,8 @@ public DevicePageViewModel(
public bool IsAdvertisingDevice => Device is BluetoothAdvertisingDevice;
+ public bool IsInputDevice => Device is IDynamicInputDevice;
+
public bool IsServoOrStepperSupported => DeviceOutputs.Any(x => x.IsServoOrStepperSupported);
public ICommand RenameCommand { get; }
@@ -84,6 +92,8 @@ public DevicePageViewModel(
public IEnumerable DeviceOutputs { get; }
+ public ObservableCollection InputEventList { get; } = new();
+
public override async void OnAppearing()
{
_isDisappearing = false;
@@ -104,6 +114,9 @@ await _dialogService.ShowMessageBoxAsync(
}
}
+ // connect input device if available
+ InputDevice?.ConnectInputController(this);
+
_connectionTokenSource = new CancellationTokenSource();
_connectionTask = ConnectAsync();
}
@@ -112,6 +125,9 @@ public override async void OnDisappearing()
{
base.OnDisappearing();
+ // disconnect input device if available
+ InputDevice?.DisconnectInputController();
+
if (_connectionTokenSource is not null && _connectionTask is not null)
{
_connectionTokenSource?.Cancel();
@@ -121,6 +137,42 @@ public override async void OnDisappearing()
await Device.DisconnectAsync();
}
+
+ bool IInputDeviceConnector.HasValueChanged(string axisName, float value)
+ {
+ // get last reported value or the default one
+ _lastAxisValues.TryGetValue(axisName, out float lastValue);
+ // skip value if there is no change
+ if (AreAlmostEqual(value, lastValue))
+ {
+ return false;
+ }
+ // persist
+ _lastAxisValues[axisName] = value;
+ return true;
+ }
+
+ void IInputDeviceConnector.RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
+ {
+ foreach (var inputDeviceEvent in events)
+ {
+ var item = InputEventList.FirstOrDefault(x => x.EventCode == inputDeviceEvent.Key.Item2);
+
+ if (AXIS_DELTA_VALUE >= Math.Abs(inputDeviceEvent.Value))
+ {
+ InputEventList.Remove(item);
+ }
+ else if (item != null)
+ {
+ item.Value = inputDeviceEvent.Value;
+ }
+ else
+ {
+ InputEventList.Add(new InputDeviceEventViewModel(inputDeviceEvent.Key.Item1, inputDeviceEvent.Key.Item2, inputDeviceEvent.Value));
+ }
+ }
+ }
+
private async Task RenameDeviceAsync()
{
try