This commit is contained in:
EugeneTes
2026-03-19 09:03:37 +01:00
parent 14fe822962
commit 83597b8763
55 changed files with 2136 additions and 32 deletions

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link href="_content/VisionBuilder.UI.Blazor/css/visionbuilder.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
@inherits LayoutComponentBase
@using MudBlazor
@using VisionBuilder.UI.Common.ViewModel
@using VisionBuilder.UI.Common.Services
@using VisionBuilder.UI.Common
@inject MainWindowVM MainWindowVm
@inject IPasswordInputService PasswordInputService
@implements IDisposable
<MudThemeProvider Theme="@_currentTheme" />
<MudPopoverProvider />
<MudDialogProvider />
<MudSnackbarProvider />
<div class="app-root @(_testMode ? "test-mode" : "")">
<div class="app-content">
@Body
</div>
<div class="app-divider"></div>
<div class="app-toolbar">
<div class="toolbar-buttons">
@if (_showTestModeButton)
{
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Class="toolbar-btn" OnClick="ToggleTestMode">Test Mode</MudButton>
}
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Class="toolbar-btn" OnClick="OnSettings">Settings</MudButton>
</div>
<span class="toolbar-version">@_version</span>
</div>
</div>

View File

@@ -0,0 +1,103 @@
using System.ComponentModel;
using System.Reflection;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using VisionBuilder.UI.Blazor.Services;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Services;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
using Ninject;
namespace VisionBuilder.UI.Blazor.Uno.Components.Layout;
public partial class MainLayout : IDisposable
{
[Microsoft.AspNetCore.Components.Inject] private IDialogService DialogService { get; set; } = null!;
[Microsoft.AspNetCore.Components.Inject] private IKernel Kernel { get; set; } = null!;
private bool _testMode;
private bool _showTestModeButton;
private string _version = "";
private readonly MudTheme _inspectronTheme = new()
{
PaletteLight = new PaletteLight
{
Primary = "#d32f2f",
AppbarBackground = "#37474f"
},
LayoutProperties = new LayoutProperties
{
DefaultBorderRadius = "0px"
}
};
private readonly MudTheme _testModeTheme = new()
{
PaletteLight = new PaletteLight
{
Primary = "#2e7d32",
AppbarBackground = "#2e7d32"
},
LayoutProperties = new LayoutProperties
{
DefaultBorderRadius = "0px"
}
};
private MudTheme _currentTheme = null!;
protected override void OnInitialized()
{
_currentTheme = _inspectronTheme;
_version = $"v{Assembly.GetEntryAssembly()?.GetName().Version?.ToString(3) ?? "0.0.0"}";
var uiConfig = Kernel.Get<UIConfiguration>();
_showTestModeButton = uiConfig.ShowTestModeButton;
MainWindowVm.PropertyChanged += OnPropertyChanged;
// Wire up dialog services
if (Kernel.Get<IRecipeSelectionDialogService>() is BlazorRecipeSelectionDialogService recipeService)
recipeService.SetDialogService(DialogService);
if (Kernel.Get<IImagePreviewService>() is BlazorImagePreviewService imageService)
imageService.SetDialogService(DialogService);
if (Kernel.Get<IPasswordInputService>() is BlazorPasswordInputService passwordService)
passwordService.SetDialogService(DialogService);
if (Kernel.Get<ISettingsService>() is BlazorSettingsService settingsService)
settingsService.SetDialogService(DialogService);
}
private async void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(MainWindowVM.TestMode))
{
_testMode = MainWindowVm.TestMode;
_currentTheme = _testMode ? _testModeTheme : _inspectronTheme;
await InvokeAsync(StateHasChanged);
}
}
private void ToggleTestMode()
{
MainWindowVm.ToggleTestModeCommand.Execute(null);
}
private async Task OnSettings()
{
var uiConfig = Kernel.Get<UIConfiguration>();
if (uiConfig.ProtectSettingsWithPassword)
{
var password = await PasswordInputService.GetPasswordAsync();
if (password != uiConfig.AdminPassword)
return;
}
MainWindowVm.SettingsCommand.Execute(null);
}
public void Dispose()
{
MainWindowVm.PropertyChanged -= OnPropertyChanged;
}
}

View File

@@ -0,0 +1,7 @@
<Router AppAssembly="typeof(Program).Assembly"
AdditionalAssemblies="new[] { typeof(VisionBuilder.UI.Blazor.Components.SingleCameraControl).Assembly }">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>

View File

@@ -0,0 +1,9 @@
@page "/"
@using VisionBuilder.UI.Blazor.Components
@using VisionBuilder.UI.Common
@using VisionBuilder.UI.Common.ViewModel
@inject MainWindowVM MainWindowVm
<PageTitle>VisionBuilder</PageTitle>
<SingleCameraControl ViewModel="@MainWindowVm.SingleCameraVms[0]" />

View File

@@ -0,0 +1,104 @@
using Inspectron.Settings;
using Lindt.Colorballs.Duo.Utils;
using MudBlazor.Services;
using Ninject;
using Ninject.Extensions.ChildKernel;
using VisionBuilder.UI.Blazor;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Plugins;
using VisionBuilder.UI.Common.Processing;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.Console;
using VisionBuilder.UI.IOCommander;
using VisionBuilder.UI.Recipes.HawkeyeRecipe;
using VisionBuilder.UI.Ringbuffer;
using VisionBuilder.UI.Statistics;
const string CAMERA1 = "Camera 1";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddMudServices();
// Parse command line args
CommandLineParser commandLineParser = new CommandLineParser(args);
// Setup settings
InspectronSettings settings = new InspectronSettings("..\\Data\\Config");
var mainKernel = VisionBuilder.UI.Common.VisionBuilder.CreateMainKernel(settings);
var profile = commandLineParser.GetStringArgument("profile", 'p');
mainKernel.UsePlugins(profile);
mainKernel
.UseConsole()
.UseIOCommander([CAMERA1])
.UseBlazorServices()
.RegisterGlobalPlugins();
mainKernel.Get<ILoadingService>().StartLoading("Camera initialization");
// CAMERA 1 //
ChildKernel kernelCamera1 = new ChildKernel(mainKernel);
kernelCamera1
.RegisterCamera(CAMERA1)
.UseStatistics(CAMERA1)
.UseRingbuffer(CAMERA1)
.UseHawkeyeRecipes(CAMERA1)
.UseCameraIOCommander(CAMERA1)
.RegisterCameraPlugins(CAMERA1);
// LOAD SETTINGS //
mainKernel.RegisterSettings();
kernelCamera1.RegisterSettings();
settings.LoadSettings();
// Create camera and bind it (must be after settings are loaded)
kernelCamera1.UseCamera();
// Initialize modules
mainKernel.InitializeModules();
kernelCamera1.InitializeModules();
mainKernel.Get<ILoadingService>().StopLoading("Camera initialization");
var mainWindowVm = mainKernel.Get<MainWindowVM>();
mainWindowVm.SingleCameraVms =
[
kernelCamera1.Get<SingleCameraVM>()
];
settings.LoadSettings();
// Bridge Ninject → ASP.NET Core DI
builder.Services.AddSingleton(mainKernel);
builder.Services.AddSingleton(mainWindowVm);
builder.Services.AddSingleton(kernelCamera1.Get<SingleCameraVM>());
builder.Services.AddSingleton(mainKernel.Get<VisionBuilder.UI.Common.Services.IPasswordInputService>());
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<VisionBuilder.UI.Blazor.Uno.Components.App>()
.AddInteractiveServerRenderMode()
.AddAdditionalAssemblies(typeof(VisionBuilder.UI.Blazor.Components.SingleCameraControl).Assembly);
mainWindowVm.OnProgramStarted();
app.Lifetime.ApplicationStopping.Register(() =>
{
mainWindowVm.OnProgramClosed();
});
app.Run();

View File

@@ -0,0 +1,12 @@
{
"profiles": {
"VisionBuilder.UI.Blazor.Uno": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Data\**" />
<Content Remove="Data\**" />
<EmbeddedResource Remove="Data\**" />
<None Remove="Data\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MudBlazor" Version="8.0.0" />
<PackageReference Include="Ninject.Extensions.ChildKernel" Version="3.3.0" />
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\framework\Inspectron.Settings\Inspectron.Settings.csproj" />
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Emulation\Hawkeye.VisionBuilder.UI.Sources.Emulation.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Camera\VisionBuilder.UI.Camera.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Console\VisionBuilder.UI.Console.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.IOCommander\VisionBuilder.UI.IOCommander.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Recipes.HawkeyeRecipe\VisionBuilder.UI.Recipes.HawkeyeRecipe.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Ringbuffer\VisionBuilder.UI.Ringbuffer.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Statistics\VisionBuilder.UI.Statistics.csproj" />
<ProjectReference Include="..\VisionBuilder.UI.Blazor\VisionBuilder.UI.Blazor.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MudBlazor
@using VisionBuilder.UI.Blazor.Uno
@using VisionBuilder.UI.Blazor.Uno.Components

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,65 @@
html, body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
height: 100%;
overflow: hidden;
}
/* Main app layout */
.app-root {
display: flex;
flex-direction: column;
height: 100vh;
background: #fff;
}
.app-content {
flex: 1;
min-height: 0;
overflow: hidden;
}
.app-divider {
height: 1px;
background-color: #37474f;
flex-shrink: 0;
}
.app-toolbar {
display: flex;
align-items: center;
justify-content: center;
padding: 8px 16px;
flex-shrink: 0;
position: relative;
min-height: 56px;
}
.toolbar-buttons {
display: flex;
gap: 8px;
}
.toolbar-btn {
min-width: 140px !important;
height: 48px !important;
font-weight: 600 !important;
font-size: 0.85rem !important;
}
.toolbar-version {
position: absolute;
right: 16px;
font-size: 0.75rem;
color: #666;
}
/* Test mode: green toolbar tint */
.app-root.test-mode .app-toolbar {
background-color: #e8f5e9;
}
.app-root.test-mode .app-divider {
background-color: #2e7d32;
}