Running Alpha V2
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ PCPalService/service_log.txt
|
|||||||
/PCPal/Configurator/obj
|
/PCPal/Configurator/obj
|
||||||
/PCPal/Core/bin
|
/PCPal/Core/bin
|
||||||
/PCPal/Core/obj
|
/PCPal/Core/obj
|
||||||
|
/PCPal/Configurator/bin
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="PCPal.Configurator.AppShell">
|
xmlns:views="clr-namespace:PCPal.Configurator.Views"
|
||||||
|
x:Class="PCPal.Configurator.AppShell"
|
||||||
|
Shell.NavBarIsVisible="False"
|
||||||
|
Shell.FlyoutBehavior="Disabled">
|
||||||
|
|
||||||
<Shell.ContentTemplate>
|
<ShellContent>
|
||||||
|
<ContentPage>
|
||||||
<Grid ColumnDefinitions="220,*">
|
<Grid ColumnDefinitions="220,*">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<StackLayout Grid.Column="0"
|
<StackLayout Grid.Column="0"
|
||||||
@@ -68,5 +72,6 @@
|
|||||||
<!-- Content area -->
|
<!-- Content area -->
|
||||||
<ContentView Grid.Column="1" x:Name="ContentContainer" />
|
<ContentView Grid.Column="1" x:Name="ContentContainer" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Shell.ContentTemplate>
|
</ContentPage>
|
||||||
|
</ShellContent>
|
||||||
</Shell>
|
</Shell>
|
||||||
@@ -5,7 +5,6 @@ using PCPal.Configurator.Views.LCD;
|
|||||||
using PCPal.Configurator.Views.OLED;
|
using PCPal.Configurator.Views.OLED;
|
||||||
using PCPal.Configurator.Views.TFT;
|
using PCPal.Configurator.Views.TFT;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
//using UIKit;
|
|
||||||
|
|
||||||
namespace PCPal.Configurator;
|
namespace PCPal.Configurator;
|
||||||
|
|
||||||
@@ -14,6 +13,7 @@ public partial class AppShell : Shell, INotifyPropertyChanged
|
|||||||
private bool _isConnected;
|
private bool _isConnected;
|
||||||
private string _connectionStatus;
|
private string _connectionStatus;
|
||||||
private DateTime _lastUpdateTime;
|
private DateTime _lastUpdateTime;
|
||||||
|
private CancellationTokenSource _monitoringCts;
|
||||||
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
@@ -60,7 +60,8 @@ public partial class AppShell : Shell, INotifyPropertyChanged
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_serviceProvider = IPlatformApplication.Current.Services;
|
_serviceProvider = IPlatformApplication.Current?.Services;
|
||||||
|
_monitoringCts = new CancellationTokenSource();
|
||||||
|
|
||||||
// Set initial connection status
|
// Set initial connection status
|
||||||
IsConnected = false;
|
IsConnected = false;
|
||||||
@@ -74,17 +75,29 @@ public partial class AppShell : Shell, INotifyPropertyChanged
|
|||||||
StartConnectivityMonitoring();
|
StartConnectivityMonitoring();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnDisappearing()
|
||||||
|
{
|
||||||
|
base.OnDisappearing();
|
||||||
|
_monitoringCts?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AppShell()
|
||||||
|
{
|
||||||
|
_monitoringCts?.Cancel();
|
||||||
|
_monitoringCts?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnNavMenuSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void OnNavMenuSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.CurrentSelection.FirstOrDefault() is string selection)
|
if (e.CurrentSelection.FirstOrDefault() is string selection)
|
||||||
{
|
{
|
||||||
ContentView view = selection switch
|
ContentView view = selection switch
|
||||||
{
|
{
|
||||||
"1602 LCD Display" => _serviceProvider.GetService<LcdConfigView>(),
|
"1602 LCD Display" => _serviceProvider?.GetService<LcdConfigView>(),
|
||||||
"4.6 TFT Display" => _serviceProvider.GetService<TftConfigView>(),
|
"4.6\" TFT Display" => _serviceProvider?.GetService<TftConfigView>(),
|
||||||
"OLED Display" => _serviceProvider.GetService<OledConfigView>(),
|
"OLED Display" => _serviceProvider?.GetService<OledConfigView>(),
|
||||||
"Settings" => _serviceProvider.GetService<SettingsView>(),
|
"Settings" => _serviceProvider?.GetService<SettingsView>(),
|
||||||
"Help" => _serviceProvider.GetService<HelpView>(),
|
"Help" => _serviceProvider?.GetService<HelpView>(),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,7 +110,7 @@ public partial class AppShell : Shell, INotifyPropertyChanged
|
|||||||
|
|
||||||
private async void StartConnectivityMonitoring()
|
private async void StartConnectivityMonitoring()
|
||||||
{
|
{
|
||||||
var serialPortService = _serviceProvider.GetService<ISerialPortService>();
|
var serialPortService = _serviceProvider?.GetService<ISerialPortService>();
|
||||||
if (serialPortService != null)
|
if (serialPortService != null)
|
||||||
{
|
{
|
||||||
// Subscribe to connection status changes
|
// Subscribe to connection status changes
|
||||||
@@ -112,9 +125,15 @@ public partial class AppShell : Shell, INotifyPropertyChanged
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Start periodic connection check
|
// Start periodic connection check
|
||||||
while (true)
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(5000);
|
while (!_monitoringCts.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(5000, _monitoringCts.Token);
|
||||||
|
|
||||||
|
if (_monitoringCts.Token.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await serialPortService.CheckConnectionAsync();
|
await serialPortService.CheckConnectionAsync();
|
||||||
@@ -126,5 +145,11 @@ public partial class AppShell : Shell, INotifyPropertyChanged
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Expected when cancellation is requested
|
||||||
|
System.Diagnostics.Debug.WriteLine("Connectivity monitoring canceled");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
//using Android.Sax;
|
using Microsoft.Maui.Controls.Shapes;
|
||||||
using Microsoft.Maui.Controls.Shapes;
|
|
||||||
using PCPal.Configurator.ViewModels;
|
using PCPal.Configurator.ViewModels;
|
||||||
using PCPal.Core.Models;
|
using PCPal.Core.Models;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -162,14 +161,17 @@ public class OledPreviewCanvas : GraphicsView
|
|||||||
dragStartPoint = point;
|
dragStartPoint = point;
|
||||||
|
|
||||||
// Check if an element was clicked
|
// Check if an element was clicked
|
||||||
if (Elements != null)
|
if (Elements == null || Elements.Count == 0) return;
|
||||||
{
|
|
||||||
// Need to adjust for scale
|
// Need to adjust for scale
|
||||||
float x = (float)point.X / Scale;
|
float x = (float)point.X / Scale;
|
||||||
float y = (float)point.Y / Scale;
|
float y = (float)point.Y / Scale;
|
||||||
|
|
||||||
foreach (var element in Elements)
|
// Check in reverse order (top elements first)
|
||||||
|
for (int i = Elements.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
var element = Elements[i];
|
||||||
|
|
||||||
if (element is TextElement textElement)
|
if (element is TextElement textElement)
|
||||||
{
|
{
|
||||||
// Simple bounding box check
|
// Simple bounding box check
|
||||||
@@ -217,11 +219,6 @@ public class OledPreviewCanvas : GraphicsView
|
|||||||
else if (element is LineElement lineElement)
|
else if (element is LineElement lineElement)
|
||||||
{
|
{
|
||||||
// Simplified line hit detection
|
// Simplified line hit detection
|
||||||
float lineLength = (float)Math.Sqrt(
|
|
||||||
Math.Pow(lineElement.X2 - lineElement.X1, 2) +
|
|
||||||
Math.Pow(lineElement.Y2 - lineElement.Y1, 2));
|
|
||||||
|
|
||||||
// Check if point is close to the line
|
|
||||||
float distance = DistancePointToLine(
|
float distance = DistancePointToLine(
|
||||||
x, y,
|
x, y,
|
||||||
lineElement.X1, lineElement.Y1,
|
lineElement.X1, lineElement.Y1,
|
||||||
@@ -257,12 +254,13 @@ public class OledPreviewCanvas : GraphicsView
|
|||||||
// No element was clicked, deselect
|
// No element was clicked, deselect
|
||||||
SelectedElement = null;
|
SelectedElement = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDragInteraction(object sender, TouchEventArgs e)
|
private void OnDragInteraction(object sender, TouchEventArgs e)
|
||||||
{
|
{
|
||||||
if (!IsEditable || draggedElement == null) return;
|
if (!IsEditable || draggedElement == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var point = e.Touches[0];
|
var point = e.Touches[0];
|
||||||
|
|
||||||
// Calculate the delta from the start point
|
// Calculate the delta from the start point
|
||||||
@@ -281,7 +279,7 @@ public class OledPreviewCanvas : GraphicsView
|
|||||||
dragStartPoint = point;
|
dragStartPoint = point;
|
||||||
|
|
||||||
// Notify property changes
|
// Notify property changes
|
||||||
var viewModel = BindingContext as OledConfigViewModel;
|
var viewModel = BindingContext as PCPal.Configurator.ViewModels.OledConfigViewModel;
|
||||||
if (viewModel != null)
|
if (viewModel != null)
|
||||||
{
|
{
|
||||||
// Update the view model properties to reflect the new position
|
// Update the view model properties to reflect the new position
|
||||||
@@ -295,6 +293,11 @@ public class OledPreviewCanvas : GraphicsView
|
|||||||
// Invalidate the canvas to redraw
|
// Invalidate the canvas to redraw
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Error in drag interaction: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnEndInteraction(object sender, TouchEventArgs e)
|
private void OnEndInteraction(object sender, TouchEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -377,6 +380,8 @@ public class OledCanvasDrawable : IDrawable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(ICanvas canvas, RectF dirtyRect)
|
public void Draw(ICanvas canvas, RectF dirtyRect)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
float scale = _canvas.Scale;
|
float scale = _canvas.Scale;
|
||||||
|
|
||||||
@@ -440,6 +445,11 @@ public class OledCanvasDrawable : IDrawable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Error drawing canvas: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawElement(ICanvas canvas, PreviewElement element, float scale, bool isSelected)
|
private void DrawElement(ICanvas canvas, PreviewElement element, float scale, bool isSelected)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,12 +6,20 @@
|
|||||||
|
|
||||||
<!-- Note: For Android please see also Platforms\Android\Resources\values\colors.xml -->
|
<!-- Note: For Android please see also Platforms\Android\Resources\values\colors.xml -->
|
||||||
|
|
||||||
<Color x:Key="Primary">#512BD4</Color>
|
<Color x:Key="Primary">#1E88E5</Color>
|
||||||
<Color x:Key="PrimaryDark">#ac99ea</Color>
|
<Color x:Key="PrimaryDark">#1565C0</Color>
|
||||||
<Color x:Key="PrimaryDarkText">#242424</Color>
|
<Color x:Key="PrimaryLight">#E3F2FD</Color>
|
||||||
<Color x:Key="Secondary">#DFD8F7</Color>
|
<Color x:Key="Secondary">#CFD8DC</Color>
|
||||||
<Color x:Key="SecondaryDarkText">#9880e5</Color>
|
|
||||||
<Color x:Key="Tertiary">#2B0B98</Color>
|
<Color x:Key="Tertiary">#2B0B98</Color>
|
||||||
|
<Color x:Key="Accent">#03A9F4</Color>
|
||||||
|
<Color x:Key="Background">#F4F6F8</Color>
|
||||||
|
<Color x:Key="Surface">#FFFFFF</Color>
|
||||||
|
<Color x:Key="TextPrimary">#333333</Color>
|
||||||
|
<Color x:Key="TextSecondary">#555555</Color>
|
||||||
|
<Color x:Key="BorderColor">#E1E4E8</Color>
|
||||||
|
<Color x:Key="Success">#4CAF50</Color>
|
||||||
|
<Color x:Key="Error">#F44336</Color>
|
||||||
|
<Color x:Key="Warning">#FF9800</Color>
|
||||||
|
|
||||||
<Color x:Key="White">White</Color>
|
<Color x:Key="White">White</Color>
|
||||||
<Color x:Key="Black">Black</Color>
|
<Color x:Key="Black">Black</Color>
|
||||||
|
|||||||
@@ -83,15 +83,21 @@ public class StringMatchConverter : IValueConverter
|
|||||||
{
|
{
|
||||||
if (currentValue == targetValue)
|
if (currentValue == targetValue)
|
||||||
{
|
{
|
||||||
// Handle different return types based on an additional parameter
|
// Default to background coloring
|
||||||
if (parameter is string param2 && param2 == "TextColor")
|
Color returnColor = Application.Current.Resources["PrimaryLight"] as Color ?? Colors.LightBlue;
|
||||||
|
|
||||||
|
// Check for additional parameter context
|
||||||
|
if (targetValue.EndsWith(":TextColor"))
|
||||||
|
{
|
||||||
|
// Strip the ":TextColor" suffix before comparison
|
||||||
|
string strippedTarget = targetValue.Substring(0, targetValue.Length - 10);
|
||||||
|
if (currentValue == strippedTarget)
|
||||||
{
|
{
|
||||||
// Return text color for selected item
|
|
||||||
return Application.Current.Resources["Primary"] as Color ?? Colors.Black;
|
return Application.Current.Resources["Primary"] as Color ?? Colors.Black;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return background color for selected item
|
return returnColor;
|
||||||
return Application.Current.Resources["PrimaryLight"] as Color ?? Colors.LightBlue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,12 +86,6 @@
|
|||||||
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Surface}, Dark=White}" />
|
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Surface}, Dark=White}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style TargetType="TabBar">
|
|
||||||
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource Surface}, Dark=#333333}" />
|
|
||||||
<Setter Property="BarTextColor" Value="{AppThemeBinding Light=#777777, Dark=#777777}" />
|
|
||||||
<Setter Property="BarSelectedTextColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Primary}}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- Custom Styles -->
|
<!-- Custom Styles -->
|
||||||
|
|
||||||
<Style x:Key="HeaderLabelStyle" TargetType="Label">
|
<Style x:Key="HeaderLabelStyle" TargetType="Label">
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ using PCPal.Configurator.Views.OLED;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
//using Javax.Xml.Transform;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
//using Javax.Xml.Transform;
|
||||||
|
|
||||||
namespace PCPal.Configurator.ViewModels;
|
namespace PCPal.Configurator.ViewModels;
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
|
|
||||||
// Timer for sensor updates
|
// Timer for sensor updates
|
||||||
private Timer _sensorUpdateTimer;
|
private Timer _sensorUpdateTimer;
|
||||||
|
private CancellationTokenSource _sensorUpdateCts;
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
@@ -410,9 +411,9 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
IConfigurationService configService,
|
IConfigurationService configService,
|
||||||
ISerialPortService serialPortService)
|
ISerialPortService serialPortService)
|
||||||
{
|
{
|
||||||
_sensorService = sensorService;
|
_sensorService = sensorService ?? throw new ArgumentNullException(nameof(sensorService));
|
||||||
_configService = configService;
|
_configService = configService ?? throw new ArgumentNullException(nameof(configService));
|
||||||
_serialPortService = serialPortService;
|
_serialPortService = serialPortService ?? throw new ArgumentNullException(nameof(serialPortService));
|
||||||
|
|
||||||
// Initialize collections
|
// Initialize collections
|
||||||
_oledElements = new ObservableCollection<OledElement>();
|
_oledElements = new ObservableCollection<OledElement>();
|
||||||
@@ -422,6 +423,9 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
_templateList = new ObservableCollection<Template>();
|
_templateList = new ObservableCollection<Template>();
|
||||||
_customTemplates = new ObservableCollection<Template>();
|
_customTemplates = new ObservableCollection<Template>();
|
||||||
|
|
||||||
|
// Setup cancellation token source
|
||||||
|
_sensorUpdateCts = new CancellationTokenSource();
|
||||||
|
|
||||||
// Create views
|
// Create views
|
||||||
_visualEditorView = new OledVisualEditorView { BindingContext = this };
|
_visualEditorView = new OledVisualEditorView { BindingContext = this };
|
||||||
_markupEditorView = new OledMarkupEditorView { BindingContext = this };
|
_markupEditorView = new OledMarkupEditorView { BindingContext = this };
|
||||||
@@ -468,6 +472,25 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
_sensorUpdateTimer = new Timer(async (_) => await UpdateSensorDataAsync(), null, Timeout.Infinite, Timeout.Infinite);
|
_sensorUpdateTimer = new Timer(async (_) => await UpdateSensorDataAsync(), null, Timeout.Infinite, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~OledConfigViewModel()
|
||||||
|
{
|
||||||
|
CleanupResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupResources()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_sensorUpdateCts?.Cancel();
|
||||||
|
_sensorUpdateTimer?.Dispose();
|
||||||
|
_sensorUpdateCts?.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error cleaning up resources: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Initialize()
|
public async Task Initialize()
|
||||||
{
|
{
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
@@ -492,6 +515,7 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine($"Error initializing: {ex.Message}");
|
||||||
await Shell.Current.DisplayAlert("Error", $"Failed to initialize: {ex.Message}", "OK");
|
await Shell.Current.DisplayAlert("Error", $"Failed to initialize: {ex.Message}", "OK");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -501,6 +525,8 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadSensorsAsync()
|
private async Task LoadSensorsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
await _sensorService.UpdateSensorValuesAsync();
|
await _sensorService.UpdateSensorValuesAsync();
|
||||||
|
|
||||||
@@ -515,11 +541,21 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update on main thread to ensure thread safety
|
||||||
|
await MainThread.InvokeOnMainThreadAsync(() => {
|
||||||
AvailableSensors = new ObservableCollection<SensorItem>(sensors);
|
AvailableSensors = new ObservableCollection<SensorItem>(sensors);
|
||||||
FilteredSensors = new ObservableCollection<SensorItem>(sensors);
|
FilteredSensors = new ObservableCollection<SensorItem>(sensors);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error loading sensors: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadConfigAsync()
|
private async Task LoadConfigAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var config = await _configService.LoadConfigAsync();
|
var config = await _configService.LoadConfigAsync();
|
||||||
|
|
||||||
@@ -536,8 +572,16 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
await LoadExampleMarkupAsync();
|
await LoadExampleMarkupAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error loading configuration: {ex.Message}");
|
||||||
|
await LoadExampleMarkupAsync(); // Fallback to example
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadTemplatesAsync()
|
private async Task LoadTemplatesAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// Predefined templates
|
// Predefined templates
|
||||||
var templates = new List<Template>
|
var templates = new List<Template>
|
||||||
@@ -601,6 +645,11 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
CustomTemplates = new ObservableCollection<Template>(customTemplates);
|
CustomTemplates = new ObservableCollection<Template>(customTemplates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error loading templates: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SwitchTab(string tab)
|
private void SwitchTab(string tab)
|
||||||
{
|
{
|
||||||
@@ -647,6 +696,7 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine($"Error saving configuration: {ex.Message}");
|
||||||
await Shell.Current.DisplayAlert("Error", $"Failed to save configuration: {ex.Message}", "OK");
|
await Shell.Current.DisplayAlert("Error", $"Failed to save configuration: {ex.Message}", "OK");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -682,6 +732,7 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine($"Error previewing on device: {ex.Message}");
|
||||||
await Shell.Current.DisplayAlert("Error", $"Preview failed: {ex.Message}", "OK");
|
await Shell.Current.DisplayAlert("Error", $"Preview failed: {ex.Message}", "OK");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -691,6 +742,8 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task ResetLayoutAsync()
|
private async Task ResetLayoutAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
bool confirm = await Shell.Current.DisplayAlert(
|
bool confirm = await Shell.Current.DisplayAlert(
|
||||||
"Reset Layout",
|
"Reset Layout",
|
||||||
@@ -702,6 +755,11 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
await LoadExampleMarkupAsync();
|
await LoadExampleMarkupAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error resetting layout: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdatePreviewFromMarkup()
|
public void UpdatePreviewFromMarkup()
|
||||||
{
|
{
|
||||||
@@ -709,7 +767,12 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
{
|
{
|
||||||
// Parse the markup into preview elements
|
// Parse the markup into preview elements
|
||||||
var markupParser = new MarkupParser(_sensorService.GetAllSensorValues());
|
var markupParser = new MarkupParser(_sensorService.GetAllSensorValues());
|
||||||
PreviewElements = markupParser.ParseMarkup(OledMarkup);
|
var elements = markupParser.ParseMarkup(OledMarkup);
|
||||||
|
|
||||||
|
// Update on main thread to ensure thread safety
|
||||||
|
MainThread.BeginInvokeOnMainThread(() => {
|
||||||
|
PreviewElements = elements;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -717,16 +780,44 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateMarkupFromElements()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sb = new System.Text.StringBuilder();
|
||||||
|
|
||||||
|
foreach (var element in OledElements)
|
||||||
|
{
|
||||||
|
sb.AppendLine(element.ToMarkup());
|
||||||
|
}
|
||||||
|
|
||||||
|
OledMarkup = sb.ToString();
|
||||||
|
UpdatePreviewFromMarkup();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error updating markup from elements: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateSensorDataAsync()
|
private async Task UpdateSensorDataAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_sensorUpdateCts.Token.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
await _sensorService.UpdateSensorValuesAsync();
|
await _sensorService.UpdateSensorValuesAsync();
|
||||||
UpdatePreviewFromMarkup();
|
UpdatePreviewFromMarkup();
|
||||||
|
|
||||||
// Update available sensors
|
// Update available sensors
|
||||||
await LoadSensorsAsync();
|
await LoadSensorsAsync();
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Expected when cancellation is requested
|
||||||
|
Debug.WriteLine("Sensor update canceled");
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Log error but don't display to user since this happens in background
|
// Log error but don't display to user since this happens in background
|
||||||
@@ -735,20 +826,33 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ApplySensorFilter()
|
private void ApplySensorFilter()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(CurrentSensorFilter) || CurrentSensorFilter == "All")
|
if (string.IsNullOrEmpty(CurrentSensorFilter) || CurrentSensorFilter == "All")
|
||||||
{
|
{
|
||||||
|
MainThread.BeginInvokeOnMainThread(() => {
|
||||||
FilteredSensors = new ObservableCollection<SensorItem>(AvailableSensors);
|
FilteredSensors = new ObservableCollection<SensorItem>(AvailableSensors);
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var filtered = AvailableSensors.Where(s =>
|
var filtered = AvailableSensors.Where(s =>
|
||||||
s.HardwareName.Contains(CurrentSensorFilter, StringComparison.OrdinalIgnoreCase)).ToList();
|
s.HardwareName.Contains(CurrentSensorFilter, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||||
|
|
||||||
|
MainThread.BeginInvokeOnMainThread(() => {
|
||||||
FilteredSensors = new ObservableCollection<SensorItem>(filtered);
|
FilteredSensors = new ObservableCollection<SensorItem>(filtered);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error applying sensor filter: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddElement(string type)
|
private void AddElement(string type)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var element = new OledElement
|
var element = new OledElement
|
||||||
{
|
{
|
||||||
@@ -793,8 +897,15 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
// Update markup
|
// Update markup
|
||||||
UpdateMarkupFromElements();
|
UpdateMarkupFromElements();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error adding element: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DeleteSelectedElement()
|
private void DeleteSelectedElement()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (SelectedElement != null)
|
if (SelectedElement != null)
|
||||||
{
|
{
|
||||||
@@ -805,30 +916,26 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
UpdateMarkupFromElements();
|
UpdateMarkupFromElements();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public void UpdateMarkupFromElements()
|
|
||||||
{
|
{
|
||||||
var sb = new System.Text.StringBuilder();
|
Debug.WriteLine($"Error deleting element: {ex.Message}");
|
||||||
|
|
||||||
foreach (var element in OledElements)
|
|
||||||
{
|
|
||||||
sb.AppendLine(element.ToMarkup());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OledMarkup = sb.ToString();
|
|
||||||
UpdatePreviewFromMarkup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseMarkupToElementsAsync(string markup)
|
private async Task ParseMarkupToElementsAsync(string markup)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(markup))
|
if (string.IsNullOrEmpty(markup))
|
||||||
{
|
{
|
||||||
|
await MainThread.InvokeOnMainThreadAsync(() => {
|
||||||
OledElements.Clear();
|
OledElements.Clear();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var elements = new List<OledElement>();
|
var elements = new List<OledElement>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// Parse text elements
|
// Parse text elements
|
||||||
foreach (Match match in Regex.Matches(markup, @"<text\s+x=(\d+)\s+y=(\d+)(?:\s+size=(\d+))?>([^<]*)</text>"))
|
foreach (Match match in Regex.Matches(markup, @"<text\s+x=(\d+)\s+y=(\d+)(?:\s+size=(\d+))?>([^<]*)</text>"))
|
||||||
{
|
{
|
||||||
@@ -914,6 +1021,11 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error parsing markup: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<PreviewElement>> ParseMarkupToPreviewElements(string markup)
|
private async Task<List<PreviewElement>> ParseMarkupToPreviewElements(string markup)
|
||||||
{
|
{
|
||||||
@@ -946,6 +1058,8 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void AddSensorToDisplay(string sensorId)
|
private void AddSensorToDisplay(string sensorId)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// Find the sensor
|
// Find the sensor
|
||||||
var sensor = AvailableSensors.FirstOrDefault(s => s.Id == sensorId);
|
var sensor = AvailableSensors.FirstOrDefault(s => s.Id == sensorId);
|
||||||
@@ -997,8 +1111,15 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
// Update markup
|
// Update markup
|
||||||
UpdateMarkupFromElements();
|
UpdateMarkupFromElements();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error adding sensor to display: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task BrowseIconsAsync()
|
private async Task BrowseIconsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// A mock implementation - in a real app, you'd implement a proper icon browser
|
// A mock implementation - in a real app, you'd implement a proper icon browser
|
||||||
var icons = new string[] { "cpu", "gpu", "ram", "disk", "network", "fan" };
|
var icons = new string[] { "cpu", "gpu", "ram", "disk", "network", "fan" };
|
||||||
@@ -1009,8 +1130,15 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
SelectedElementIconName = result;
|
SelectedElementIconName = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error browsing icons: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InsertMarkupTemplate(string type)
|
private void InsertMarkupTemplate(string type)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
string template = string.Empty;
|
string template = string.Empty;
|
||||||
|
|
||||||
@@ -1023,6 +1151,22 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
case "bar":
|
case "bar":
|
||||||
template = "<bar x=10 y=30 w=100 h=8 val=75 />";
|
template = "<bar x=10 y=30 w=100 h=8 val=75 />";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "rect":
|
||||||
|
template = "<rect x=10 y=40 w=50 h=20 />";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "box":
|
||||||
|
template = "<box x=70 y=40 w=50 h=20 />";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "line":
|
||||||
|
template = "<line x1=10 y1=50 x2=60 y2=50 />";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "icon":
|
||||||
|
template = "<icon x=130 y=20 name=cpu />";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(template))
|
if (!string.IsNullOrEmpty(template))
|
||||||
@@ -1031,8 +1175,15 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
UpdatePreviewFromMarkup();
|
UpdatePreviewFromMarkup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error inserting markup template: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task InsertSensorVariableAsync()
|
private async Task InsertSensorVariableAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// Create a list of sensor options
|
// Create a list of sensor options
|
||||||
var options = AvailableSensors.Select(s => $"{s.DisplayName} ({s.Id})").ToArray();
|
var options = AvailableSensors.Select(s => $"{s.DisplayName} ({s.Id})").ToArray();
|
||||||
@@ -1050,20 +1201,42 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
UpdatePreviewFromMarkup();
|
UpdatePreviewFromMarkup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error inserting sensor variable: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadExampleMarkupAsync()
|
private async Task LoadExampleMarkupAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
OledMarkup = await _sensorService.CreateExampleMarkupAsync();
|
OledMarkup = await _sensorService.CreateExampleMarkupAsync();
|
||||||
await ParseMarkupToElementsAsync(OledMarkup);
|
await ParseMarkupToElementsAsync(OledMarkup);
|
||||||
UpdatePreviewFromMarkup();
|
UpdatePreviewFromMarkup();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error loading example markup: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> CreateSystemMonitorTemplateAsync()
|
private async Task<string> CreateSystemMonitorTemplateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
return await _sensorService.CreateExampleMarkupAsync();
|
return await _sensorService.CreateExampleMarkupAsync();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error creating system monitor template: {ex.Message}");
|
||||||
|
return "<text x=10 y=20 size=1>System Monitor</text>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> CreateTemperatureMonitorTemplateAsync()
|
private async Task<string> CreateTemperatureMonitorTemplateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var sb = new System.Text.StringBuilder();
|
var sb = new System.Text.StringBuilder();
|
||||||
|
|
||||||
@@ -1087,8 +1260,16 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error creating temperature monitor template: {ex.Message}");
|
||||||
|
return "<text x=10 y=20 size=1>Temperature Monitor</text>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> CreateNetworkMonitorTemplateAsync()
|
private async Task<string> CreateNetworkMonitorTemplateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var sb = new System.Text.StringBuilder();
|
var sb = new System.Text.StringBuilder();
|
||||||
|
|
||||||
@@ -1097,8 +1278,16 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error creating network monitor template: {ex.Message}");
|
||||||
|
return "<text x=10 y=20 size=1>Network Monitor</text>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> CreateStorageMonitorTemplateAsync()
|
private async Task<string> CreateStorageMonitorTemplateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var sb = new System.Text.StringBuilder();
|
var sb = new System.Text.StringBuilder();
|
||||||
|
|
||||||
@@ -1107,8 +1296,16 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error creating storage monitor template: {ex.Message}");
|
||||||
|
return "<text x=10 y=20 size=1>Storage Monitor</text>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UseSelectedTemplateAsync()
|
private async Task UseSelectedTemplateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (SelectedTemplate == null)
|
if (SelectedTemplate == null)
|
||||||
{
|
{
|
||||||
@@ -1128,8 +1325,15 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
SwitchTab("markup");
|
SwitchTab("markup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error using selected template: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveAsTemplateAsync()
|
private async Task SaveAsTemplateAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(NewTemplateName))
|
if (string.IsNullOrWhiteSpace(NewTemplateName))
|
||||||
{
|
{
|
||||||
@@ -1192,8 +1396,16 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
|
|
||||||
await Shell.Current.DisplayAlert("Success", "Template saved successfully!", "OK");
|
await Shell.Current.DisplayAlert("Success", "Template saved successfully!", "OK");
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error saving as template: {ex.Message}");
|
||||||
|
await Shell.Current.DisplayAlert("Error", $"Failed to save template: {ex.Message}", "OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UseCustomTemplateAsync(Template template)
|
private async Task UseCustomTemplateAsync(Template template)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (template == null)
|
if (template == null)
|
||||||
{
|
{
|
||||||
@@ -1213,8 +1425,15 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
SwitchTab("markup");
|
SwitchTab("markup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error using custom template: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DeleteCustomTemplateAsync(Template template)
|
private async Task DeleteCustomTemplateAsync(Template template)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (template == null)
|
if (template == null)
|
||||||
{
|
{
|
||||||
@@ -1240,6 +1459,11 @@ public class OledConfigViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error deleting custom template: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Template
|
public class Template
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ public interface ISerialPortService
|
|||||||
event EventHandler<bool> ConnectionStatusChanged;
|
event EventHandler<bool> ConnectionStatusChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SerialPortService : ISerialPortService
|
public class SerialPortService : ISerialPortService, IDisposable
|
||||||
{
|
{
|
||||||
private SerialPort _serialPort;
|
private SerialPort _serialPort;
|
||||||
private bool _isConnected;
|
private bool _isConnected;
|
||||||
private string _currentPort;
|
private string _currentPort;
|
||||||
private readonly ILogger<SerialPortService> _logger;
|
private readonly ILogger<SerialPortService> _logger;
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
public bool IsConnected => _isConnected;
|
public bool IsConnected => _isConnected;
|
||||||
public string CurrentPort => _currentPort;
|
public string CurrentPort => _currentPort;
|
||||||
@@ -30,13 +31,19 @@ public class SerialPortService : ISerialPortService
|
|||||||
|
|
||||||
public SerialPortService(ILogger<SerialPortService> logger)
|
public SerialPortService(ILogger<SerialPortService> logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_isConnected = false;
|
_isConnected = false;
|
||||||
_currentPort = string.Empty;
|
_currentPort = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ConnectAsync(string port)
|
public async Task<bool> ConnectAsync(string port)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(port))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Cannot connect to null or empty port name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Disconnect if already connected
|
// Disconnect if already connected
|
||||||
@@ -71,6 +78,7 @@ public class SerialPortService : ISerialPortService
|
|||||||
// Not a valid PCPal device, close the connection
|
// Not a valid PCPal device, close the connection
|
||||||
_serialPort.Close();
|
_serialPort.Close();
|
||||||
_serialPort.Dispose();
|
_serialPort.Dispose();
|
||||||
|
_serialPort = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -81,6 +89,7 @@ public class SerialPortService : ISerialPortService
|
|||||||
{
|
{
|
||||||
_serialPort.Close();
|
_serialPort.Close();
|
||||||
_serialPort.Dispose();
|
_serialPort.Dispose();
|
||||||
|
_serialPort = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -126,8 +135,15 @@ public class SerialPortService : ISerialPortService
|
|||||||
|
|
||||||
public async Task<bool> SendCommandAsync(string command)
|
public async Task<bool> SendCommandAsync(string command)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(command))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Cannot send null or empty command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_isConnected || _serialPort == null || !_serialPort.IsOpen)
|
if (!_isConnected || _serialPort == null || !_serialPort.IsOpen)
|
||||||
{
|
{
|
||||||
|
_logger.LogWarning("Cannot send command: Device not connected");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,9 +171,10 @@ public class SerialPortService : ISerialPortService
|
|||||||
|
|
||||||
foreach (string port in ports)
|
foreach (string port in ports)
|
||||||
{
|
{
|
||||||
|
SerialPort testPort = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using SerialPort testPort = new SerialPort(port, 115200)
|
testPort = new SerialPort(port, 115200)
|
||||||
{
|
{
|
||||||
ReadTimeout = 1000,
|
ReadTimeout = 1000,
|
||||||
WriteTimeout = 1000
|
WriteTimeout = 1000
|
||||||
@@ -173,15 +190,33 @@ public class SerialPortService : ISerialPortService
|
|||||||
response.Contains("DISPLAY_TYPE:OLED") ||
|
response.Contains("DISPLAY_TYPE:OLED") ||
|
||||||
response.Contains("DISPLAY_TYPE:TFT"))
|
response.Contains("DISPLAY_TYPE:TFT"))
|
||||||
{
|
{
|
||||||
|
// Make sure to close and dispose the port before returning
|
||||||
|
testPort.Close();
|
||||||
|
testPort.Dispose();
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
testPort.Close();
|
testPort.Close();
|
||||||
|
testPort.Dispose();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Skip ports that can't be opened
|
_logger.LogDebug(ex, $"Failed to check port {port}");
|
||||||
continue;
|
|
||||||
|
// Clean up the port if an exception occurs
|
||||||
|
if (testPort != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (testPort.IsOpen)
|
||||||
|
testPort.Close();
|
||||||
|
testPort.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception disposalEx)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(disposalEx, $"Error disposing test port {port}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,8 +259,10 @@ public class SerialPortService : ISerialPortService
|
|||||||
|
|
||||||
return connected;
|
return connected;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
_logger.LogError(ex, "Error checking connection");
|
||||||
|
|
||||||
if (_isConnected)
|
if (_isConnected)
|
||||||
{
|
{
|
||||||
_isConnected = false;
|
_isConnected = false;
|
||||||
@@ -236,4 +273,40 @@ public class SerialPortService : ISerialPortService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
if (_serialPort != null && _serialPort.IsOpen)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serialPort.Close();
|
||||||
|
_serialPort.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error disposing serial port");
|
||||||
|
}
|
||||||
|
_serialPort = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~SerialPortService()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user