Skip to content
Snippets Groups Projects
Commit 69a65ddd authored by Paul-Winpenny's avatar Paul-Winpenny
Browse files

Merge branch 'app-bluetooth-connection'

parents 75b1662f 82c9b141
No related branches found
No related tags found
No related merge requests found
Showing
with 361 additions and 76 deletions
......@@ -12,5 +12,19 @@
<StyleSheet Source="/Resources/Styles/appstyle.css" />
</ResourceDictionary>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="FontFamily" Value="Cabal" />
<Setter Property="FontSize" Value="16" />
<Setter Property="TextColor" Value="#E8EDF1" />
<Setter Property="Margin" Value="10" />
</Style>
<Style TargetType="Button">
<Setter Property="FontFamily" Value="Cabal" />
<Setter Property="FontSize" Value="16" />
<Setter Property="TextColor" Value="#E8EDF1" />
<Setter Property="Margin" Value="10" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
using Microsoft.Extensions.Logging;
namespace RobobinApp;
using Plugin.BLE;
using Plugin.BLE.Abstractions.Contracts;
using RobobinApp.Networking;
public partial class App : Application
namespace RobobinApp
{
public App()
public partial class App : Application
{
InitializeComponent();
ConfigureLogging();
MainPage = new AppShell();
}
public static IBluetoothLE BluetoothLE { get; private set; }
public static IAdapter BluetoothAdapter { get; private set; }
private WifiManager _wifiManager;
public App()
{
InitializeComponent();
ConfigureLogging();
InitializeBluetoothAdapter();
_wifiManager = new WifiManager();
Task.Run(() => _wifiManager.StartListening());
MainPage = new AppShell();
}
protected override Window CreateWindow(IActivationState activationState)
{
var window = base.CreateWindow(activationState);
window.MinimumWidth = 800;
window.MinimumHeight = 650;
return window;
}
private void ConfigureLogging()
{
// Create a LoggerFactory
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole()
.AddDebug();
.AddConsole()
.AddDebug();
});
// Store the logger factory for later use
Logger = loggerFactory.CreateLogger<App>();
}
private void InitializeBluetoothAdapter()
{
BluetoothLE = CrossBluetoothLE.Current;
BluetoothAdapter = BluetoothLE.Adapter;
}
public ILogger<App> Logger { get; private set; }
}
}
......@@ -3,6 +3,4 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:RobobinApp.Views"
x:Class="RobobinApp.AppShell">
<ShellContent ContentTemplate="{DataTemplate views:MainPage}" />
</Shell>
\ No newline at end of file
</Shell>
namespace RobobinApp;
using Microsoft.Maui.Controls;
using RobobinApp.Views;
public partial class AppShell : Shell
namespace RobobinApp
{
public AppShell()
{
InitializeComponent();
}
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
// Conditionally add ShellContent based on the platform
#if WINDOWS
Items.Add(new ShellContent
{
ContentTemplate = new DataTemplate(typeof(MainPage))
});
#elif ANDROID
Items.Add(new ShellContent
{
ContentTemplate = new DataTemplate(typeof(MainPage_Android))
});
#else
// Fallback if you need it for other platforms
Items.Add(new ShellContent
{
ContentTemplate = new DataTemplate(typeof(MainPage_Default))
});
#endif
}
}
}
......@@ -16,6 +16,8 @@ public static class MauiProgram
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
// fonts.AddFont("Cabal-w5j3.tff", "Atop");
fonts.AddFont("Cabal-w5j3.tff", "Cabal");
});
......
namespace Robobin.Models
{
public class Node
{
public string Name { get; set; }
public float X { get; set; }
public float Y { get; set; }
public List<Edge> Edges { get; set; } // Add edges to keep track of connections
public Node(string name, float x, float y)
{
Name = name;
X = x;
Y = y;
Edges = new List<Edge>(); // Initialize the edges list
}
}
public class Edge
{
public Node TargetNode { get; set; }
public float Cost { get; set; } // Cost to traverse this edge
public Edge(Node targetNode, float cost)
{
TargetNode = targetNode;
Cost = cost;
}
}
public class Graph
{
public string Name { get; set; }
public List<Node> Nodes { get; set; }
public (float magnitude, float angle)[,] AdjacencyMatrix { get; set; }
public Graph(string name, List<Node> nodes, (float, float)[,] adjacencyMatrix)
{
Name = name;
Nodes = nodes;
AdjacencyMatrix = adjacencyMatrix;
BuildEdges(); // Create edges based on the adjacency matrix
}
public Graph(List<Node> nodes, (float, float)[,] adjacencyMatrix)
{
Name = "Graph";
Nodes = nodes;
AdjacencyMatrix = adjacencyMatrix;
BuildEdges(); // Create edges based on the adjacency matrix
}
private void BuildEdges()
{
for (int i = 0; i < Nodes.Count; i++)
{
for (int j = 0; j < Nodes.Count; j++)
{
if (AdjacencyMatrix[i, j].magnitude > 0) // Check if there's a connection
{
float angle = AdjacencyMatrix[i, j].angle;
float x = Nodes[j].X; // Target node x
float y = Nodes[j].Y; // Target node y
Nodes[i].Edges.Add(new Edge(Nodes[j], AdjacencyMatrix[i, j].magnitude));
}
}
}
}
}
}
......@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Robobin.Interfaces
namespace RobobinApp.Networking
{
public class BluetoothLEService : IBluetoothLEService
{
......
......@@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Robobin.Interfaces
namespace RobobinApp.Networking
{
public interface IBluetoothLEService
{
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RobobinApp.Networking
{
public interface IWifiService
{
Task StartListening();
void SendConnectMessage(string ipAddress);
}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
namespace RobobinApp.Networking
{
public class WifiManager : IWifiService
{
private UdpClient _udpClient;
private const int BroadcastPort = 5005;
private bool _isConnected = false; // Flag to indicate connection status
private CancellationTokenSource _cancellationTokenSource; // For stopping the UDP listener
public WifiManager()
{
_udpClient = new UdpClient(BroadcastPort);
_cancellationTokenSource = new CancellationTokenSource();
}
public async Task StartListening()
{
while (!_isConnected) // Continue listening until connected
{
Debug.WriteLine("Waiting for broadcast...");
var result = await _udpClient.ReceiveAsync();
string message = Encoding.ASCII.GetString(result.Buffer);
if (message == "ROBOBIN_PRESENT")
{
Debug.WriteLine("Detected Robobin presence from: " + result.RemoteEndPoint);
SendConnectMessage(result.RemoteEndPoint.Address.ToString());
}
}
// Stop listening if connected
Debug.WriteLine("Stopping UDP listener.");
_udpClient.Close();
}
public void SendConnectMessage(string ipAddress)
{
if (_isConnected)
{
Debug.WriteLine("Already connected. No need to send another connect message.");
return;
}
var endPoint = new IPEndPoint(IPAddress.Parse(ipAddress), 5006);
byte[] message = Encoding.ASCII.GetBytes("CONNECT");
_udpClient.Send(message, message.Length, endPoint);
Debug.WriteLine($"Sent connect message to: {ipAddress}");
Task.Run(() => ConnectToTcpServer(endPoint));
}
public async Task SendPingMessage(TcpClient tcpClient)
{
if (!_isConnected)
{
Debug.WriteLine("Not connected. Cannot send ping message.");
return;
}
try
{
NetworkStream stream = tcpClient.GetStream();
byte[] pingMessage = Encoding.ASCII.GetBytes("PING");
await stream.WriteAsync(pingMessage, 0, pingMessage.Length);
Debug.WriteLine("Sent PING message to Robobin.");
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to send PING message: {ex.Message}");
}
}
private async Task ConnectToTcpServer(IPEndPoint endPoint)
{
using (TcpClient tcpClient = new TcpClient())
{
try
{
await tcpClient.ConnectAsync(endPoint.Address, endPoint.Port);
Debug.WriteLine("Connected to Robobin via TCP.");
_isConnected = true;
// Keep the connection open to send PING messages periodically
while (_isConnected)
{
await SendPingMessage(tcpClient);
await Task.Delay(5000); // Send a ping every 5 seconds
}
}
catch (Exception ex)
{
Debug.WriteLine($"TCP connection failed: {ex.Message}");
}
}
_cancellationTokenSource.Cancel(); // Cancel the token to stop the UDP listener
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true" android:label="RoboBin"></application>
<application android:allowBackup="true" android:supportsRtl="true" android:label="RoboBin"></application>
<!-- Required permissions -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
......@@ -10,6 +10,4 @@
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
</manifest>
\ No newline at end of file
<?xml version="1.0" ?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 48 48" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>.cls-1{fill:#55aae1;}.cls-2{opacity:0.35;}</style>
</defs>
<title/>
<path class="cls-1" d="M36,44H12a3,3,0,0,1-3-3V12a1,1,0,0,1,1-1H38a1,1,0,0,1,1,1V41A3,3,0,0,1,36,44ZM11,13V41a1,1,0,0,0,1,1H36a1,1,0,0,0,1-1V13Z"/>
<g class="cls-2">
<path class="cls-1" d="M35,12V38a2,2,0,0,1-2,2H10v1a2,2,0,0,0,2,2H36a2,2,0,0,0,2-2V12Z"/>
</g>
<path class="cls-1" d="M43,13H5a1,1,0,0,1,0-2H43a1,1,0,0,1,0,2Z"/>
<path class="cls-1" d="M17,35a1,1,0,0,1-1-1V20a1,1,0,0,1,2,0V34A1,1,0,0,1,17,35Z"/>
<path class="cls-1" d="M31,35a1,1,0,0,1-1-1V20a1,1,0,0,1,2,0V34A1,1,0,0,1,31,35Z"/>
<path class="cls-1" d="M24,37a1,1,0,0,1-1-1V18a1,1,0,0,1,2,0V36A1,1,0,0,1,24,37Z"/>
<path class="cls-1" d="M33,13H15a1,1,0,0,1-1-1V7a3,3,0,0,1,3-3H31a3,3,0,0,1,3,3v5A1,1,0,0,1,33,13ZM16,11H32V7a1,1,0,0,0-1-1H17a1,1,0,0,0-1,1Z"/>
</svg>
\ No newline at end of file
App/RobobinApp/Platforms/Android/Resources/mipmap/robobinlogo1.png

19.3 KiB

......@@ -6,41 +6,45 @@
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="8646FF59-A3E1-4276-A8EA-36FBC95CCEAD" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="8646FF59-A3E1-4276-A8EA-36FBC95CCEAD" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="bluetooth" />
<rescap:Capability Name="bluetoothGenericAttributeProfile" />
<rescap:Capability Name="location" />
</Capabilities>
</Package>
App/RobobinApp/Resources/AppIcon/RobobinLogo.png

19.3 KiB

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>
\ No newline at end of file
File added
<?xml version="1.0" ?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 48 48" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>.cls-1{fill:#55aae1;}.cls-2{opacity:0.35;}</style>
</defs>
<title/>
<path class="cls-1" d="M36,44H12a3,3,0,0,1-3-3V12a1,1,0,0,1,1-1H38a1,1,0,0,1,1,1V41A3,3,0,0,1,36,44ZM11,13V41a1,1,0,0,0,1,1H36a1,1,0,0,0,1-1V13Z"/>
<g class="cls-2">
<path class="cls-1" d="M35,12V38a2,2,0,0,1-2,2H10v1a2,2,0,0,0,2,2H36a2,2,0,0,0,2-2V12Z"/>
</g>
<path class="cls-1" d="M43,13H5a1,1,0,0,1,0-2H43a1,1,0,0,1,0,2Z"/>
<path class="cls-1" d="M17,35a1,1,0,0,1-1-1V20a1,1,0,0,1,2,0V34A1,1,0,0,1,17,35Z"/>
<path class="cls-1" d="M31,35a1,1,0,0,1-1-1V20a1,1,0,0,1,2,0V34A1,1,0,0,1,31,35Z"/>
<path class="cls-1" d="M24,37a1,1,0,0,1-1-1V18a1,1,0,0,1,2,0V36A1,1,0,0,1,24,37Z"/>
<path class="cls-1" d="M33,13H15a1,1,0,0,1-1-1V7a3,3,0,0,1,3-3H31a3,3,0,0,1,3,3v5A1,1,0,0,1,33,13ZM16,11H32V7a1,1,0,0,0-1-1H17a1,1,0,0,0-1,1Z"/>
</svg>
\ No newline at end of file
......@@ -5,7 +5,7 @@
<Style x:Key="TopBarButtonStyle" TargetType="Button">
<Setter Property="BackgroundColor" Value="#E4E4E4"/>
<Setter Property="TextColor" Value="#333333"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="CornerRadius" Value="5"/>
<Setter Property="BorderWidth" Value="0.5"/>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment