From 7dc5e73af7916fe1cd2de5737337e2b55f9f9f78 Mon Sep 17 00:00:00 2001 From: Tyrone Date: Sun, 29 Mar 2026 23:17:20 +0800 Subject: [PATCH] v1 --- .gitattributes | 63 +++ .gitignore | 363 ++++++++++++++++++ .../Annotation/Options/CvatOptions.cs | 22 ++ .../Services/CvatAnnotationSyncAppService.cs | 82 ++++ OrpaonVision.ConfigApp/App.xaml | 8 + OrpaonVision.ConfigApp/App.xaml.cs | 37 ++ OrpaonVision.ConfigApp/AssemblyInfo.cs | 10 + .../ServiceCollectionExtensions.cs | 24 ++ OrpaonVision.ConfigApp/MainWindow.xaml | 12 + OrpaonVision.ConfigApp/MainWindow.xaml.cs | 25 ++ .../OrpaonVision.ConfigApp.csproj | 20 + OrpaonVision.Core/Abstractions/IClock.cs | 21 + .../Abstractions/IUserContext.cs | 26 ++ .../Contracts/AnnotationSyncStatusDto.cs | 44 +++ .../Contracts/SyncAnnotationProjectCommand.cs | 46 +++ .../Annotation/IAnnotationSyncAppService.cs | 24 ++ OrpaonVision.Core/Class1.cs | 12 + .../Enums/AnnotationPlatformEnum.cs | 16 + .../Enums/AnnotationSyncStatusEnum.cs | 27 ++ .../Enums/AnnotationTaskStatusEnum.cs | 32 ++ OrpaonVision.Core/OrpaonVision.Core.csproj | 9 + OrpaonVision.Core/Results/Result.cs | 102 +++++ .../Annotation/AnnotationTaskItemModel.cs | 57 +++ .../Annotation/AnnotationTaskModel.cs | 89 +++++ .../Annotation/DatasetItemModel.cs | 77 ++++ .../Annotation/DatasetVersionModel.cs | 82 ++++ OrpaonVision.Model/Class1.cs | 8 + OrpaonVision.Model/OrpaonVision.Model.csproj | 9 + .../OrpaonVision.Shared.csproj | 11 + OrpaonVision.SiteApp/App.xaml | 9 + OrpaonVision.SiteApp/App.xaml.cs | 14 + OrpaonVision.SiteApp/AssemblyInfo.cs | 10 + OrpaonVision.SiteApp/MainWindow.xaml | 12 + OrpaonVision.SiteApp/MainWindow.xaml.cs | 15 + .../OrpaonVision.SiteApp.csproj | 16 + OrpaonVision.sln | 46 +++ 36 files changed, 1480 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 OrpaonVision.ConfigApp/Annotation/Options/CvatOptions.cs create mode 100644 OrpaonVision.ConfigApp/Annotation/Services/CvatAnnotationSyncAppService.cs create mode 100644 OrpaonVision.ConfigApp/App.xaml create mode 100644 OrpaonVision.ConfigApp/App.xaml.cs create mode 100644 OrpaonVision.ConfigApp/AssemblyInfo.cs create mode 100644 OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs create mode 100644 OrpaonVision.ConfigApp/MainWindow.xaml create mode 100644 OrpaonVision.ConfigApp/MainWindow.xaml.cs create mode 100644 OrpaonVision.ConfigApp/OrpaonVision.ConfigApp.csproj create mode 100644 OrpaonVision.Core/Abstractions/IClock.cs create mode 100644 OrpaonVision.Core/Abstractions/IUserContext.cs create mode 100644 OrpaonVision.Core/Annotation/Contracts/AnnotationSyncStatusDto.cs create mode 100644 OrpaonVision.Core/Annotation/Contracts/SyncAnnotationProjectCommand.cs create mode 100644 OrpaonVision.Core/Annotation/IAnnotationSyncAppService.cs create mode 100644 OrpaonVision.Core/Class1.cs create mode 100644 OrpaonVision.Core/Enums/AnnotationPlatformEnum.cs create mode 100644 OrpaonVision.Core/Enums/AnnotationSyncStatusEnum.cs create mode 100644 OrpaonVision.Core/Enums/AnnotationTaskStatusEnum.cs create mode 100644 OrpaonVision.Core/OrpaonVision.Core.csproj create mode 100644 OrpaonVision.Core/Results/Result.cs create mode 100644 OrpaonVision.Model/Annotation/AnnotationTaskItemModel.cs create mode 100644 OrpaonVision.Model/Annotation/AnnotationTaskModel.cs create mode 100644 OrpaonVision.Model/Annotation/DatasetItemModel.cs create mode 100644 OrpaonVision.Model/Annotation/DatasetVersionModel.cs create mode 100644 OrpaonVision.Model/Class1.cs create mode 100644 OrpaonVision.Model/OrpaonVision.Model.csproj create mode 100644 OrpaonVision.Shared/OrpaonVision.Shared.csproj create mode 100644 OrpaonVision.SiteApp/App.xaml create mode 100644 OrpaonVision.SiteApp/App.xaml.cs create mode 100644 OrpaonVision.SiteApp/AssemblyInfo.cs create mode 100644 OrpaonVision.SiteApp/MainWindow.xaml create mode 100644 OrpaonVision.SiteApp/MainWindow.xaml.cs create mode 100644 OrpaonVision.SiteApp/OrpaonVision.SiteApp.csproj create mode 100644 OrpaonVision.sln diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/OrpaonVision.ConfigApp/Annotation/Options/CvatOptions.cs b/OrpaonVision.ConfigApp/Annotation/Options/CvatOptions.cs new file mode 100644 index 0000000..3f965ae --- /dev/null +++ b/OrpaonVision.ConfigApp/Annotation/Options/CvatOptions.cs @@ -0,0 +1,22 @@ +namespace OrpaonVision.ConfigApp.Annotation.Options; + +/// +/// CVAT 连接配置。 +/// +public sealed class CvatOptions +{ + /// + /// CVAT 服务地址。 + /// + public string ServerEndpoint { get; set; } = "http://127.0.0.1:8080"; + + /// + /// API Token(预留)。 + /// + public string? ApiToken { get; set; } + + /// + /// 同步超时时间(秒)。 + /// + public int SyncTimeoutSeconds { get; set; } = 60; +} diff --git a/OrpaonVision.ConfigApp/Annotation/Services/CvatAnnotationSyncAppService.cs b/OrpaonVision.ConfigApp/Annotation/Services/CvatAnnotationSyncAppService.cs new file mode 100644 index 0000000..80fe099 --- /dev/null +++ b/OrpaonVision.ConfigApp/Annotation/Services/CvatAnnotationSyncAppService.cs @@ -0,0 +1,82 @@ +using OrpaonVision.ConfigApp.Annotation.Options; +using OrpaonVision.Core.Annotation; +using OrpaonVision.Core.Annotation.Contracts; +using OrpaonVision.Core.Enums; +using OrpaonVision.Core.Results; + +namespace OrpaonVision.ConfigApp.Annotation.Services; + +/// +/// CVAT 标注同步应用服务(骨架实现)。 +/// +/// 说明: +/// - 当前阶段先提供参数校验与返回契约,便于前后链路先跑通; +/// - 实际 HTTP 调用、认证、重试策略将在后续迭代补齐。 +/// +public sealed class CvatAnnotationSyncAppService : IAnnotationSyncAppService +{ + private readonly CvatOptions _options; + + /// + /// 构造函数。 + /// + /// CVAT 配置。 + public CvatAnnotationSyncAppService(CvatOptions options) + { + _options = options; + } + + /// + public Task SyncProjectAsync(SyncAnnotationProjectCommand command, CancellationToken cancellationToken = default) + { + if (command.Platform != AnnotationPlatformEnum.Cvat) + { + return Task.FromResult(Result.Fail("ANNOTATION_PLATFORM_NOT_SUPPORTED", "当前版本仅支持 CVAT 平台同步。")); + } + + if (command.ProjectId == Guid.Empty || command.AnnotationTaskId == Guid.Empty) + { + return Task.FromResult(Result.Fail("ANNOTATION_ARGUMENT_INVALID", "项目标识或标注任务标识无效。")); + } + + if (string.IsNullOrWhiteSpace(command.CvatServerEndpoint)) + { + return Task.FromResult(Result.Fail("ANNOTATION_ENDPOINT_REQUIRED", "CVAT 服务地址不能为空。")); + } + + if (!Uri.TryCreate(command.CvatServerEndpoint, UriKind.Absolute, out _)) + { + return Task.FromResult(Result.Fail("ANNOTATION_ENDPOINT_INVALID", "CVAT 服务地址格式不合法。")); + } + + if (command.CvatTaskId <= 0 || command.CvatProjectId <= 0) + { + return Task.FromResult(Result.Fail("ANNOTATION_CVAT_ID_INVALID", "CVAT 项目 ID 或任务 ID 必须大于 0。")); + } + + // 当前为骨架实现:预留后续与 CVAT API 的双向同步逻辑。 + return Task.FromResult(Result.Success(message: "CVAT 同步请求已受理。")); + } + + /// + public Task> GetSyncStatusAsync(Guid projectId, CancellationToken cancellationToken = default) + { + if (projectId == Guid.Empty) + { + return Task.FromResult(Result.Fail("ANNOTATION_PROJECT_ID_INVALID", "项目标识不能为空。")); + } + + var status = new AnnotationSyncStatusDto + { + ProjectId = projectId, + AnnotationTaskId = Guid.Empty, + Platform = AnnotationPlatformEnum.Cvat, + SyncStatus = AnnotationSyncStatusEnum.None, + ProgressPercent = 0, + LastSyncedAtUtc = null, + LastErrorMessage = null + }; + + return Task.FromResult(Result.Success(status, message: $"CVAT 状态查询完成({_options.ServerEndpoint})。")); + } +} diff --git a/OrpaonVision.ConfigApp/App.xaml b/OrpaonVision.ConfigApp/App.xaml new file mode 100644 index 0000000..1a64827 --- /dev/null +++ b/OrpaonVision.ConfigApp/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/OrpaonVision.ConfigApp/App.xaml.cs b/OrpaonVision.ConfigApp/App.xaml.cs new file mode 100644 index 0000000..3b10e78 --- /dev/null +++ b/OrpaonVision.ConfigApp/App.xaml.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using OrpaonVision.ConfigApp.DependencyInjection; +using System.Windows; + +namespace OrpaonVision.ConfigApp +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + private ServiceProvider? _serviceProvider; + + /// + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + var services = new ServiceCollection(); + services.AddConfigAppServices(); + services.AddSingleton(); + + _serviceProvider = services.BuildServiceProvider(); + + var mainWindow = _serviceProvider.GetRequiredService(); + mainWindow.Show(); + } + + /// + protected override void OnExit(ExitEventArgs e) + { + _serviceProvider?.Dispose(); + base.OnExit(e); + } + } + +} diff --git a/OrpaonVision.ConfigApp/AssemblyInfo.cs b/OrpaonVision.ConfigApp/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/OrpaonVision.ConfigApp/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs b/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..d6817e2 --- /dev/null +++ b/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using OrpaonVision.ConfigApp.Annotation.Options; +using OrpaonVision.ConfigApp.Annotation.Services; +using OrpaonVision.Core.Annotation; + +namespace OrpaonVision.ConfigApp.DependencyInjection; + +/// +/// ConfigApp 服务注册扩展。 +/// +public static class ServiceCollectionExtensions +{ + /// + /// 注册 ConfigApp 应用服务。 + /// + /// 服务集合。 + /// 服务集合。 + public static IServiceCollection AddConfigAppServices(this IServiceCollection services) + { + services.AddSingleton(new CvatOptions()); + services.AddSingleton(); + return services; + } +} diff --git a/OrpaonVision.ConfigApp/MainWindow.xaml b/OrpaonVision.ConfigApp/MainWindow.xaml new file mode 100644 index 0000000..2e3462b --- /dev/null +++ b/OrpaonVision.ConfigApp/MainWindow.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/OrpaonVision.ConfigApp/MainWindow.xaml.cs b/OrpaonVision.ConfigApp/MainWindow.xaml.cs new file mode 100644 index 0000000..5bd4eb2 --- /dev/null +++ b/OrpaonVision.ConfigApp/MainWindow.xaml.cs @@ -0,0 +1,25 @@ +using OrpaonVision.Core.Annotation; +using System.Windows; + +namespace OrpaonVision.ConfigApp +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + private readonly IAnnotationSyncAppService _annotationSyncAppService; + + /// + /// 主窗口构造函数。 + /// + /// 标注同步应用服务。 + public MainWindow(IAnnotationSyncAppService annotationSyncAppService) + { + _annotationSyncAppService = annotationSyncAppService; + InitializeComponent(); + + Title = "OrpaonVision ConfigApp (CVAT)"; + } + } +} \ No newline at end of file diff --git a/OrpaonVision.ConfigApp/OrpaonVision.ConfigApp.csproj b/OrpaonVision.ConfigApp/OrpaonVision.ConfigApp.csproj new file mode 100644 index 0000000..f3bd6a2 --- /dev/null +++ b/OrpaonVision.ConfigApp/OrpaonVision.ConfigApp.csproj @@ -0,0 +1,20 @@ + + + + WinExe + net8.0-windows + enable + enable + true + + + + + + + + + + + + diff --git a/OrpaonVision.Core/Abstractions/IClock.cs b/OrpaonVision.Core/Abstractions/IClock.cs new file mode 100644 index 0000000..1c56b63 --- /dev/null +++ b/OrpaonVision.Core/Abstractions/IClock.cs @@ -0,0 +1,21 @@ +namespace OrpaonVision.Core.Abstractions; + +/// +/// 时间服务抽象。 +/// +/// 用途: +/// - 消除业务代码对 DateTime.Now 的直接依赖; +/// - 提升可测试性,便于通过替身时间源复现时序问题。 +/// +public interface IClock +{ + /// + /// 获取当前本地时间。 + /// + DateTime Now { get; } + + /// + /// 获取当前 UTC 时间。 + /// + DateTime UtcNow { get; } +} diff --git a/OrpaonVision.Core/Abstractions/IUserContext.cs b/OrpaonVision.Core/Abstractions/IUserContext.cs new file mode 100644 index 0000000..39013ff --- /dev/null +++ b/OrpaonVision.Core/Abstractions/IUserContext.cs @@ -0,0 +1,26 @@ +namespace OrpaonVision.Core.Abstractions; + +/// +/// 当前用户上下文抽象。 +/// +/// 用途: +/// - 为审计字段(CreatedBy/UpdatedBy)提供统一来源; +/// - 在应用服务中统一获取当前登录人信息。 +/// +public interface IUserContext +{ + /// + /// 当前用户 ID。 + /// + string UserId { get; } + + /// + /// 当前用户名或工号。 + /// + string UserName { get; } + + /// + /// 当前用户角色集合。 + /// + IReadOnlyCollection Roles { get; } +} diff --git a/OrpaonVision.Core/Annotation/Contracts/AnnotationSyncStatusDto.cs b/OrpaonVision.Core/Annotation/Contracts/AnnotationSyncStatusDto.cs new file mode 100644 index 0000000..c16a5bc --- /dev/null +++ b/OrpaonVision.Core/Annotation/Contracts/AnnotationSyncStatusDto.cs @@ -0,0 +1,44 @@ +using OrpaonVision.Core.Enums; + +namespace OrpaonVision.Core.Annotation.Contracts; + +/// +/// 标注同步状态 DTO。 +/// +public sealed class AnnotationSyncStatusDto +{ + /// + /// 本地项目标识。 + /// + public Guid ProjectId { get; init; } + + /// + /// 本地标注任务标识。 + /// + public Guid AnnotationTaskId { get; init; } + + /// + /// 平台类型(当前固定为 Cvat)。 + /// + public AnnotationPlatformEnum Platform { get; init; } = AnnotationPlatformEnum.Cvat; + + /// + /// 同步状态。 + /// + public AnnotationSyncStatusEnum SyncStatus { get; init; } + + /// + /// 同步进度(0~100)。 + /// + public decimal ProgressPercent { get; init; } + + /// + /// 最近一次同步时间(UTC)。 + /// + public DateTime? LastSyncedAtUtc { get; init; } + + /// + /// 最近错误信息。 + /// + public string? LastErrorMessage { get; init; } +} diff --git a/OrpaonVision.Core/Annotation/Contracts/SyncAnnotationProjectCommand.cs b/OrpaonVision.Core/Annotation/Contracts/SyncAnnotationProjectCommand.cs new file mode 100644 index 0000000..8dea878 --- /dev/null +++ b/OrpaonVision.Core/Annotation/Contracts/SyncAnnotationProjectCommand.cs @@ -0,0 +1,46 @@ +using OrpaonVision.Core.Enums; + +namespace OrpaonVision.Core.Annotation.Contracts; + +/// +/// 同步标注项目命令。 +/// +/// 当前版本约束:仅支持 CVAT。 +/// +public sealed class SyncAnnotationProjectCommand +{ + /// + /// 平台类型(当前固定为 Cvat)。 + /// + public AnnotationPlatformEnum Platform { get; init; } = AnnotationPlatformEnum.Cvat; + + /// + /// 本地项目标识。 + /// + public Guid ProjectId { get; init; } + + /// + /// 本地标注任务标识。 + /// + public Guid AnnotationTaskId { get; init; } + + /// + /// CVAT 服务地址,例如 http://127.0.0.1:8080。 + /// + public string CvatServerEndpoint { get; init; } = string.Empty; + + /// + /// CVAT 项目 ID。 + /// + public long CvatProjectId { get; init; } + + /// + /// CVAT 任务 ID。 + /// + public long CvatTaskId { get; init; } + + /// + /// 发起同步的用户。 + /// + public string RequestedBy { get; init; } = string.Empty; +} diff --git a/OrpaonVision.Core/Annotation/IAnnotationSyncAppService.cs b/OrpaonVision.Core/Annotation/IAnnotationSyncAppService.cs new file mode 100644 index 0000000..0e73aeb --- /dev/null +++ b/OrpaonVision.Core/Annotation/IAnnotationSyncAppService.cs @@ -0,0 +1,24 @@ +using OrpaonVision.Core.Annotation.Contracts; +using OrpaonVision.Core.Results; + +namespace OrpaonVision.Core.Annotation; + +/// +/// 标注同步应用服务接口。 +/// +/// 职责: +/// - 发起本地标注任务与 CVAT 任务的同步; +/// - 查询任务同步状态,供配置端界面展示。 +/// +public interface IAnnotationSyncAppService +{ + /// + /// 发起同步。 + /// + Task SyncProjectAsync(SyncAnnotationProjectCommand command, CancellationToken cancellationToken = default); + + /// + /// 查询同步状态。 + /// + Task> GetSyncStatusAsync(Guid projectId, CancellationToken cancellationToken = default); +} diff --git a/OrpaonVision.Core/Class1.cs b/OrpaonVision.Core/Class1.cs new file mode 100644 index 0000000..a7e3610 --- /dev/null +++ b/OrpaonVision.Core/Class1.cs @@ -0,0 +1,12 @@ +namespace OrpaonVision.Core; + +/// +/// Core 模块标记类型。 +/// +/// 该类型用于: +/// 1) 提供统一的程序集锚点(反射扫描、依赖注入装配); +/// 2) 替代模板生成的占位类,避免语义不明确的 Class1 命名。 +/// +public sealed class CoreModuleMarker +{ +} diff --git a/OrpaonVision.Core/Enums/AnnotationPlatformEnum.cs b/OrpaonVision.Core/Enums/AnnotationPlatformEnum.cs new file mode 100644 index 0000000..e1cb145 --- /dev/null +++ b/OrpaonVision.Core/Enums/AnnotationPlatformEnum.cs @@ -0,0 +1,16 @@ +namespace OrpaonVision.Core.Enums; + +/// +/// 标注平台类型。 +/// +/// 说明: +/// - 根据当前需求文档,系统当前版本仅支持 CVAT; +/// - 后续若扩展其他平台,可在本枚举中追加,并由适配层实现差异化处理。 +/// +public enum AnnotationPlatformEnum +{ + /// + /// CVAT 平台。 + /// + Cvat = 1 +} diff --git a/OrpaonVision.Core/Enums/AnnotationSyncStatusEnum.cs b/OrpaonVision.Core/Enums/AnnotationSyncStatusEnum.cs new file mode 100644 index 0000000..57d245c --- /dev/null +++ b/OrpaonVision.Core/Enums/AnnotationSyncStatusEnum.cs @@ -0,0 +1,27 @@ +namespace OrpaonVision.Core.Enums; + +/// +/// 标注任务同步状态。 +/// +public enum AnnotationSyncStatusEnum +{ + /// + /// 未同步。 + /// + None = 0, + + /// + /// 同步中。 + /// + Syncing = 1, + + /// + /// 同步成功。 + /// + Succeeded = 2, + + /// + /// 同步失败。 + /// + Failed = 3 +} diff --git a/OrpaonVision.Core/Enums/AnnotationTaskStatusEnum.cs b/OrpaonVision.Core/Enums/AnnotationTaskStatusEnum.cs new file mode 100644 index 0000000..018f59d --- /dev/null +++ b/OrpaonVision.Core/Enums/AnnotationTaskStatusEnum.cs @@ -0,0 +1,32 @@ +namespace OrpaonVision.Core.Enums; + +/// +/// 标注任务状态。 +/// +public enum AnnotationTaskStatusEnum +{ + /// + /// 待开始。 + /// + Pending = 1, + + /// + /// 进行中。 + /// + InProgress = 2, + + /// + /// 已完成(外部平台已提交)。 + /// + Completed = 3, + + /// + /// 已回收(业务系统已成功回收标注结果)。 + /// + Recovered = 4, + + /// + /// 失败。 + /// + Failed = 5 +} diff --git a/OrpaonVision.Core/OrpaonVision.Core.csproj b/OrpaonVision.Core/OrpaonVision.Core.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/OrpaonVision.Core/OrpaonVision.Core.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/OrpaonVision.Core/Results/Result.cs b/OrpaonVision.Core/Results/Result.cs new file mode 100644 index 0000000..e48dfa0 --- /dev/null +++ b/OrpaonVision.Core/Results/Result.cs @@ -0,0 +1,102 @@ +namespace OrpaonVision.Core.Results; + +/// +/// 非泛型统一返回结果。 +/// +/// 约束: +/// - 普通业务失败使用 Result 返回,不建议抛异常; +/// - 异常场景请由上层捕获后转换为统一错误码与消息。 +/// +public class Result +{ + /// + /// 是否成功。 + /// + public bool Succeeded { get; init; } + + /// + /// 业务编码(成功或失败编码)。 + /// + public string Code { get; init; } = string.Empty; + + /// + /// 业务消息。 + /// + public string Message { get; init; } = string.Empty; + + /// + /// 错误明细。 + /// + public IReadOnlyCollection Errors { get; init; } = Array.Empty(); + + /// + /// 跟踪 ID,用于日志链路关联。 + /// + public string? TraceId { get; init; } + + /// + /// 创建成功结果。 + /// + public static Result Success(string code = "OK", string message = "Success") + { + return new Result + { + Succeeded = true, + Code = code, + Message = message + }; + } + + /// + /// 创建失败结果。 + /// + public static Result Fail(string code, string message, params string[] errors) + { + return new Result + { + Succeeded = false, + Code = code, + Message = message, + Errors = errors + }; + } +} + +/// +/// 泛型统一返回结果。 +/// +public sealed class Result : Result +{ + /// + /// 业务数据。 + /// + public T? Data { get; init; } + + /// + /// 创建成功结果。 + /// + public static Result Success(T data, string code = "OK", string message = "Success") + { + return new Result + { + Succeeded = true, + Code = code, + Message = message, + Data = data + }; + } + + /// + /// 创建失败结果。 + /// + public static new Result Fail(string code, string message, params string[] errors) + { + return new Result + { + Succeeded = false, + Code = code, + Message = message, + Errors = errors + }; + } +} diff --git a/OrpaonVision.Model/Annotation/AnnotationTaskItemModel.cs b/OrpaonVision.Model/Annotation/AnnotationTaskItemModel.cs new file mode 100644 index 0000000..2b4f6da --- /dev/null +++ b/OrpaonVision.Model/Annotation/AnnotationTaskItemModel.cs @@ -0,0 +1,57 @@ +namespace OrpaonVision.Model.Annotation; + +/// +/// 标注任务项模型(对应需求文档中的 mdl_annotation_task_item)。 +/// +public sealed class AnnotationTaskItemModel +{ + /// + /// 主键。 + /// + public long Id { get; set; } + + /// + /// 所属标注任务 ID。 + /// + public long AnnotationTaskId { get; set; } + + /// + /// 数据集项 ID。 + /// + public long DatasetItemId { get; set; } + + /// + /// 处理状态。 + /// + public int Status { get; set; } + + /// + /// 审核人。 + /// + public string? ReviewedBy { get; set; } + + /// + /// 审核时间。 + /// + public DateTime? ReviewedAt { get; set; } + + /// + /// 创建时间。 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人。 + /// + public string? CreatedBy { get; set; } + + /// + /// 更新时间。 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 更新人。 + /// + public string? UpdatedBy { get; set; } +} diff --git a/OrpaonVision.Model/Annotation/AnnotationTaskModel.cs b/OrpaonVision.Model/Annotation/AnnotationTaskModel.cs new file mode 100644 index 0000000..2976f8b --- /dev/null +++ b/OrpaonVision.Model/Annotation/AnnotationTaskModel.cs @@ -0,0 +1,89 @@ +namespace OrpaonVision.Model.Annotation; + +/// +/// 标注任务模型(对应需求文档中的 mdl_annotation_task)。 +/// +/// 说明: +/// - 为保持 Model 层轻量与独立,枚举字段暂以 int 存储; +/// - 业务层可在 Core 中将其映射为强类型枚举。 +/// +public sealed class AnnotationTaskModel +{ + /// + /// 主键。 + /// + public long Id { get; set; } + + /// + /// 任务编码(唯一)。 + /// + public string TaskCode { get; set; } = string.Empty; + + /// + /// 任务名称。 + /// + public string TaskName { get; set; } = string.Empty; + + /// + /// 平台类型。 + /// 当前版本仅支持 CVAT,建议固定为 1。 + /// + public int PlatformType { get; set; } = 1; + + /// + /// 外部平台任务 ID(CVAT TaskId)。 + /// + public string? ExternalTaskId { get; set; } + + /// + /// 数据集 ID。 + /// + public long DatasetId { get; set; } + + /// + /// 标签集 ID。 + /// + public long LabelSetId { get; set; } + + /// + /// 指派标注人员。 + /// + public string? AssignedTo { get; set; } + + /// + /// 标注任务状态。 + /// 参考:Pending/InProgress/Completed/Recovered/Failed。 + /// + public int Status { get; set; } + + /// + /// 进度百分比(0~100)。 + /// + public decimal? ProgressPercent { get; set; } + + /// + /// 同步状态。 + /// 参考:None/Syncing/Succeeded/Failed。 + /// + public int SyncStatus { get; set; } + + /// + /// 创建时间。 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人。 + /// + public string? CreatedBy { get; set; } + + /// + /// 更新时间。 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 更新人。 + /// + public string? UpdatedBy { get; set; } +} diff --git a/OrpaonVision.Model/Annotation/DatasetItemModel.cs b/OrpaonVision.Model/Annotation/DatasetItemModel.cs new file mode 100644 index 0000000..b876e53 --- /dev/null +++ b/OrpaonVision.Model/Annotation/DatasetItemModel.cs @@ -0,0 +1,77 @@ +namespace OrpaonVision.Model.Annotation; + +/// +/// 数据集项模型(对应需求文档中的 mdl_dataset_item 关键字段)。 +/// +public sealed class DatasetItemModel +{ + /// + /// 主键。 + /// + public long Id { get; set; } + + /// + /// 数据集 ID。 + /// + public long DatasetId { get; set; } + + /// + /// 文件路径(图片或视频帧存储路径)。 + /// + public string FilePath { get; set; } = string.Empty; + + /// + /// 文件名。 + /// + public string FileName { get; set; } = string.Empty; + + /// + /// 宽度。 + /// + public int? Width { get; set; } + + /// + /// 高度。 + /// + public int? Height { get; set; } + + /// + /// 标注状态。 + /// + public int AnnotationStatus { get; set; } + + /// + /// 质量状态。 + /// + public int QualityStatus { get; set; } + + /// + /// 文件哈希。 + /// + public string? HashValue { get; set; } + + /// + /// 扩展字段(JSON)。 + /// + public string? ExtraJson { get; set; } + + /// + /// 创建时间。 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人。 + /// + public string? CreatedBy { get; set; } + + /// + /// 更新时间。 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 更新人。 + /// + public string? UpdatedBy { get; set; } +} diff --git a/OrpaonVision.Model/Annotation/DatasetVersionModel.cs b/OrpaonVision.Model/Annotation/DatasetVersionModel.cs new file mode 100644 index 0000000..2a64265 --- /dev/null +++ b/OrpaonVision.Model/Annotation/DatasetVersionModel.cs @@ -0,0 +1,82 @@ +namespace OrpaonVision.Model.Annotation; + +/// +/// 数据集版本模型(对应需求文档中的 mdl_dataset_version 关键字段)。 +/// +public sealed class DatasetVersionModel +{ + /// + /// 主键。 + /// + public long Id { get; set; } + + /// + /// 数据集 ID。 + /// + public long DatasetId { get; set; } + + /// + /// 版本号。 + /// + public string VersionNo { get; set; } = string.Empty; + + /// + /// 标签集 ID。 + /// + public long LabelSetId { get; set; } + + /// + /// 条目数量。 + /// + public int ItemCount { get; set; } + + /// + /// 训练集数量。 + /// + public int? TrainCount { get; set; } + + /// + /// 验证集数量。 + /// + public int? ValCount { get; set; } + + /// + /// 测试集数量。 + /// + public int? TestCount { get; set; } + + /// + /// 导出路径。 + /// + public string? ExportPath { get; set; } + + /// + /// 快照 JSON。 + /// + public string SnapshotJson { get; set; } = string.Empty; + + /// + /// 状态。 + /// + public int Status { get; set; } + + /// + /// 创建时间。 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人。 + /// + public string? CreatedBy { get; set; } + + /// + /// 更新时间。 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 更新人。 + /// + public string? UpdatedBy { get; set; } +} diff --git a/OrpaonVision.Model/Class1.cs b/OrpaonVision.Model/Class1.cs new file mode 100644 index 0000000..4155245 --- /dev/null +++ b/OrpaonVision.Model/Class1.cs @@ -0,0 +1,8 @@ +namespace OrpaonVision.Model; + +/// +/// Model 模块标记类型。 +/// +public sealed class ModelModuleMarker +{ +} diff --git a/OrpaonVision.Model/OrpaonVision.Model.csproj b/OrpaonVision.Model/OrpaonVision.Model.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/OrpaonVision.Model/OrpaonVision.Model.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/OrpaonVision.Shared/OrpaonVision.Shared.csproj b/OrpaonVision.Shared/OrpaonVision.Shared.csproj new file mode 100644 index 0000000..69bea40 --- /dev/null +++ b/OrpaonVision.Shared/OrpaonVision.Shared.csproj @@ -0,0 +1,11 @@ + + + + Library + net8.0-windows + enable + enable + true + + + diff --git a/OrpaonVision.SiteApp/App.xaml b/OrpaonVision.SiteApp/App.xaml new file mode 100644 index 0000000..294bbd9 --- /dev/null +++ b/OrpaonVision.SiteApp/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/OrpaonVision.SiteApp/App.xaml.cs b/OrpaonVision.SiteApp/App.xaml.cs new file mode 100644 index 0000000..91add33 --- /dev/null +++ b/OrpaonVision.SiteApp/App.xaml.cs @@ -0,0 +1,14 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace OrpaonVision.SiteApp +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } + +} diff --git a/OrpaonVision.SiteApp/AssemblyInfo.cs b/OrpaonVision.SiteApp/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/OrpaonVision.SiteApp/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/OrpaonVision.SiteApp/MainWindow.xaml b/OrpaonVision.SiteApp/MainWindow.xaml new file mode 100644 index 0000000..09bd1dd --- /dev/null +++ b/OrpaonVision.SiteApp/MainWindow.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/OrpaonVision.SiteApp/MainWindow.xaml.cs b/OrpaonVision.SiteApp/MainWindow.xaml.cs new file mode 100644 index 0000000..4761aed --- /dev/null +++ b/OrpaonVision.SiteApp/MainWindow.xaml.cs @@ -0,0 +1,15 @@ +using System.Windows; + +namespace OrpaonVision.SiteApp +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/OrpaonVision.SiteApp/OrpaonVision.SiteApp.csproj b/OrpaonVision.SiteApp/OrpaonVision.SiteApp.csproj new file mode 100644 index 0000000..1b8af4a --- /dev/null +++ b/OrpaonVision.SiteApp/OrpaonVision.SiteApp.csproj @@ -0,0 +1,16 @@ + + + + WinExe + net8.0-windows + enable + enable + true + + + + + + + + diff --git a/OrpaonVision.sln b/OrpaonVision.sln new file mode 100644 index 0000000..56d44ea --- /dev/null +++ b/OrpaonVision.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35514.174 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrpaonVision.SiteApp", "OrpaonVision.SiteApp\OrpaonVision.SiteApp.csproj", "{B73678CE-709E-4D54-B973-088E7D8FDFA9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrpaonVision.ConfigApp", "OrpaonVision.ConfigApp\OrpaonVision.ConfigApp.csproj", "{3548E93A-5613-4F21-ACFB-F7A4C732EE75}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrpaonVision.Model", "OrpaonVision.Model\OrpaonVision.Model.csproj", "{3BB30820-DD9F-40D0-8DDA-8A8F8C7B4F07}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrpaonVision.Core", "OrpaonVision.Core\OrpaonVision.Core.csproj", "{50CC1F6C-D21E-45B2-B288-9F6FBC98AD33}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrpaonVision.Shared", "OrpaonVision.Shared\OrpaonVision.Shared.csproj", "{4B9F801C-8F51-4E6A-9B84-8BDA2BB41BBD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B73678CE-709E-4D54-B973-088E7D8FDFA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B73678CE-709E-4D54-B973-088E7D8FDFA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B73678CE-709E-4D54-B973-088E7D8FDFA9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B73678CE-709E-4D54-B973-088E7D8FDFA9}.Release|Any CPU.Build.0 = Release|Any CPU + {3548E93A-5613-4F21-ACFB-F7A4C732EE75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3548E93A-5613-4F21-ACFB-F7A4C732EE75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3548E93A-5613-4F21-ACFB-F7A4C732EE75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3548E93A-5613-4F21-ACFB-F7A4C732EE75}.Release|Any CPU.Build.0 = Release|Any CPU + {3BB30820-DD9F-40D0-8DDA-8A8F8C7B4F07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BB30820-DD9F-40D0-8DDA-8A8F8C7B4F07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BB30820-DD9F-40D0-8DDA-8A8F8C7B4F07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BB30820-DD9F-40D0-8DDA-8A8F8C7B4F07}.Release|Any CPU.Build.0 = Release|Any CPU + {50CC1F6C-D21E-45B2-B288-9F6FBC98AD33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CC1F6C-D21E-45B2-B288-9F6FBC98AD33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CC1F6C-D21E-45B2-B288-9F6FBC98AD33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CC1F6C-D21E-45B2-B288-9F6FBC98AD33}.Release|Any CPU.Build.0 = Release|Any CPU + {4B9F801C-8F51-4E6A-9B84-8BDA2BB41BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B9F801C-8F51-4E6A-9B84-8BDA2BB41BBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B9F801C-8F51-4E6A-9B84-8BDA2BB41BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B9F801C-8F51-4E6A-9B84-8BDA2BB41BBD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal