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
7 changes: 7 additions & 0 deletions BrickController2.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{5AFB0DE9
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrickController2.Tools", "BrickController2\BrickController2.Tools\BrickController2.Tools.csproj", "{C127AB50-E104-2345-5239-13258E2B7474}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MCPSercer", "MCPSercer", "{12E3959D-03BB-49C4-B382-2188D5CCD111}"
ProjectSection(SolutionItems) = preProject
BrickController2\MCPServer\claude_desktop_config.json = BrickController2\MCPServer\claude_desktop_config.json
BrickController2\MCPServer\mcp_http_bridge.py = BrickController2\MCPServer\mcp_http_bridge.py
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -126,6 +132,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{7981CBBF-BB2F-4AF4-BEF3-3CF0C92DBB23} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{C127AB50-E104-2345-5239-13258E2B7474} = {5AFB0DE9-536E-4C5D-831A-421ADC3EF3D9}
{12E3959D-03BB-49C4-B382-2188D5CCD111} = {5AFB0DE9-536E-4C5D-831A-421ADC3EF3D9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {024A01F8-4022-4F9E-A006-AC2CABB04620}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<ItemGroup>
<PackageReference Include="Autofac" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Maui.Controls" />
<PackageReference Include="Microsoft.Maui.Controls" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" />
<PackageReference Include="Microsoft.Maui.Essentials" />
<ProjectReference Include="..\BrickController2\BrickController2.csproj" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
using BrickController2.Droid.PlatformServices.GameController;
using BrickController2.Droid.PlatformServices.Infrared;
using BrickController2.Droid.PlatformServices.Localization;
using BrickController2.Droid.PlatformServices.ModelContextProtocol;
using BrickController2.Droid.PlatformServices.Permission;
using BrickController2.Droid.PlatformServices.SharedFileStorage;
using BrickController2.PlatformServices.BluetoothLE;
using BrickController2.PlatformServices.GameController;
using BrickController2.PlatformServices.Infrared;
using BrickController2.PlatformServices.Localization;
using BrickController2.PlatformServices.ModelContextProtocol;
using BrickController2.PlatformServices.Permission;
using BrickController2.PlatformServices.SharedFileStorage;

Expand All @@ -32,6 +34,7 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType<CameraPermission>().As<ICameraPermission>().InstancePerDependency();
builder.RegisterType<MKPlatformService>().As<IMKPlatformService>().SingleInstance();
builder.RegisterType<CaDAPlatformService>().As<ICaDAPlatformService>().SingleInstance();
builder.RegisterType<McpServerService>().AsSelf().As<IMcpServerService>().SingleInstance();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
using Android.Views;
using Android.Hardware.Input;
using Android.Content;
using BrickController2.Droid.PlatformServices.ModelContextProtocol;
using BrickController2.PlatformServices.GameController;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using BrickController2.PlatformServices.ModelContextProtocol;

namespace BrickController2.Droid.PlatformServices.GameController
{
internal class GameControllerService : GameControllerServiceBase<GamepadController>
internal class GameControllerService : GameControllerServiceBase
{
private readonly InputManager _inputManager;
private readonly McpServerService _mcpServerService;

public GameControllerService(Context context, ILogger<GameControllerService> logger) :base(logger)
public GameControllerService(Context context,
McpServerService mcpServerService,
ILogger<GameControllerService> logger) :base(logger)
{
_inputManager = (InputManager)context.GetSystemService(Context.InputService)!;
_mcpServerService = mcpServerService;
}

public override bool IsControllerIdSupported => true;
Expand All @@ -36,9 +42,9 @@ internal void MainActivityOnInputDeviceAdded(int deviceId)
/// <param name="deviceId">deviceId of InputDevice</param>
internal void MainActivityOnInputDeviceRemoved(int deviceId)
{
if (TryRemove(x => x.Gamepad.Id == deviceId, out var controller))
if (TryRemove<GamepadController>(x => x.ControllerDevice.Id == deviceId, out var controller))
{
_logger.LogInformation("Gamepad has been removed DeviceId:{id}, ControllerId:{controllerId}",
_logger.LogInformation("ControllerDevice has been removed DeviceId:{id}, ControllerId:{controllerId}",
deviceId, controller.ControllerId);
}
}
Expand All @@ -64,14 +70,14 @@ internal void MainActivityOnInputDeviceChanged(int deviceId)
else if (controller != null)
{
// handle change - remove and then add it again
TryRemove(x => x.Gamepad.Id == deviceId, out _);
TryRemove<GamepadController>(x => x.ControllerDevice.Id == deviceId, out _);
}
AddGameControllerDevice(device);
}
}
else if (TryRemove(x => x.Gamepad.Id == deviceId, out var controller))
else if (TryRemove<GamepadController>(x => x.ControllerDevice.Id == deviceId, out var controller))
{
_logger.LogInformation("Gamepad has been removed DeviceId:{id}, ControllerId:{controllerId}",
_logger.LogInformation("ControllerDevice has been removed DeviceId:{id}, ControllerId:{controllerId}",
deviceId, controller.ControllerId);
}
}
Expand All @@ -98,6 +104,9 @@ internal bool OnGameControllerAxisEvent(MotionEvent e)

protected override void InitializeCurrentControllers()
{
// add McpServer
AddMcpServer();

// add any connected game controller
var deviceIds = _inputManager?.GetInputDeviceIds() ?? [];
foreach (int deviceId in deviceIds)
Expand All @@ -107,6 +116,46 @@ protected override void InitializeCurrentControllers()
AddGameControllerDevice(device);
}
}

_mcpServerService.McpServerAdded += McpServerAdded;
_mcpServerService.McpServerRemoved += McpServerRemoved; ;
}

protected override void RemoveAllControllers()
{
_mcpServerService.McpServerAdded -= McpServerAdded;
_mcpServerService.McpServerRemoved -= McpServerRemoved; ;

base.RemoveAllControllers();
}

private void McpServerRemoved(object? sender, McpServer e)
{
lock (_lockObject)
{
if (TryRemove<McpServerController>(x => x.ControllerDevice is McpServer, out var controller))
{
_logger.LogInformation("McpServer has been removed");
}
}
}

private void McpServerAdded(object? sender, McpServer e)
{
AddMcpServer();
}

private void AddMcpServer()
{
if (_mcpServerService?.Server != null)
{
lock (_lockObject)
{
var newController = new McpServerController(this, _mcpServerService.Server);

AddController(newController);
}
}
}

/// <summary>
Expand All @@ -122,7 +171,7 @@ private void AddGameControllerDevice(InputDevice gamepad)
}

private bool TryGetControllerByDeviceId(int deviceId, [MaybeNullWhen(false)] out GamepadController controller)
=> TryGetController(x => x.Gamepad.Id == deviceId, out controller);
=> TryGetController(x => x.ControllerDevice.Id == deviceId, out controller);

private static bool TryGetGamepadDevice(int deviceId, [MaybeNullWhen(false)] out InputDevice device)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Android.Views;
using BrickController2.Droid.Extensions;
using BrickController2.PlatformServices.GameController;
using System;
using System.Collections.Generic;
Expand All @@ -25,11 +24,8 @@ public GamepadController(GameControllerService service, InputDevice gamePad)
{
// initialize properties
Name = GetDisplayName(gamePad);
VendorId = gamePad.VendorId;
ProductId = gamePad.ProductId;
ControllerNumber = gamePad.ControllerNumber;
ControllerId = GetControllerIdFromNumber(gamePad.ControllerNumber);
UniquePersistantDeviceId = gamePad.GetUniquePersistentDeviceId();
}

internal bool OnButtonEvent(KeyEvent e, float buttonValue)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BrickController2.Droid.PlatformServices.GameController;
using BrickController2.PlatformServices.GameController;
using BrickController2.PlatformServices.ModelContextProtocol;
using System.Collections.Generic;
using System.Linq;

namespace BrickController2.Droid.PlatformServices.ModelContextProtocol
{
internal class McpServerController : GamepadControllerBase<McpServer>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="service">reference to GameControllerService</param>
/// <param name="mcpServer">reference to InputDevice</param>
public McpServerController(GameControllerService service, McpServer mcpServer)
: base(service, mcpServer)
{
ControllerDevice.ChannelStatesUpdated += mcpServer_ChannelStatesUpdated;

// initialize properties
Name = "McpServer";
ControllerNumber = -1;
ControllerId = "McpServer";
}

private void mcpServer_ChannelStatesUpdated(List<McpServer.ChannelValue> obj)
{
// grab all changed axis event
var currentEvents = obj
.Where(x => HasValueChanged($"{x.Channel}", (float)x.Value))
.ToDictionary(x => (GameControllerEventType.Axis, $"{x.Channel}"), x => (float)x.Value);

RaiseEvent(currentEvents);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using BrickController2.PlatformServices.ModelContextProtocol;
using BrickController2.UI.Services.Preferences;
using System;
using System.Threading.Tasks;

namespace BrickController2.Droid.PlatformServices.ModelContextProtocol;

public class McpServerService : IMcpServerService, IDisposable
{
private readonly object _Lock = new object();
private readonly IPreferencesService _preferencesService;
private McpServer? _server;

public McpServerService(IPreferencesService preferencesService)
{
_preferencesService = preferencesService;
_server = null;

ApplyMcpServer();
}

public event EventHandler<McpServer>? McpServerAdded;
public event EventHandler<McpServer>? McpServerRemoved;

public bool IsMcpServerAvailable => true;
public bool IsMcpServerEnabled
{
get => _preferencesService.Get("McpServerEnabled", false);
set
{
if (IsMcpServerEnabled != value)
{
_preferencesService.Set("McpServerEnabled", value);
ApplyMcpServer();
}
}
}
public int McpServerPort
{
get => _preferencesService.Get("McpServerPort", McpServerBase.PortDefault);

set
{
if (McpServerPort != value)
{
_preferencesService.Set("McpServerPort", value);
ApplyMcpServer();
}
}
}

public string McpServerAuthToken
{
get => _preferencesService.Get("McpServerAuthToken", McpServerBase.AuthTokenDefault);

set
{
if (McpServerAuthToken != value)
{
_preferencesService.Set("McpServerAuthToken", value);
ApplyMcpServer();
}
}
}

public McpServer? Server => _server;

public void Dispose()
{
lock (_Lock)
{
_server?.Dispose();
_server = null;
}
}

private void ApplyMcpServer()
{
lock (_Lock)
{
if (_server != null)
{
McpServerRemoved?.Invoke(this, _server);

_server?.Dispose();
_server = null;
}

if (IsMcpServerEnabled)
{
if (_server == null)
{
_server = new McpServer(McpServerPort, McpServerAuthToken);
Start();
McpServerAdded?.Invoke(this, _server);
}
}
}
}

public void Start()
{
// start non-blocking
Task.Run(() => _server?.StartAsync());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
<uses-permission android:name="android.permission.TRANSMIT_IR" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<application android:label="BrickController2.Android" android:icon="@mipmap/ic_launcher"></application>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
<ProjectReference Include="..\BrickController2\BrickController2.csproj" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<rescap:Capability Name="runFullTrust" />
<Capability Name="internetClient"/>
<uap:Capability Name="removableStorage"/>
<Capability Name="internetClientServer"/>
<DeviceCapability Name="location"/>
<DeviceCapability Name="bluetooth"/>
</Capabilities>
Expand Down
Loading