Skip to content

Commit

Permalink
Merge pull request #571 from WildernessLabs/v2_debugging
Browse files Browse the repository at this point in the history
V2 debugging
  • Loading branch information
ctacke authored May 25, 2024
2 parents bff8607 + 30ce107 commit 65d96ac
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 279 deletions.
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

0 comments on commit 65d96ac

Please sign in to comment.