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

Fix runtime commands on macOS #381

Merged
merged 5 commits into from
Oct 21, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected override async ValueTask ExecuteCommand()

// in order to deploy, the runtime must be disabled
var wasRuntimeEnabled = await Connection.IsRuntimeEnabled();

if (wasRuntimeEnabled)
{
Logger?.LogInformation("Disabling runtime...");
Expand Down
1 change: 1 addition & 0 deletions Source/v2/Meadow.Cli/Commands/Current/App/AppRunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ protected override async ValueTask ExecuteCommand()

// in order to deploy, the runtime must be disabled
var wasRuntimeEnabled = await Connection.IsRuntimeEnabled();

if (wasRuntimeEnabled)
{
Logger?.LogInformation("Disabling runtime...");
Expand Down
10 changes: 9 additions & 1 deletion Source/v2/Meadow.Cli/Commands/Current/BaseDeviceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,17 @@ protected override async ValueTask ExecuteCommand()
Logger?.LogInformation(message);
};

// the connection passes messages back to us (info about actions happening on-device)
connection.DeviceMessageReceived += (s, e) =>
{
Logger?.LogInformation(e.message);
if (e.message.Contains("% downloaded"))
{
// don't echo this, as we're already reporting % written
}
else
{
Logger?.LogInformation(e.message);
}
};

try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ protected override async ValueTask ExecuteCommand()

if (wasRuntimeEnabled)
{
Logger?.LogError($"The runtime must be disabled before doing any file management. Use 'meadow runtime disable' first.");
return;
Logger?.LogInformation("Disabling device runtime...");
await Connection.Device.RuntimeDisable();
}

Logger?.LogInformation($"Deleting file '{MeadowFile}' from device...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,22 +236,6 @@ private ILibUsbDevice GetLibUsbDeviceForCurrentEnvironment()

private async ValueTask WriteFiles(IMeadowConnection connection)
{
// the connection passes messages back to us (info about actions happening on-device
connection.DeviceMessageReceived += (s, e) =>
{
if (e.message.Contains("% downloaded"))
{
// don't echo this, as we're already reporting % written
}
else
{
Logger?.LogInformation(e.message);
}
};
connection.ConnectionMessage += (s, message) =>
{
Logger?.LogInformation(message);
};
connection.FileWriteFailed += (s, e) =>
{
// TODO _fileWriteError = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class RuntimeDisableCommand : BaseDeviceCommand<RuntimeEnableCommand>
public RuntimeDisableCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
: base(connectionManager, loggerFactory)
{
Logger?.LogInformation($"Disabling runtime...");
}

protected override async ValueTask ExecuteCommand()
Expand All @@ -20,11 +19,16 @@ protected override async ValueTask ExecuteCommand()
{
if (Connection.Device != null)
{
await Connection.Device.RuntimeDisable(CancellationToken);
try
{
Logger?.LogInformation($"Disabling runtime...");

var state = await Connection.Device.IsRuntimeEnabled(CancellationToken);

Logger?.LogInformation($"Runtime is {(state ? "ENABLED" : "DISABLED")}");
await Connection.Device.RuntimeDisable(CancellationToken);
}
catch (Exception ex)
{
Logger?.LogError(ex, $"Failed to disable runtime.");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class RuntimeEnableCommand : BaseDeviceCommand<RuntimeEnableCommand>
public RuntimeEnableCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
: base(connectionManager, loggerFactory)
{
Logger?.LogInformation($"Enabling runtime...");
}

protected override async ValueTask ExecuteCommand()
Expand All @@ -20,11 +19,16 @@ protected override async ValueTask ExecuteCommand()
{
if (Connection.Device != null)
{
await Connection.Device.RuntimeEnable(CancellationToken);
try
{
Logger?.LogInformation($"Enabling runtime...");

var state = await Connection.Device.IsRuntimeEnabled(CancellationToken);

Logger?.LogInformation($"Runtime is {(state ? "ENABLED" : "DISABLED")}");
await Connection.Device.RuntimeEnable(CancellationToken);
}
catch (Exception ex)
{
Logger?.LogError(ex, $"Failed to enable runtime.");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class RuntimeStateCommand : BaseDeviceCommand<RuntimeStateCommand>
public RuntimeStateCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
: base(connectionManager, loggerFactory)
{
Logger?.LogInformation($"Querying runtime state...");
}

protected override async ValueTask ExecuteCommand()
Expand All @@ -20,9 +19,16 @@ protected override async ValueTask ExecuteCommand()
{
if (Connection.Device != null)
{
var state = await Connection.Device.IsRuntimeEnabled(CancellationToken);
try
{
Logger?.LogInformation($"Querying runtime state...");

Logger?.LogInformation($"Runtime is {(state ? "ENABLED" : "DISABLED")}");
await Connection.Device.IsRuntimeEnabled(CancellationToken);
}
catch (Exception ex)
{
Logger?.LogError(ex, $"Unable to determine the runtime state.");
}
}
}
}
Expand Down
13 changes: 0 additions & 13 deletions Source/v2/Meadow.Cli/Commands/Legacy/FlashOsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,19 +240,6 @@ private async ValueTask WriteFiles()
{
if (Connection != null)
{
// the connection passes messages back to us (info about actions happening on-device
/* TODO Don't think this is needed as it's in the base class now Connection.DeviceMessageReceived += (s, e) =>
{
if (e.message.Contains("% downloaded"))
{
// don't echo this, as we're already reporting % written
}
else
{
Logger?.LogInformation(e.message);
}
};*/

var package = await GetSelectedPackage();

if (Connection.Device != null
Expand Down
9 changes: 2 additions & 7 deletions Source/v2/Meadow.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ public class Program
public static async Task<int> Main(string[] args)
{
var logLevel = LogEventLevel.Information;
var logModifier = args.FirstOrDefault(a => a.Contains("-m"))
?.Count(x => x == 'm') ?? 0;

logLevel -= logModifier;
if (logLevel < 0)
{
logLevel = 0;
}
if (args.Contains("--verbose"))
logLevel = LogEventLevel.Verbose;

var outputTemplate = logLevel == LogEventLevel.Verbose
? "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Message:lj}{NewLine}{Exception}"
Expand Down
14 changes: 2 additions & 12 deletions Source/v2/Meadow.Hcom/Connections/SerialConnection.ListenerProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,7 @@ public override async Task WaitForMeadowAttach(CancellationToken? cancellationTo

await Task.Delay(500);

if (!_port.IsOpen)
{
try
{
Open();
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to open port: {ex.Message}");
}
}
Open();
}

throw new TimeoutException();
Expand Down Expand Up @@ -204,7 +194,7 @@ private async Task ListenerProc()
}
else if (response is ReconnectRequiredResponse rrr)
{
// the device is going to restart - we need to wait for a HCOM_HOST_REQUEST_TEXT_CONCLUDED to know it's back
// the device is going to restart - we need to wait for a HCOM_HOST_REQUEST_TEXT_CONCLUDED/TextConcludedResponse to know it's back
State = ConnectionState.Disconnected;
_reconnectInProgress = true;
}
Expand Down
82 changes: 52 additions & 30 deletions Source/v2/Meadow.Hcom/Connections/SerialConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace Meadow.Hcom;

public partial class SerialConnection : ConnectionBase, IDisposable
{
public const int DefaultBaudRate = 115200;
public const int ReadBufferSizeBytes = 0x2000;
private const int DefaultTimeout = 5000;

private event EventHandler<string>? FileReadCompleted = delegate { };
private event EventHandler? FileWriteAccepted;
Expand Down Expand Up @@ -42,7 +44,7 @@ public SerialConnection(string port, ILogger? logger = default)
State = ConnectionState.Disconnected;
_logger = logger;
_port = new SerialPort(port);
_port.ReadTimeout = _port.WriteTimeout = 5000;
_port.ReadTimeout = _port.WriteTimeout = DefaultTimeout;

new Task(
() => _ = ListenerProc(),
Expand Down Expand Up @@ -86,24 +88,7 @@ private void ConnectionManagerProc()
{
while (_maintainConnection)
{
if (!_port.IsOpen)
{
try
{
Debug.WriteLine("Opening COM port...");
_port.Open();
Debug.WriteLine("Opened COM port");
}
catch (Exception ex)
{
Debug.WriteLine($"{ex.Message}");
Thread.Sleep(1000);
}
}
else
{
Thread.Sleep(1000);
}
Open(true);
}
}

Expand All @@ -129,27 +114,54 @@ public void RemoveListener(IConnectionListener listener)
// TODO: stop maintaining connection?
}

private void Open()
private void Open(bool inLoop = false)
{
if (!_port.IsOpen)
{
try
{
Debug.WriteLine("Opening COM port...");
_port.Open();
}
catch (FileNotFoundException)
catch (UnauthorizedAccessException ex)
{
// Handle unauthorized access (e.g., port in use by another application)
throw new Exception($"Serial port '{_port.PortName}' is in use by another application.", ex.InnerException);
}
catch (IOException ex)
{
throw new Exception($"Serial port '{_port.PortName}' not found");
// Handle I/O errors
throw new Exception($"An I/O error occurred when opening the serial port '{_port.PortName}'.", ex.InnerException);
}
catch (TimeoutException ex)
{
// Handle timeout
throw new Exception($"Timeout occurred when opening the serial port '{_port.PortName}'.", ex.InnerException);
}
}
else if (inLoop)
{
Thread.Sleep(1000);
}

State = ConnectionState.Connected;

Debug.WriteLine("Opened COM port");
}

private void Close()
{
if (_port.IsOpen)
{
_port.Close();
try
{
_port.Close();
}
catch (IOException ex)
{
// Handle I/O errors
throw new Exception($"An I/O error occurred when attempting to close the serial port '{_port.PortName}'.", ex.InnerException);
}
}

State = ConnectionState.Disconnected;
Expand Down Expand Up @@ -481,9 +493,12 @@ protected override void Dispose(bool disposing)
private List<string> InfoMessages { get; } = new List<string>();

private const string RuntimeSucessfullyEnabledToken = "Meadow successfully started MONO";
private const string RuntimeSucessfullyDisabledToken = "Mono is disabled";
private const string RuntimeStateToken = "Mono is";
private const string RuntimeIsEnabledToken = "Mono is enabled";
private const string RuntimeIsDisabledToken = "Mono is disabled";
private const string RuntimeHasBeenToken = "Mono has been";
private const string RuntimeHasBeenEnabledToken = "Mono has been enabled";
private const string RuntimeHasBeenDisabledToken = "Mono has been disabled";
private const string RtcRetrievalToken = "UTC time:";

public int CommandTimeoutSeconds { get; set; } = 30;
Expand Down Expand Up @@ -600,19 +615,26 @@ public override async Task<bool> IsRuntimeEnabled(CancellationToken? cancellatio

EnqueueRequest(command);

return await WaitForInformationResponse(RuntimeStateToken, RuntimeIsEnabledToken, cancellationToken);
}

private async Task<bool> WaitForInformationResponse(string textToContain, string textToVerify, CancellationToken? cancellationToken)
{
// wait for an information response
var timeout = CommandTimeoutSeconds * 2;
while (timeout-- > 0)
{
if (cancellationToken?.IsCancellationRequested ?? false) return false;
if (timeout <= 0) throw new TimeoutException();
if (cancellationToken?.IsCancellationRequested ?? false)
return false;
if (timeout <= 0)
throw new TimeoutException();

if (InfoMessages.Count > 0)
{
var m = InfoMessages.FirstOrDefault(i => i.Contains(RuntimeStateToken));
var m = InfoMessages.FirstOrDefault(i => i.Contains(textToContain));
if (m != null)
{
return m == RuntimeIsEnabledToken;
return m == textToVerify;
}
}

Expand All @@ -631,7 +653,7 @@ public override async Task RuntimeEnable(CancellationToken? cancellationToken =

EnqueueRequest(command);

await WaitForConcluded(null, cancellationToken);
await WaitForInformationResponse(RuntimeHasBeenToken, RuntimeHasBeenEnabledToken, cancellationToken);
}

public override async Task RuntimeDisable(CancellationToken? cancellationToken = null)
Expand All @@ -644,7 +666,7 @@ public override async Task RuntimeDisable(CancellationToken? cancellationToken =

EnqueueRequest(command);

await WaitForConcluded(null, cancellationToken);
await WaitForInformationResponse(RuntimeHasBeenToken, RuntimeHasBeenDisabledToken, cancellationToken);
}

public override async Task TraceEnable(CancellationToken? cancellationToken = null)
Expand Down
Loading