2025-12-17 15:34:34 -07:00
|
|
|
using System.Linq;
|
|
|
|
|
using Avalonia.Controls;
|
|
|
|
|
using Avalonia.Interactivity;
|
|
|
|
|
using UnifiedFarmLauncher.Models;
|
|
|
|
|
using UnifiedFarmLauncher.Services;
|
|
|
|
|
using UnifiedFarmLauncher.ViewModels;
|
|
|
|
|
using Avalonia.Controls.Primitives;
|
|
|
|
|
using MsBox.Avalonia;
|
|
|
|
|
using MsBox.Avalonia.Enums;
|
|
|
|
|
|
|
|
|
|
namespace UnifiedFarmLauncher.Views
|
|
|
|
|
{
|
|
|
|
|
public partial class MainWindow : Window
|
|
|
|
|
{
|
|
|
|
|
private readonly ConfigService _configService = new();
|
|
|
|
|
private readonly SshService _sshService = new();
|
|
|
|
|
private readonly WorkerControllerService _controllerService;
|
|
|
|
|
private readonly AttachService _attachService;
|
|
|
|
|
|
|
|
|
|
public MainWindow()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
_controllerService = new WorkerControllerService(_sshService, _configService);
|
|
|
|
|
_attachService = new AttachService(_sshService, _controllerService);
|
|
|
|
|
DataContext = new MainWindowViewModel();
|
|
|
|
|
SetupEventHandlers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeComponent()
|
|
|
|
|
{
|
|
|
|
|
Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetupEventHandlers()
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
this.FindControl<Button>("AddWorkerButton")!.Click += AddWorkerButton_Click;
|
|
|
|
|
this.FindControl<Button>("EditWorkerButton")!.Click += EditWorkerButton_Click;
|
|
|
|
|
this.FindControl<Button>("DeleteWorkerButton")!.Click += DeleteWorkerButton_Click;
|
|
|
|
|
this.FindControl<Button>("StartWorkerButton")!.Click += StartWorkerButton_Click;
|
|
|
|
|
this.FindControl<Button>("StopWorkerButton")!.Click += StopWorkerButton_Click;
|
|
|
|
|
this.FindControl<Button>("AttachWorkerButton")!.Click += AttachWorkerButton_Click;
|
2025-12-17 16:19:08 -07:00
|
|
|
this.FindControl<Button>("SettingsButton")!.Click += SettingsButton_Click;
|
2025-12-17 16:46:35 -07:00
|
|
|
this.FindControl<Button>("OperationModeToggleButton")!.Click += OperationModeToggleButton_Click;
|
2025-12-17 16:15:11 -07:00
|
|
|
this.FindControl<TabControl>("WorkerTypeTabs")!.SelectionChanged += WorkerTypeTabs_SelectionChanged;
|
|
|
|
|
this.FindControl<DataGrid>("WorkersGrid")!.SelectionChanged += WorkersGrid_SelectionChanged;
|
2025-12-17 16:46:35 -07:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OperationModeToggleButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (DataContext is MainWindowViewModel vm)
|
|
|
|
|
{
|
|
|
|
|
vm.OperationMode = vm.OperationMode == "sheepit" ? "flamenco" : "sheepit";
|
|
|
|
|
}
|
2025-12-17 15:34:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void AddWorkerButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var dialog = new WorkerEditWindow();
|
|
|
|
|
if (await dialog.ShowDialogAsync(this))
|
|
|
|
|
{
|
|
|
|
|
((MainWindowViewModel)DataContext!).RefreshWorkers();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void EditWorkerButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
if (this.FindControl<DataGrid>("WorkersGrid")?.SelectedItem is WorkerConfig worker)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
var dialog = new WorkerEditWindow(worker);
|
|
|
|
|
if (await dialog.ShowDialogAsync(this))
|
|
|
|
|
{
|
|
|
|
|
((MainWindowViewModel)DataContext!).RefreshWorkers();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void DeleteWorkerButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
if (this.FindControl<DataGrid>("WorkersGrid")?.SelectedItem is WorkerConfig worker)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Delete Worker",
|
|
|
|
|
$"Are you sure you want to delete worker '{worker.Name}'?",
|
2025-12-17 15:55:45 -07:00
|
|
|
ButtonEnum.YesNo, MsBox.Avalonia.Enums.Icon.Warning);
|
2025-12-17 15:34:34 -07:00
|
|
|
var result = await box.ShowAsync();
|
|
|
|
|
|
|
|
|
|
if (result == ButtonResult.Yes)
|
|
|
|
|
{
|
|
|
|
|
_configService.DeleteWorker(worker.Id);
|
|
|
|
|
((MainWindowViewModel)DataContext!).RefreshWorkers();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void StartWorkerButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
if (this.FindControl<DataGrid>("WorkersGrid")?.SelectedItem is WorkerConfig worker)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-12-17 16:46:35 -07:00
|
|
|
var vm = (MainWindowViewModel)DataContext!;
|
|
|
|
|
string? workerType = vm.OperationMode;
|
2025-12-17 15:34:34 -07:00
|
|
|
|
2025-12-17 16:46:35 -07:00
|
|
|
// Verify the worker supports the selected operation mode
|
|
|
|
|
if (workerType == "sheepit" && worker.WorkerTypes.SheepIt == null)
|
|
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Error",
|
|
|
|
|
$"Worker '{worker.Name}' does not have SheepIt configured.",
|
|
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error);
|
|
|
|
|
await box.ShowAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (workerType == "flamenco" && worker.WorkerTypes.Flamenco == null)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Error",
|
2025-12-17 16:46:35 -07:00
|
|
|
$"Worker '{worker.Name}' does not have Flamenco configured.",
|
2025-12-17 15:55:45 -07:00
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error);
|
2025-12-17 15:34:34 -07:00
|
|
|
await box.ShowAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _controllerService.StartWorkerAsync(worker, workerType);
|
|
|
|
|
var successBox = MessageBoxManager.GetMessageBoxStandard("Start Worker",
|
2025-12-17 16:46:35 -07:00
|
|
|
$"Worker '{worker.Name}' ({vm.OperationModeDisplayName}) started successfully.",
|
2025-12-17 15:55:45 -07:00
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Success);
|
2025-12-17 15:34:34 -07:00
|
|
|
await successBox.ShowAsync();
|
2025-12-17 16:46:35 -07:00
|
|
|
vm.RefreshWorkers();
|
2025-12-17 15:34:34 -07:00
|
|
|
}
|
|
|
|
|
catch (System.Exception ex)
|
|
|
|
|
{
|
2025-12-17 17:18:27 -07:00
|
|
|
var errorDialog = new ErrorDialog($"Failed to start worker: {ex.Message}");
|
|
|
|
|
await errorDialog.ShowDialog(this);
|
2025-12-17 15:34:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void StopWorkerButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
if (this.FindControl<DataGrid>("WorkersGrid")?.SelectedItem is WorkerConfig worker)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-12-17 16:46:35 -07:00
|
|
|
var vm = (MainWindowViewModel)DataContext!;
|
|
|
|
|
string workerType = vm.OperationMode;
|
2025-12-17 15:34:34 -07:00
|
|
|
|
2025-12-17 16:46:35 -07:00
|
|
|
// Verify the worker supports the selected operation mode
|
|
|
|
|
if (workerType == "sheepit" && worker.WorkerTypes.SheepIt == null)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Error",
|
2025-12-17 16:46:35 -07:00
|
|
|
$"Worker '{worker.Name}' does not have SheepIt configured.",
|
|
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error);
|
|
|
|
|
await box.ShowAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (workerType == "flamenco" && worker.WorkerTypes.Flamenco == null)
|
|
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Error",
|
|
|
|
|
$"Worker '{worker.Name}' does not have Flamenco configured.",
|
2025-12-17 15:55:45 -07:00
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error);
|
2025-12-17 15:34:34 -07:00
|
|
|
await box.ShowAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _controllerService.StopWorkerAsync(worker, workerType);
|
|
|
|
|
var successBox = MessageBoxManager.GetMessageBoxStandard("Stop Worker",
|
2025-12-17 16:46:35 -07:00
|
|
|
$"Stop command sent to worker '{worker.Name}' ({vm.OperationModeDisplayName}).",
|
2025-12-17 15:55:45 -07:00
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Info);
|
2025-12-17 15:34:34 -07:00
|
|
|
await successBox.ShowAsync();
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception ex)
|
|
|
|
|
{
|
2025-12-17 17:18:27 -07:00
|
|
|
var errorDialog = new ErrorDialog($"Failed to stop worker: {ex.Message}");
|
|
|
|
|
await errorDialog.ShowDialog(this);
|
2025-12-17 15:34:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void AttachWorkerButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
if (this.FindControl<DataGrid>("WorkersGrid")?.SelectedItem is WorkerConfig worker)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-12-17 16:46:35 -07:00
|
|
|
var vm = (MainWindowViewModel)DataContext!;
|
|
|
|
|
string workerType = vm.OperationMode;
|
2025-12-17 15:34:34 -07:00
|
|
|
|
2025-12-17 16:46:35 -07:00
|
|
|
// Verify the worker supports the selected operation mode
|
|
|
|
|
if (workerType == "sheepit" && worker.WorkerTypes.SheepIt == null)
|
|
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Error",
|
|
|
|
|
$"Worker '{worker.Name}' does not have SheepIt configured.",
|
|
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error);
|
|
|
|
|
await box.ShowAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (workerType == "flamenco" && worker.WorkerTypes.Flamenco == null)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
var box = MessageBoxManager.GetMessageBoxStandard("Error",
|
2025-12-17 16:46:35 -07:00
|
|
|
$"Worker '{worker.Name}' does not have Flamenco configured.",
|
2025-12-17 15:55:45 -07:00
|
|
|
ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error);
|
2025-12-17 15:34:34 -07:00
|
|
|
await box.ShowAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _attachService.AttachToWorkerAsync(worker, workerType);
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception ex)
|
|
|
|
|
{
|
2025-12-17 17:18:27 -07:00
|
|
|
var errorDialog = new ErrorDialog($"Failed to attach to worker: {ex.Message}");
|
|
|
|
|
await errorDialog.ShowDialog(this);
|
2025-12-17 15:34:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WorkerTypeTabs_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
if (this.FindControl<TabControl>("WorkerTypeTabs")?.SelectedItem is TabItem tab)
|
2025-12-17 15:34:34 -07:00
|
|
|
{
|
|
|
|
|
var type = tab.Header?.ToString() ?? "All";
|
|
|
|
|
if (type == "All Workers") type = "All";
|
|
|
|
|
((MainWindowViewModel)DataContext!).SelectedWorkerType = type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 16:19:08 -07:00
|
|
|
private async void SettingsButton_Click(object? sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var dialog = new GlobalSettingsWindow();
|
|
|
|
|
await dialog.ShowDialogAsync(this);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 15:34:34 -07:00
|
|
|
private void WorkersGrid_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (DataContext is MainWindowViewModel vm)
|
|
|
|
|
{
|
2025-12-17 16:15:11 -07:00
|
|
|
vm.SelectedWorker = this.FindControl<DataGrid>("WorkersGrid")?.SelectedItem as WorkerConfig;
|
2025-12-17 15:34:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|