Skip to content

Commit b91bec5

Browse files
authored
Merge pull request #7 from akinbender/feature/mac-serial-support
Mac serial & net10 support
2 parents 83aa07e + e4dd03e commit b91bec5

23 files changed

Lines changed: 323 additions & 143 deletions

‎.github/workflows/build.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup .NET
1818
uses: actions/setup-dotnet@v3
1919
with:
20-
dotnet-version: '8.0.x'
20+
dotnet-version: '10.0.x'
2121

2222
- name: Add msbuild to PATH
2323
uses: microsoft/setup-msbuild@v2
@@ -31,7 +31,7 @@ jobs:
3131
run: dotnet restore MakerPrompt.Blazor/MakerPrompt.Blazor.csproj
3232

3333
- name: Build (Windows)
34-
run: dotnet build MakerPrompt.MAUI/MakerPrompt.MAUI.csproj -c Release -f net8.0-windows10.0.19041.0 -p:RuntimeIdentifierOverride=win-x64
34+
run: dotnet build MakerPrompt.MAUI/MakerPrompt.MAUI.csproj -c Release -f net10.0-windows10.0.19041.0 -p:RuntimeIdentifierOverride=win-x64
3535

3636
- name: Build (Blazor WebAssembly)
3737
run: dotnet build MakerPrompt.Blazor/MakerPrompt.Blazor.csproj -c Release --no-restore

‎.github/workflows/publish-github-pages.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- name: Setup .NET
3131
uses: actions/setup-dotnet@v4
3232
with:
33-
dotnet-version: '8.0.x'
33+
dotnet-version: '10.0.x'
3434

3535
- name: Install MAUI workloads
3636
run: dotnet workload install wasm-tools

‎.github/workflows/publish-maui.yml‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Setup .NET
2020
uses: actions/setup-dotnet@v3
2121
with:
22-
dotnet-version: '8.0.x'
22+
dotnet-version: '10.0.x'
2323

2424
- name: Add msbuild to PATH
2525
uses: microsoft/setup-msbuild@v2
@@ -34,23 +34,35 @@ jobs:
3434
- name: Restore dependencies
3535
run: dotnet restore ${{ env.MAUI_PROJECT }}
3636

37-
- name: Publish MAUI Windows
37+
38+
- name: Publish MAUI Windows 10
39+
continue-on-error: true
40+
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net10.0-windows10.0.19041.0 -p:WindowsPackageType=None -o ${{ env.OUTPUT_DIR }}/windows10
41+
42+
- name: Publish MAUI Windows 11
3843
continue-on-error: true
39-
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net8.0-windows10.0.19041.0 -p:WindowsPackageType=None -o ${{ env.OUTPUT_DIR }}/windows
44+
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net10.0-windows10.0.22000.0 -p:WindowsPackageType=None -o ${{ env.OUTPUT_DIR }}/windows11
4045

4146
- name: Publish MAUI Android
4247
continue-on-error: true
43-
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net8.0-android -o ${{ env.OUTPUT_DIR }}/android
48+
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net10.0-android -o ${{ env.OUTPUT_DIR }}/android
4449

4550
- name: Publish MAUI MacCatalyst
4651
continue-on-error: true
47-
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net8.0-maccatalyst -p:CreatePackage=false
52+
run: dotnet publish ${{ env.MAUI_PROJECT }} -c Release -f net10.0-maccatalyst -p:CreatePackage=false
53+
54+
55+
- name: Zip Windows 10 Release
56+
uses: vimtor/action-zip@v1.2
57+
with:
58+
files: ${{ env.OUTPUT_DIR }}/windows10
59+
dest: windows10-release.zip
4860

49-
- name: Zip Windows Release
61+
- name: Zip Windows 11 Release
5062
uses: vimtor/action-zip@v1.2
5163
with:
52-
files: ${{ env.OUTPUT_DIR }}/windows
53-
dest: windows-release.zip
64+
files: ${{ env.OUTPUT_DIR }}/windows11
65+
dest: windows11-release.zip
5466

5567
- name: Zip Android Release
5668
uses: vimtor/action-zip@v1.2
@@ -71,6 +83,7 @@ jobs:
7183
body_path: ${{ github.workspace }}/CHANGELOG.md
7284
make_latest: true
7385
files: |
74-
windows-release.zip
86+
windows10-release.zip
87+
windows11-release.zip
7588
android-release.zip
7689
maccatalyst-release.zip

‎MakerPrompt.Blazor/MakerPrompt.Blazor.csproj‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
1+
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
7-
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
7+
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
88
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
99
<Version>0.1.13</Version>
1010
<AssemblyVersion>$(Version)</AssemblyVersion>
@@ -13,8 +13,8 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.14" />
17-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.14" PrivateAssets="all" />
16+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1" />
17+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.1" PrivateAssets="all" />
1818
</ItemGroup>
1919

2020
<ItemGroup>

‎MakerPrompt.MAUI/MakerPrompt.MAUI.csproj‎

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Razor">
1+
<Project Sdk="Microsoft.NET.Sdk.Razor">
22
<PropertyGroup>
3-
<TargetFrameworks>net8.0-android;net8.0-maccatalyst</TargetFrameworks>
4-
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
3+
<TargetFrameworks>net10.0-android;net10.0-maccatalyst</TargetFrameworks>
4+
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0;net10.0-windows10.0.22000.0</TargetFrameworks>
55
<RuntimeIdentifiers Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">android-arm64;android-x64</RuntimeIdentifiers>
66
<RuntimeIdentifiers Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers>
77

@@ -29,16 +29,23 @@
2929
</PropertyGroup>
3030

3131
<!-- Android -->
32-
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-android')) != true">
32+
<ItemGroup Condition="$(TargetFramework.Contains('-android')) != true">
3333
<Compile Remove="**\*.Android.cs" />
3434
<None Include="**\*.Android.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
3535
</ItemGroup>
3636

37+
<!-- IOS for future -->
38+
<ItemGroup Condition="$(TargetFramework.Contains('-ios')) != true">
39+
<Compile Remove="**\*.iOS.cs" />
40+
<None Include="**\*.iOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
41+
</ItemGroup>
42+
3743
<!-- Mac Catalyst -->
38-
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-ios')) != true AND $(TargetFramework.StartsWith('net8.0-maccatalyst')) != true">
39-
<Compile Remove="**\*.MaciOS.cs" />
40-
<None Include="**\*.MaciOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
44+
<ItemGroup Condition="$(TargetFramework.Contains('-maccatalyst')) != true">
45+
<Compile Remove="**\*.MacOS.cs" />
46+
<None Include="**\*.MacOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
4147
</ItemGroup>
48+
4249
<!-- Windows -->
4350
<ItemGroup Condition="$(TargetFramework.Contains('-windows')) != true">
4451
<Compile Remove="**\*.Windows.cs" />
@@ -60,12 +67,13 @@
6067
</ItemGroup>
6168

6269
<ItemGroup>
63-
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
64-
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)" />
65-
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
70+
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.20" />
71+
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="10.0.20" />
72+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.1" />
6673
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.3.0" />
67-
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
68-
<PackageReference Include="UsbSerialForAndroid.Net" Version="1.0.3" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'" />
74+
<PackageReference Include="System.IO.Ports" Version="10.0.1" />
75+
<PackageReference Include="UsbSerialForAndroid.Net" Version="1.0.4" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'" />
76+
<PackageReference Include="UsbSerialForMacOS" Version="0.9.4" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'" />
6977
</ItemGroup>
7078

7179
<ItemGroup>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
using MakerPrompt.Shared.Infrastructure;
2+
using MakerPrompt.Shared.Models;
3+
using MakerPrompt.Shared.Utils;
4+
using System.Text;
5+
using System.Threading.Tasks.Dataflow;
6+
using UsbSerialForMacOS;
7+
namespace MakerPrompt.MAUI.Services
8+
{
9+
public class SerialService : BaseSerialService, ISerialService
10+
{
11+
private UsbSerialManager? _manager = new();
12+
private readonly BufferBlock<string> _commandQueue = new();
13+
private CancellationTokenSource? _cts;
14+
private Task? _sendTask;
15+
private Task? _receiveTask;
16+
private bool _disposed = false;
17+
public bool IsSupported => true;
18+
19+
public SerialService() { }
20+
21+
public async Task<bool> ConnectAsync(PrinterConnectionSettings connectionSettings)
22+
{
23+
if (IsConnected) return IsConnected;
24+
if (connectionSettings.ConnectionType != ConnectionType || connectionSettings.Serial == null)
25+
throw new ArgumentException("Invalid connection settings");
26+
27+
var portName = connectionSettings.Serial.PortName;
28+
var baudRate = connectionSettings.Serial.BaudRate;
29+
30+
try
31+
{
32+
_manager ??= new UsbSerialManager();
33+
_cts?.Dispose();
34+
_cts = new CancellationTokenSource();
35+
36+
IsConnected = _manager.Open(portName, baudRate);
37+
ConnectionName = portName;
38+
39+
_sendTask = Task.Run(() => SendLoopAsync(_cts.Token));
40+
_receiveTask = Task.Run(() => ReceiveLoopAsync(_cts.Token));
41+
RaiseConnectionChanged();
42+
}
43+
catch (Exception ex)
44+
{
45+
IsConnected = false;
46+
throw new SerialException("Connection failed", ex);
47+
}
48+
49+
return IsConnected;
50+
}
51+
52+
public async Task DisconnectAsync()
53+
{
54+
if (!IsConnected) return;
55+
_cts?.Cancel();
56+
57+
try
58+
{
59+
if (_sendTask != null)
60+
await _sendTask.ConfigureAwait(false);
61+
if (_receiveTask != null)
62+
await _receiveTask.ConfigureAwait(false);
63+
}
64+
finally
65+
{
66+
try
67+
{
68+
_manager?.Close();
69+
}
70+
catch
71+
{
72+
// swallow close exceptions on shutdown
73+
}
74+
75+
_manager = null;
76+
IsConnected = false;
77+
RaiseConnectionChanged();
78+
}
79+
}
80+
81+
public override async Task WriteDataAsync(string data)
82+
{
83+
if (!IsConnected) return;
84+
await _commandQueue.SendAsync(data);
85+
}
86+
87+
public async Task<IEnumerable<string>> GetAvailablePortsAsync()
88+
{
89+
return _manager?.AvailablePorts().OrderBy(p => p).ToList() ?? [];
90+
}
91+
92+
private async Task SendLoopAsync(CancellationToken ct)
93+
{
94+
try
95+
{
96+
while (IsConnected && !ct.IsCancellationRequested)
97+
{
98+
var command = await _commandQueue.ReceiveAsync(ct);
99+
var manager = _manager;
100+
if (manager == null || ct.IsCancellationRequested)
101+
{
102+
break;
103+
}
104+
manager.Write(command);
105+
await Task.Delay(10, ct);
106+
}
107+
}
108+
catch (OperationCanceledException)
109+
{
110+
// Normal shutdown
111+
}
112+
}
113+
114+
private async Task ReceiveLoopAsync(CancellationToken ct)
115+
{
116+
while (!ct.IsCancellationRequested && IsConnected)
117+
{
118+
try
119+
{
120+
var manager = _manager;
121+
if (manager == null || ct.IsCancellationRequested)
122+
{
123+
break;
124+
}
125+
var bytesRead = manager.Read(4096);
126+
if (bytesRead.Length > 0)
127+
{
128+
var received = Encoding.UTF8.GetString(bytesRead.ToArray());
129+
ProcessReceivedData(received);
130+
}
131+
await Task.Delay(10, ct);
132+
}
133+
catch (OperationCanceledException)
134+
{
135+
break;
136+
}
137+
catch (Exception ex)
138+
{
139+
Console.WriteLine($"Receive error: {ex.Message}");
140+
IsConnected = false;
141+
await DisposeAsync();
142+
}
143+
}
144+
}
145+
146+
public override async ValueTask DisposeAsync()
147+
{
148+
if (_disposed) return;
149+
_disposed = true;
150+
151+
try
152+
{
153+
await DisconnectAsync();
154+
_cts?.Dispose();
155+
}
156+
catch (ObjectDisposedException)
157+
{
158+
// Ignore if already disposed to ensure idempotent disposal
159+
}
160+
}
161+
162+
public Task<bool> CheckSupportedAsync() => Task.FromResult(true);
163+
164+
public Task RequestPortAsync() => Task.CompletedTask;
165+
}
166+
}

‎MakerPrompt.MAUI/Services/SerialService.Windows.cs‎

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using System.IO.Ports;
1+
using System.Text;
2+
using System.IO.Ports;
23
using System.Threading.Tasks.Dataflow;
34
using MakerPrompt.Shared.Infrastructure;
4-
using System.Text;
55
using MakerPrompt.Shared.Models;
6+
using MakerPrompt.Shared.Utils;
67

78
namespace MakerPrompt.MAUI.Services
89
{
@@ -172,10 +173,4 @@ await Task.Run(() =>
172173

173174
public Task RequestPortAsync() => Task.CompletedTask;
174175
}
175-
176-
public class SerialException : Exception
177-
{
178-
public SerialException(string message, Exception inner)
179-
: base(message, inner) { }
180-
}
181176
}

MakerPrompt.MAUI/Services/SerialService.MaciOS.cs renamed to MakerPrompt.MAUI/Services/SerialService.iOS.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,4 @@ public override async ValueTask DisposeAsync()
2828

2929
public Task RequestPortAsync() => throw new NotSupportedException();
3030
}
31-
32-
public class SerialException : Exception
33-
{
34-
public SerialException(string message, Exception inner)
35-
: base(message, inner) { }
36-
}
3731
}

0 commit comments

Comments
 (0)