-
Notifications
You must be signed in to change notification settings - Fork 0
Async mount control #2
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces comprehensive asynchronous control for mount operations, refactoring the device/profile listing logic for improved flexibility and test reliability. The changes include:
- Conversion of synchronous mount driver operations to async patterns with proper cancellation token support
- Introduction of a new
deviceCLI subcommand for device discovery and listing - Refactoring serial connection interface to support async I/O operations
- Addition of memory pooling utilities for improved performance
- Test infrastructure improvements including xunit v3 migration and cancellation token usage
Reviewed changes
Copilot reviewed 38 out of 39 changed files in this pull request and generated 22 comments.
Show a summary per file
| File | Description |
|---|---|
| StringHelper.cs | New utility for replacing non-printable characters with hex representation for better debugging |
| ArrayPoolHelper.cs | New helper class providing array pool rental with disposable wrapper for memory efficiency |
| SpanHelper.cs | Removed (functionality likely moved or no longer needed) |
| IMountDriver.cs | Converted all mount operation properties to async methods with cancellation token support |
| MeadeLX200ProtocolMountDriverBase.cs | Complete async refactoring of LX200 protocol implementation with proper locking and memory pooling |
| AscomTelescopeDriver.cs | Updated ASCOM mount driver to implement new async interface |
| Session.cs | Updated sequencing logic to use async mount operations throughout |
| ISerialConnection.cs | Refactored to async I/O with proper cancellation support |
| SerialConnection.cs | Implemented async serial I/O inheriting from new base class |
| SerialConnectionBase.cs | New abstract base class for serial connections with shared async I/O logic |
| FakeMeadeLX200SerialDevice.cs | Updated test fake to support async operations and improved concurrency handling |
| MeadeDeviceSource.cs | Implemented async device discovery with caching |
| FakeDeviceSource.cs | Adjusted fake device counts for testing |
| DeviceSubCommand.cs | New CLI command for device discovery and listing |
| ProfileSubCommand.cs | Refactored to use generic device listing methods |
| IConsoleHost.cs/ConsoleHost.cs | Generalized device listing with type-safe generic methods |
| Test files | Updated to use cancellation tokens and xunit v3 patterns |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private async ValueTask SendAsync(ISerialConnection port, ReadOnlyMemory<byte> command, CancellationToken cancellationToken) | ||
| { | ||
| if (_deviceInfo.SerialDevice is { } port && port.TryReadExactly(count, out response)) | ||
| if (!Connected) | ||
| { | ||
| return true; | ||
| throw new InvalidOperationException("Mount is not connected"); | ||
| } | ||
|
|
||
| response = default; | ||
| return false; | ||
| } | ||
| if (!CanUnpark && !CanSetPark && await AtParkAsync(cancellationToken)) | ||
| { | ||
| throw new InvalidOperationException("Mount is parked, but it is not possible to unpark it"); | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method acquires a port lock but never releases it in the error path when the AtParkAsync check fails. The semaphore will remain locked indefinitely, causing deadlock on subsequent operations. The Release() should be moved to a finally block to ensure it's always called.
| } | ||
| } | ||
| finally | ||
| { |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a trailing whitespace after the opening brace on line 868 that should be removed for consistency with the rest of the codebase.
| public async ValueTask<bool> WaitForSlewCompleteAsync(CancellationToken cancellationToken) | ||
| { | ||
| var period = TimeSpan.FromMilliseconds(250); | ||
| var period = TimeSpan.FromMilliseconds(251); |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The WaitForSlewCompleteAsync method has a hardcoded period of 251 milliseconds which appears to be an arbitrary change from the original 250. This should either be 250 milliseconds for consistency, or if the change to 251 was intentional, it should be documented why this specific value is needed.
| bool TryReadExactly(int count, [NotNullWhen(true)] out ReadOnlySpan<byte> message); | ||
| ValueTask<bool> TryWriteAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken); | ||
|
|
||
| ValueTask<bool> TryWriteAsync(string data, CancellationToken cancellationToken) => TryWriteAsync(Encoding.GetBytes(data), cancellationToken); |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method signature parameter name is missing. The parameter should have a name like 'data' to match the method body usage and the interface definition.
| /// Encoding used for decoding byte messages (used for display/logging only) | ||
| /// </summary> | ||
| public Encoding Encoding { get; } | ||
| public override string DisplayName => throw new NotImplementedException(); |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DisplayName property is marked as 'throw new NotImplementedException()' which will cause runtime failures when this property is accessed. This needs to return the actual port name.
| public override string DisplayName => throw new NotImplementedException(); | |
| public override string DisplayName => _port.PortName; |
| private volatile bool _isTracking = false; | ||
| private volatile bool _isSlewing = false; | ||
| private volatile bool _highPrecision = false; | ||
| private volatile int _trackingFrequency = 601; // TODO simulate tracking and tracking rate |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field '_trackingFrequency' can be 'readonly'.
| if (await TryReadExactlyRawAsync(buffer.AsMemory(0, count), cancellationToken)) | ||
| { | ||
| return Encoding.GetString(buffer.AsSpan(0, count)); | ||
| } | ||
| else | ||
| { | ||
| return null; | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement return - consider using '?' to express intent better.
| if (await TryReadExactlyRawAsync(buffer.AsMemory(0, count), cancellationToken)) | |
| { | |
| return Encoding.GetString(buffer.AsSpan(0, count)); | |
| } | |
| else | |
| { | |
| return null; | |
| } | |
| return await TryReadExactlyRawAsync(buffer.AsMemory(0, count), cancellationToken) | |
| ? Encoding.GetString(buffer.AsSpan(0, count)) | |
| : null; |
| if (Connected) | ||
| { | ||
| return await DestinationSideOfPierAsync(await GetRightAscensionAsync(cancellationToken), await GetDeclinationAsync(cancellationToken), cancellationToken); | ||
| } | ||
| else | ||
| { | ||
| return PointingState.Unknown; | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement return - consider using '?' to express intent better.
| if (Connected) | ||
| { | ||
| return ConditionHA(await GetSiderealTimeAsync(cancellationToken) - await GetRightAscensionAsync(cancellationToken)); | ||
| } | ||
| else | ||
| { | ||
| return double.NaN; | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement return - consider using '?' to express intent better.
| if (!double.IsNaN(raMount) && !double.IsNaN(decMount) && !double.IsNaN(az) && !double.IsNaN(alt)) | ||
| { | ||
| return (raMount, decMount, az, alt); | ||
| } | ||
| else | ||
| { | ||
| return null; | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement return - consider using '?' to express intent better.
This pull request introduces a new device management subcommand to the CLI, refactors device and profile listing logic for greater flexibility, and improves test reliability and API consistency. The most significant changes are the addition of the
deviceCLI subcommand, the generalization of device listing methods, and updates to tests and supporting infrastructure.CLI Feature Additions
DeviceSubCommandclass that introduces thedeviceCLI command withdiscoverandlistsubcommands, allowing users to discover and list all connected devices except profiles. (src/TianWen.Lib.CLI/DeviceSubCommand.cs)src/TianWen.Lib.CLI/Program.cs)Device and Profile Listing Refactor
IConsoleHostand its implementation to generalize device listing: replacedListProfilesAsyncwithListDevicesAsync<TDevice>andListAllDevicesAsync, allowing listing of arbitrary device types and all devices. (src/TianWen.Lib.CLI/IConsoleHost.cs,src/TianWen.Lib.CLI/ConsoleHost.cs) [1] [2]src/TianWen.Lib.CLI/ProfileSubCommand.cs) [1] [2] [3]Test Infrastructure and Reliability
Assert.SkipUnlessfor improved reliability and clearer intent. (src/TianWen.Lib.Tests/AscomDeviceTests.cs,src/TianWen.Lib.Tests/FindBestFocusTests.cs) [1] [2] [3] [4] [5] [6] [7]src/TianWen.Lib.Tests/FakeExternal.cs) [1] [2] [3]Miscellaneous Improvements
device listcommand for easier testing and development. (src/TianWen.Lib.CLI/Properties/launchSettings.json)src/TianWen.Lib.CLI/IConsoleHost.cs,src/TianWen.Lib.Tests/AscomDeviceTests.cs,src/TianWen.Lib.Tests/FindBestFocusTests.cs) [1] [2] [3]These changes collectively enhance the CLI's device management capabilities, improve code maintainability, and strengthen test coverage and reliability.