Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ namespace BrickController2.DeviceManagement.Lego;
/// <summary>
/// Represents a LEGO® Powered Up 88010 Remote Control
/// </summary>
internal class RemoteControl : WirelessProtocolBasedDevice, IDeviceType<RemoteControl>
internal class RemoteControl : WirelessProtocolBasedDevice, IDeviceType<RemoteControl>, IDynamicInputDevice
{
private const string ENABLED_SETTING_NAME = "RemoteControlEnabled";
private const bool DEFAULT_ENABLED = false;

private InputDeviceBase<RemoteControl>? _inputController;
private IInputDeviceConnector? _inputDeviceConnector;

public RemoteControl(string name, string address, IEnumerable<NamedSetting> settings, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
: base(name, address, deviceRepository, bleService)
Expand All @@ -39,14 +39,14 @@ public RemoteControl(string name, string address, IEnumerable<NamedSetting> sett

public override void SetOutput(int channel, float value) => throw new InvalidOperationException();

internal void ConnectInputController<TController>(TController inputController) where TController : InputDeviceBase<RemoteControl>
public void ConnectInputController(IInputDeviceConnector inputController)
{
_inputController = inputController;
_inputDeviceConnector = inputController;
}

internal void DisconnectInputController()
public void DisconnectInputController()
{
_inputController = default;
_inputDeviceConnector = default;
}
Comment thread
vicocz marked this conversation as resolved.

internal void ResetEvents() => RaiseButtonEvents(
Expand Down Expand Up @@ -123,16 +123,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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace BrickController2.PlatformServices.InputDevice;

internal interface IDynamicInputDevice
{
void ConnectInputController(IInputDeviceConnector controller);
void DisconnectInputController();
}
Original file line number Diff line number Diff line change
@@ -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);
}
Comment thread
vicocz marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BrickController2.PlatformServices.InputDevice;
/// abstract base class for input devices
/// </summary>
/// <typeparam name="TInputDeviceDevice">Type of native instance of inputdevice device</typeparam>
public abstract class InputDeviceBase<TInputDeviceDevice> : IInputDevice
public abstract class InputDeviceBase<TInputDeviceDevice> : IInputDevice, IInputDeviceConnector
where TInputDeviceDevice : class
{
/// <summary>stored last value per axis to detect changes</summary>
Expand Down Expand Up @@ -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);
Expand All @@ -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())
{
Expand Down
21 changes: 21 additions & 0 deletions BrickController2/BrickController2/UI/Pages/DevicePage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,27 @@
</VerticalStackLayout>
</VerticalStackLayout>

<!-- Input events -->
<VerticalStackLayout IsVisible="{Binding IsInputDevice}">
<CollectionView ItemsSource="{Binding InputEventList}" SelectionMode="None" ItemsLayout="VerticalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<VerticalStackLayout Padding="{OnIdiom '10,5,10,5', Desktop='10,0,10,0'}">
<HorizontalStackLayout>
<Label Text="{Binding EventCode}" FontSize="Large" FontAttributes="Bold"/>
<Label Text=":" FontSize="Large" Margin="5,0,5,0"/>
<Label Text="{Binding Value}" FontSize="Large"/>
</HorizontalStackLayout>
<Label Text="{Binding EventType}" FontSize="Medium"/>
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<Label Text="{extensions:Translate PressButtonsOrMoveJoys}" Style="{StaticResource CollectionViewEmptyLabelStyle}"/>
</CollectionView.EmptyView>
</CollectionView>
</VerticalStackLayout>

</VerticalStackLayout>
</ScrollView>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -8,19 +10,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 ConcurrentDictionary<string, float> _lastAxisValues = [];

private CancellationTokenSource? _connectionTokenSource;
private Task? _connectionTask;
Expand Down Expand Up @@ -83,6 +88,12 @@ public DevicePageViewModel(

public IEnumerable<DeviceOutputViewModel> DeviceOutputs { get; }

public ObservableCollection<InputDeviceEventViewModel> InputEventList { get; } = [];

public bool IsInputDevice => InputDevice is not null;

internal IDynamicInputDevice? InputDevice => Device as IDynamicInputDevice;

public override async void OnAppearing()
{
_isDisappearing = false;
Expand All @@ -103,6 +114,10 @@ await _dialogService.ShowMessageBoxAsync(
}
}

// connect input device if available
ResetInputEvents();
InputDevice?.ConnectInputController(this);

_connectionTokenSource = new CancellationTokenSource();
_connectionTask = ConnectAsync();
}
Expand All @@ -112,9 +127,58 @@ public override async void OnDisappearing()
_isDisappearing = true;
base.OnDisappearing();

// disconnect input device if available
ResetInputEvents();
InputDevice?.DisconnectInputController();

await 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;
Comment thread
vicocz marked this conversation as resolved.
}

void IInputDeviceConnector.RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
{
foreach (var inputEvent in events)
{
var (eventType, eventCode) = inputEvent.Key;
var item = InputEventList.FirstOrDefault(x => x.EventCode == eventCode && x.EventType == eventType);

if (AXIS_DELTA_VALUE >= Math.Abs(inputEvent.Value))
{
if (item != null)
{
InputEventList.Remove(item);
}
}
else if(item is null)
{
InputEventList.Add(new InputDeviceEventViewModel(eventType, eventCode, inputEvent.Value));
}
else
{
item.Value = inputEvent.Value;
}
}
}
Comment on lines +151 to +174

private void ResetInputEvents()
{
_lastAxisValues.Clear();
InputEventList.Clear();
}
Comment thread
vicocz marked this conversation as resolved.
Comment on lines +176 to +180

private async Task DisconnectAsync()
{
if (_connectionTokenSource is not null && _connectionTask is not null)
Expand Down Expand Up @@ -346,6 +410,8 @@ await _dialogService.ShowMessageBoxAsync(

private void OnDeviceDisconnected(Device device)
{
// clear input events
ResetInputEvents();
// update command enablement
UpdateCommandsAvailability();
}
Comment on lines 411 to 417
Expand Down
Loading