Files
GSS2Rework/GSS2/Commands/UI.cs
T
amkovkov 72ae26eee7 comprehensive update
1) remove unused components: camera view (due it causes segmentation faults), compute resources and all related, illuminator controller (due in useless without camera view), image storage service, temperature and humidity service
2) database schema - reduce tables references, features storing in records themselves in compressed form, add records creation and editing date and time, add separator comment column
3) analysis - rework of pipeline and ui, now database storing only raw data and all display values calculated from it
4) lights service - add reconnection if disconnected
5) add width and height command line arguments
6) fix some typos and other issues
2026-06-29 15:13:29 +03:00

226 lines
9.5 KiB
C#

using System.CommandLine;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Avalonia;
using Avalonia.OpenGL.Egl;
using GSS2.Core;
using GSS2.Core.Logging;
using GSS2.Core.Analysis.AmineContent.Database;
namespace GSS2.Commands;
public static class UI
{
public enum RenderingModes
{
X11,
DRM
}
public static readonly RootCommand RootCommand;
public static readonly Command Command;
public static readonly Option<bool> Fullscreen;
public static readonly Option<RenderingModes> RenderingMode;
public static readonly Option<bool> HideConsole;
public static readonly Option<bool> Mock;
public static readonly Option<int> Width;
public static readonly Option<int> Height;
static UI()
{
// Создаём команды, опции, аргументы и пр.
RootCommand = new("Запустить графическое приложение");
Command = new("ui")
{
Description = "Запустить графическое приложение"
};
Fullscreen = new("--fullscreen", ["-f"])
{
Description = "Запустить приложение в полноэкранном режиме",
};
RenderingMode = new("--mode", ["--rendering-mode", "-m"])
{
Description = "Режим рендеринга",
DefaultValueFactory = (_) => RenderingModes.X11
};
HideConsole = new("--hide-console", ["--no-console", "-c"])
{
Description = "Отключить вывод в консоль",
DefaultValueFactory = (result) => result.GetValue(RenderingMode) == RenderingModes.DRM
};
Mock = new("--mock")
{
Description = "Запуск в фиктивном режиме без инициализации и использования периферии",
DefaultValueFactory = (result) => false
};
Width = new("--width")
{
Description = "Ширина окна, если меньше или равна 0, то устанавливается ширина по-умолчанию",
DefaultValueFactory = (result) => 0
};
Height = new("--height")
{
Description = "Высота окна, если меньше или равна 0, то устанавливается высота по-умолчанию",
DefaultValueFactory = (result) => 0
};
// Изменяем описания стандартных опций
Helper.TranslateDefaultOptionDescriptions(RootCommand);
// Наполняем команды
RootCommand.Add(Fullscreen);
RootCommand.Add(RenderingMode);
RootCommand.Add(HideConsole);
RootCommand.Add(Mock);
RootCommand.Add(Width);
RootCommand.Add(Height);
RootCommand.SetAction(CommandAction);
Command.Add(Fullscreen);
Command.Add(RenderingMode);
Command.Add(HideConsole);
Command.Add(Mock);
Command.Add(Width);
Command.Add(Height);
Command.SetAction(CommandAction);
}
/// <summary>
/// Получить команду как корневую
/// </summary>
public static RootCommand GetCommand() => RootCommand;
/// <summary>
/// Зарегистрировать команду как подкоманду
/// </summary>
/// <param name="command">Команда в которой необходимо зарегистрировать подкоманду</param>
public static void RegisterCommand(Command command) => command.Add(Command);
private static void CommandAction(ParseResult result)
{
/// Получаем значения аргументов командной строки
var fullscreen = result.GetValue(Fullscreen);
var renderingMode = result.GetValue(RenderingMode);
var hideConsole = result.GetValue(HideConsole);
var mock = result.GetValue(Mock);
var width = result.GetValue(Width);
var height = result.GetValue(Height);
// Если выбран режим рендеринга DRM, то приложение будет рисоваться вместе с консолью,
// поэтому если не перехватить консоль, то вывод в неё будет рисоваться вместе с интерфейсом
if (hideConsole)
SilenceConsole();
// Собираем хост
var host = BuildHost(mock);
// Запускаем хост асинхронно без блокировки потока
_ = host.RunAsync();
// Собираем графическое приложение
var ui = BuildApplication(host, fullscreen, width, height);
switch (renderingMode)
{
case RenderingModes.X11:
// Если мы запускаемся под X11,
// то очевидно у нас есть полноценный рабочий стол,
// поэтому запускаем графический интерфейс в классическом режиме с главным окном
ui.StartWithClassicDesktopLifetime(Program.Args);
break;
case RenderingModes.DRM:
// Если мы запускаемся в DRM (Direct Rendering Mode),
// то у нас нет рабочего стола и окон,
// потому что отрисовка (вроде как) идёт напрямую в кадровый буфер,
// поэтому запускаем графический интерфейс в DRM.
// Если работает X11, или любая другая графическая система,
// то запустить приложение в DRM не получится,
// потому что DRI (Direct Rendering Interface) занят этой графической системой
// TODO: Подумать над тем, чтобы брать DRI и scale для DRM рендеринга из переменных среды
// TODO: Подумать над тем, чтобы при запуске в DRM убивать приложения, которые занимают DRI
ui.StartLinuxDrm(Program.Args, "/dev/dri/card1", 1.0);
break;
}
}
private static void SilenceConsole()
{
var start = new ThreadStart(() =>
{
Console.CursorVisible = false;
while (true)
Console.ReadKey(true);
});
var thread = new Thread(start)
{
IsBackground = true
};
thread.Start();
}
private static IHost BuildHost(bool mock)
{
// Создаём хост
var builder = Host.CreateApplicationBuilder();
// Добавляем конфигурацию из appsettings.json
builder.Configuration.AddAppSettingsJson();
// Конфигурируем логгер
builder.Logging.ConfigureLogging();
builder.Services.AddSingleton<LibCameraLogSink>();
// Добавляем сервисы баз данных
builder.Services.AddAmineContentContext(builder.Configuration);
// Добавляем сервисы аппаратной части и прочее
builder.Services.AddLightsService("Hardware:Lights");
if (!mock)
{
// builder.Services.AddTemperatureHumidityService("Hardware:TemperatureHumidity");
builder.Services.AddIlluminatorService("Hardware:Illuminator");
builder.Services.AddCameraService("Hardware:Camera", true);
}
builder.Services.AddAmineContentImageCapturerService(mock);
builder.Services.AddAmineContentAnalyzerService();
// Добавляем элементы интерфейса как сервисы
builder.Services.AddViewsAndModels();
// Собираем хост
var host = builder.Build();
// Поскольку LibCameraLogSink, который перенаправляет логи из libcamera в хост,
// не будет создан ни одним из других сервисов, его необходимо создать превентивно
if (!mock)
host.Services.GetRequiredService<LibCameraLogSink>();
// Применяем миграции баз данных если их схема данных изменялась
using (var scope = host.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<Context>();
context.Database.Migrate();
}
return host;
}
private static AppBuilder BuildApplication(IHost host, bool fullscreen, int width, int height) =>
AppBuilder.Configure(() => new Application(host.Services, fullscreen, width, height))
.UsePlatformDetect()
.UseSkia()
.LogToTrace()
.With(new X11PlatformOptions
{
RenderingMode = [X11RenderingMode.Egl],
})
.With(new EglDisplayOptions
{
SupportsContextSharing = true
});
}