Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 debugging #571

Merged
merged 5 commits into from
May 25, 2024
Merged
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
2 changes: 1 addition & 1 deletion Source/v2/Meadow.Cli/Meadow.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<Authors>Wilderness Labs, Inc</Authors>
<Company>Wilderness Labs, Inc</Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>2.0.44.0</PackageVersion>
<PackageVersion>2.0.46.0</PackageVersion>
<Platforms>AnyCPU</Platforms>
<PackageProjectUrl>http://developer.wildernesslabs.co/Meadow/Meadow.CLI/</PackageProjectUrl>
<RepositoryUrl>https://github.com/WildernessLabs/Meadow.CLI</RepositoryUrl>
Expand Down
2 changes: 1 addition & 1 deletion Source/v2/Meadow.Cli/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace Meadow.CLI;

public static class Constants
{
public const string CLI_VERSION = "2.0.44.0";
public const string CLI_VERSION = "2.0.46.0";
}
28 changes: 9 additions & 19 deletions Source/v2/Meadow.Hcom/Connections/SerialConnection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Buffers;
using System.IO.Ports;
using System.Net;
using System.Security.Cryptography;

namespace Meadow.Hcom;
Expand All @@ -23,6 +22,7 @@ public partial class SerialConnection : ConnectionBase, IDisposable
private ConnectionState _state;
private readonly List<IConnectionListener> _listeners = new List<IConnectionListener>();
private readonly Queue<IRequest> _pendingCommands = new Queue<IRequest>();
private readonly AutoResetEvent _commandEvent = new AutoResetEvent(false);
private bool _maintainConnection;
private Thread? _connectionManager = null;
private readonly List<string> _textList = new List<string>();
Expand Down Expand Up @@ -77,7 +77,6 @@ private bool MaintainConnection
Name = "HCOM Connection Manager"
};
_connectionManager.Start();

}
}
}
Expand Down Expand Up @@ -210,6 +209,7 @@ public override void Detach()
var count = _messageCount;

_pendingCommands.Enqueue(command);
_commandEvent.Set();

while (timeout-- > 0)
{
Expand Down Expand Up @@ -246,6 +246,7 @@ private void CommandManager()
{
while (!_isDisposed)
{
_commandEvent.WaitOne();
while (_pendingCommands.Count > 0)
{
Debug.WriteLine($"There are {_pendingCommands.Count} pending commands");
Expand All @@ -261,8 +262,6 @@ private void CommandManager()
// TODO: re-queue on fail?
}
}

Thread.Sleep(1000);
}
}

Expand Down Expand Up @@ -298,6 +297,7 @@ public void EnqueueRequest(IRequest command)
}

_pendingCommands.Enqueue(command);
_commandEvent.Set();
}

private void EncodeAndSendPacket(byte[] messageBytes, CancellationToken? cancellationToken = null)
Expand Down Expand Up @@ -1226,16 +1226,16 @@ public override async Task<DebuggingServer> StartDebuggingSession(int port, ILog
throw new DeviceNotFoundException();
}

var debuggingServer = new DebuggingServer(this, port, logger);

logger?.LogDebug("Tell the Debugging Server to Start Listening");
_ = debuggingServer.StartListening(cancellationToken);

logger?.LogDebug($"Start Debugging on port: {port}");
await Device.StartDebugging(port, logger, cancellationToken);

await WaitForMeadowAttach(cancellationToken);

var endpoint = new IPEndPoint(IPAddress.Loopback, port);
var debuggingServer = new DebuggingServer(this, Device, endpoint, logger);

logger?.LogDebug("Tell the Debugging Server to Start Listening");
await debuggingServer.StartListening(cancellationToken);
return debuggingServer;
}

Expand Down Expand Up @@ -1267,15 +1267,5 @@ public override async Task SendDebuggerData(byte[] debuggerData, uint userData,
_lastRequestConcluded = null;

EnqueueRequest(command);

var success = await WaitForResult(() =>
{
if (_lastRequestConcluded != null && _lastRequestConcluded == RequestType.HCOM_MDOW_REQUEST_RTC_SET_TIME_CMD)
{
return true;
}

return false;
}, cancellationToken);
}
}
185 changes: 185 additions & 0 deletions Source/v2/Meadow.Hcom/Debugging/DebuggingServer.ActiveClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using System.Buffers;
using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Security.Cryptography;

namespace Meadow.Hcom;

public partial class DebuggingServer
{
private class ActiveClient : IDisposable
{
private readonly IMeadowConnection _connection;
private readonly TcpClient _tcpClient;
private readonly NetworkStream _networkStream;
private readonly CancellationTokenSource _cts;
private readonly Task _receiveVsDebugDataTask;
private readonly Task _receiveMeadowDebugDataTask;
private readonly ILogger? _logger;
private bool _disposed;
private readonly BlockingCollection<byte[]> _debuggerMessages = new();
private readonly AutoResetEvent _vsDebugDataReady = new(false);

internal ActiveClient(IMeadowConnection connection, TcpClient tcpClient, ILogger? logger, CancellationToken? cancellationToken)
{
_cts = cancellationToken != null
? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken.Value)
: new CancellationTokenSource();

_logger = logger;
_connection = connection;
_tcpClient = tcpClient;
_networkStream = tcpClient.GetStream();

_logger?.LogDebug("Starting receive task");

_connection.DebuggerMessageReceived += MeadowConnection_DebuggerMessageReceived;

_receiveVsDebugDataTask = Task.Factory.StartNew(SendToMeadowAsync, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
_receiveMeadowDebugDataTask = Task.Factory.StartNew(SendToVisualStudio, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

private void MeadowConnection_DebuggerMessageReceived(object sender, byte[] e)
{
_debuggerMessages.Add(e);
_vsDebugDataReady.Set();
}

private const int RECEIVE_BUFFER_SIZE = 256;

private async Task SendToMeadowAsync()
{
try
{
using var md5 = MD5.Create();
var receiveBuffer = ArrayPool<byte>.Shared.Rent(RECEIVE_BUFFER_SIZE);
var meadowBuffer = Array.Empty<byte>();

while (!_cts.Token.IsCancellationRequested)
{
if (_networkStream != null && _networkStream.CanRead)
{
int bytesRead;
do
{
bytesRead = await _networkStream.ReadAsync(receiveBuffer, 0, receiveBuffer.Length, _cts.Token);

if (bytesRead == 0 || _cts.Token.IsCancellationRequested)
{
continue;
}

var destIndex = meadowBuffer.Length;
Array.Resize(ref meadowBuffer, destIndex + bytesRead);
Array.Copy(receiveBuffer, 0, meadowBuffer, destIndex, bytesRead);

_logger?.LogTrace("Received {count} bytes from VS, will forward to HCOM/Meadow. {hash}",
meadowBuffer.Length,
BitConverter.ToString(md5.ComputeHash(meadowBuffer))
.Replace("-", string.Empty)
.ToLowerInvariant());

await _connection.SendDebuggerData(meadowBuffer, 0, _cts.Token);
meadowBuffer = Array.Empty<byte>();
} while (_networkStream.DataAvailable);
}
else
{
_logger?.LogInformation("Unable to Read Data from Visual Studio");
_logger?.LogTrace("Unable to Read Data from Visual Studio");
}
}
}
catch (IOException ioe)
{
_logger?.LogInformation("Visual Studio has Disconnected");
_logger?.LogTrace(ioe, "Visual Studio has Disconnected");
}
catch (ObjectDisposedException ode)
{
_logger?.LogInformation("Visual Studio has stopped debugging");
_logger?.LogTrace(ode, "Visual Studio has stopped debugging");
}
catch (Exception ex)
{
_logger?.LogError($"Error receiving data from Visual Studio.\nError: {ex.Message}\nStackTrace:\n{ex.StackTrace}");
throw;
}
}

private async Task SendToVisualStudio()
{
try
{
while (!_cts.Token.IsCancellationRequested)
{
if (_networkStream != null && _networkStream.CanWrite)
{
_vsDebugDataReady.WaitOne();

while (_debuggerMessages.Count > 0)
{
var byteData = _debuggerMessages.Take(_cts.Token);

_logger?.LogTrace("Received {count} bytes from Meadow, will forward to VS", byteData.Length);
if (!_tcpClient.Connected)
{
_logger?.LogDebug("Cannot forward data, Visual Studio is not connected");
return;
}

await _networkStream.WriteAsync(byteData, 0, byteData.Length, _cts.Token);
_logger?.LogTrace("Forwarded {count} bytes to VS", byteData.Length);
}
}
else
{
_logger?.LogInformation("Unable to Write Data from Visual Studio");
}
}
}
catch (OperationCanceledException oce)
{
_logger?.LogInformation("Operation Cancelled");
_logger?.LogTrace(oce, "Operation Cancelled");
}
catch (Exception ex)
{
_logger?.LogError($"Error sending data to Visual Studio.\nError: {ex.Message}\nStackTrace:\n{ex.StackTrace}");

if (!_cts.Token.IsCancellationRequested)
{
throw;
}
}
}

public void Dispose()
{
if (_disposed)
{
return;
}

_logger?.LogTrace("Disposing ActiveClient");
_cts.Cancel();
try
{
Task.WhenAll(_receiveVsDebugDataTask, _receiveMeadowDebugDataTask).Wait(TimeSpan.FromSeconds(10));
}
catch (AggregateException ex)
{
_logger?.LogError("Error waiting for tasks to complete during dispose", ex);
}
_tcpClient.Dispose();
_networkStream.Dispose();
_cts.Dispose();

if (_connection != null)
{
_connection.DebuggerMessageReceived -= MeadowConnection_DebuggerMessageReceived;
}
_disposed = true;
}
}
}
Loading
Loading