change - 多实例启动修改

master
wenjy 2 months ago
parent 60a43a6828
commit feb3052bd3

@ -95,7 +95,7 @@ namespace ConsoleApp
}
//启用Serilog中间件
app.UseSerilogExtensions();
//app.UseSerilogExtensions();
//app.UseTouchSocketExtensions();

@ -36,7 +36,11 @@ namespace SlnMesnac.Config
/// 日志文件路径
/// </summary>
public string logPath { get; set; }
public string InstanceId { get; set; }
public string InstanceDataPath { get; set; }
/// <summary>
/// Sql连接配置
/// </summary>

@ -35,27 +35,27 @@ namespace SlnMesnac.Serilog
/// </summary>
public static class SerilogExtensions
{
public static IApplicationBuilder UseSerilogExtensions(this IApplicationBuilder app)
public static void UseSerilogExtensions(this IServiceProvider service)
{
//启用Serilog中间件
app.UseSerilogRequestLogging();
#region 通过配置文件读取日志存放位置
var appConfig = app.ApplicationServices.GetService<AppConfig>();
var appConfig = service.GetService<AppConfig>();
var logPath = $"{appConfig.logPath}/Logs/";
#endregion
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console()
.WriteTo.File(Path.Combine(logPath, "Info.log"), LogEventLevel.Information,rollingInterval:RollingInterval.Day)
.WriteTo.File(Path.Combine(logPath, "Error.log"), LogEventLevel.Error, rollingInterval: RollingInterval.Day)
.WriteTo.File(Path.Combine(logPath, "data.log"), LogEventLevel.Warning, rollingInterval: RollingInterval.Day)
//.WriteTo.File(Path.Combine(logPath, "data.log"), LogEventLevel.Debug)
//.WriteTo.File(Path.Combine(logPath, "Debug.log"), LogEventLevel.Debug, fileSizeLimitBytes: 5 * 1024)
.CreateLogger();
app.UseSerilogRequestLogging();
return app;
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Info"))
.WriteTo.File(Path.Combine($"{logPath}/Info/", "Info.log"), rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("data"))
.WriteTo.File(Path.Combine($"{logPath}/data/", "data.log"), rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Error"))
.WriteTo.File(Path.Combine($"{logPath}/Error/", "Error.log"), rollingInterval: RollingInterval.Day))
.CreateLogger();
}
}
}

@ -1,8 +1,7 @@
<Application x:Class="SlnMesnac.WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SlnMesnac.WPF"
StartupUri="MainWindow.xaml">
xmlns:local="clr-namespace:SlnMesnac.WPF">
<Application.Resources>
<!--Button样式-->
<ResourceDictionary>

@ -1,16 +1,20 @@
using Autofac.Extensions.DependencyInjection;
using Lierda.WPFHelper;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Lierda.WPFHelper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using SlnMesnac.Config;
using SlnMesnac.Plc;
using SlnMesnac.Rfid;
using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Extensions.Configuration;
using SlnMesnac.Extensions;
using SlnMesnac.Serilog;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Linq;
using System.Reflection;
using TouchSocket.Sockets;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.Rfid;
namespace SlnMesnac.WPF
{
@ -19,65 +23,97 @@ namespace SlnMesnac.WPF
/// </summary>
public partial class App : Application
{
private System.Threading.Mutex? mutex = null;
private Mutex _instanceMutex = null;
private LierdaCracker cracker = new LierdaCracker();
public static IServiceProvider? ServiceProvider = null;
private static IHost? host;
private AppConfig appConfig;
public static IServiceProvider ServiceProvider { get; private set; }
// 实例唯一标识符
private static string _instanceId;
// 实例数据目录
private static string _instanceDataPath;
public new static App Current => (App)Application.Current;
// 获取当前实例ID静态属性
public static string InstanceId => _instanceId;
// 获取当前实例的数据路径
public static string InstanceDataPath => _instanceDataPath;
// Startup事件
protected override async void OnStartup(StartupEventArgs e)
{
try
{
bool ret;
//mutex = new System.Threading.Mutex(true, System.Diagnostics.Process.GetCurrentProcess().ProcessName, out ret);
//if (!ret)
//{
// MessageBox.Show("应用程序已开启,禁止重复运行");
// Environment.Exit(0);
//}
this.DispatcherUnhandledException += App_DispatcherUnhandledException; //全局异常处理
#region 多实例初始化
// 从命令行参数获取实例ID如果指定了
string customInstanceId = GetInstanceIdFromArgs(e.Args);
// 生成实例ID
_instanceId = !string.IsNullOrEmpty(customInstanceId)
? customInstanceId
: GenerateInstanceId();
// 初始化实例数据目录
InitializeInstanceDataPath();
// 创建基于实例ID的互斥体名称确保每个实例独立
string mutexName = $"Global\\{Process.GetCurrentProcess().ProcessName}_{_instanceId}";
bool createdNew;
_instanceMutex = new Mutex(true, mutexName, out createdNew);
if (!createdNew)
{
// 如果mutex已存在说明相同实例ID的程序已经在运行
MessageBox.Show($"实例 {_instanceId} 已在运行中,无法重复启动!",
"启动失败", MessageBoxButton.OK, MessageBoxImage.Warning);
Environment.Exit(0);
return;
}
#endregion
cracker.Cracker(100); //设置GC回收间隔
base.OnStartup(e);
//var host = CreateHostBuilder(e.Args).Build();//生成宿主。
//ServiceProvider = host.Services;
// 设置ServiceCollection
var services = new ServiceCollection();
ConfigureServices(services); // 配置服务
//await host.StartAsync();
var instanceId = GetInstanceId(e.Args);
// 创建完全独立的Host构建器
host = CreateHostBuilder(e.Args, instanceId).Build();
// 创建ServiceProvider
ServiceProvider = services.BuildServiceProvider();
// 预先验证服务
using (var scope = host.Services.CreateScope())
// 配置Serilog和其他扩展
ServiceProvider.UseSerilogExtensions();
// 获取AppConfig并更新实例特定的配置
var appConfig = ServiceProvider.GetService<AppConfig>();
if (appConfig != null)
{
ServiceProvider = scope.ServiceProvider;
// 更新日志路径为实例专用路径
appConfig.logPath = Path.Combine(_instanceDataPath, "Logs");
Directory.CreateDirectory(appConfig.logPath);
try
{
// 更新其他可能需要实例化的路径
appConfig.InstanceId = _instanceId;
appConfig.InstanceDataPath = _instanceDataPath;
//serilogHelper = services.GetRequiredService<SerilogHelper>();
//serilogHelper.Info($"启动服务");
//var appConfig = services.GetRequiredService<AppConfig>();
appConfig = host.Services.GetService<AppConfig>();
// 强制设置实例ID到配置对象
SetInstanceSpecificConfiguration(appConfig, instanceId);
//serilogHelper.Info($"实例 {instanceId} 服务初始化完成");
Console.WriteLine($"实例 {instanceId} 服务初始化完成");
}
catch (Exception ex)
{
Console.WriteLine($"实例 {instanceId} 服务初始化失败: {ex.Message}");
//serilogHelper.Info($"详细错误: {ex}");
}
// 根据实例ID调整数据库连接如果需要
// AdjustDatabaseConnectionForInstance(appConfig);
}
//await host.StartAsync();
var hostTask = host.RunAsync();
var logPath = $"{appConfig.logPath}/Logs/{DateTime.UtcNow:yyyy-MM-dd}/";
Log.Information($"系统初始化完成,日志存放路径:{appConfig.logPath}");
Log.Information($"应用程序启动 - 实例ID: {_instanceId}, 进程ID: {Process.GetCurrentProcess().Id}");
Log.Information($"日志目录: {appConfig?.logPath}");
Log.Information($"数据目录: {_instanceDataPath}");
// 显示主窗口
var loginWindow = ServiceProvider.GetRequiredService<MainWindow>();
loginWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
// 在窗口标题显示实例信息
loginWindow.Title = $"{loginWindow.Title} [{_instanceId}]";
loginWindow.Show();
}
catch (Exception exception)
{
@ -87,170 +123,399 @@ namespace SlnMesnac.WPF
}
private static void SetInstanceSpecificConfiguration(AppConfig appConfig, string instanceId)
/// <summary>
/// 从命令行参数获取实例ID
/// </summary>
private string GetInstanceIdFromArgs(string[] args)
{
if (args == null || args.Length == 0)
return null;
// 支持两种参数格式:/instance:ID 或 -instance=ID
foreach (var arg in args)
{
if (arg.StartsWith("/instance:") || arg.StartsWith("-instance="))
{
return arg.Contains(':')
? arg.Substring(arg.IndexOf(':') + 1)
: arg.Substring(arg.IndexOf('=') + 1);
}
}
return null;
}
/// <summary>
/// 生成实例ID
/// </summary>
private string GenerateInstanceId()
{
string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
string random = new Random().Next(1000, 9999).ToString();
return $"{timestamp}_{random}";
}
/// <summary>
/// 初始化实例数据路径
/// </summary>
private void InitializeInstanceDataPath()
{
string appName = Assembly.GetExecutingAssembly().GetName().Name;
string baseDataRoot = GetDataRootPath();
_instanceDataPath = Path.Combine(baseDataRoot, appName, "Instances", _instanceId);
Directory.CreateDirectory(_instanceDataPath);
Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Data"));
Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Logs"));
Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Config"));
Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Cache"));
Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Temp"));
SaveInstanceInfo();
CleanupOldInstances();
}
/// <summary>
/// 获取数据存储根路径
/// </summary>
private string GetDataRootPath()
{
string envPath = Environment.GetEnvironmentVariable("SLNMESNAC_DATA_ROOT");
if (!string.IsNullOrEmpty(envPath) && Directory.Exists(envPath))
return envPath;
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
}
/// <summary>
/// 保存实例信息文件
/// </summary>
private void SaveInstanceInfo()
{
try
{
Console.WriteLine($"为实例 {instanceId} 设置特定配置...");
// 使用反射设置所有可能的配置属性
var configType = appConfig.GetType();
var properties = configType.GetProperties();
foreach (var property in properties)
var process = Process.GetCurrentProcess();
var info = new
{
if (property.CanWrite)
InstanceId = _instanceId,
StartTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
ProcessId = process.Id,
ProcessName = process.ProcessName,
MachineName = Environment.MachineName,
UserName = Environment.UserName,
WorkingDirectory = Environment.CurrentDirectory
};
string infoFile = Path.Combine(_instanceDataPath, "instance.info");
File.WriteAllText(infoFile,
$"Instance ID: {info.InstanceId}\r\n" +
$"Start Time: {info.StartTime}\r\n" +
$"Process ID: {info.ProcessId}\r\n" +
$"Process Name: {info.ProcessName}\r\n" +
$"Machine: {info.MachineName}\r\n" +
$"User: {info.UserName}\r\n" +
$"Working Dir: {info.WorkingDirectory}");
}
catch (Exception ex)
{
Debug.WriteLine($"保存实例信息失败: {ex.Message}");
}
}
/// <summary>
/// 清理过期的实例数据
/// </summary>
private void CleanupOldInstances(int keepDays = 3)
{
try
{
string appName = Assembly.GetExecutingAssembly().GetName().Name;
string instancesRoot = Path.Combine(
GetDataRootPath(),
appName,
"Instances"
);
if (!Directory.Exists(instancesRoot))
return;
DateTime cutoffTime = DateTime.Now.AddDays(-keepDays);
var instanceDirs = Directory.GetDirectories(instancesRoot);
foreach (var dir in instanceDirs)
{
try
{
var value = property.GetValue(appConfig);
if (value is string stringValue)
var dirInfo = new DirectoryInfo(dir);
var infoFile = Path.Combine(dir, "instance.info");
if (File.Exists(infoFile))
{
// 替换路径中的占位符
if (stringValue.Contains("{Instance}") ||
stringValue.Contains("{instance}"))
// 读取启动时间
var content = File.ReadAllText(infoFile);
var startTimeLine = content.Split('\n')
.FirstOrDefault(line => line.StartsWith("Start Time:"));
if (!string.IsNullOrEmpty(startTimeLine))
{
var newValue = stringValue
.Replace("{Instance}", instanceId)
.Replace("{instance}", instanceId);
property.SetValue(appConfig, newValue);
Console.WriteLine($" 更新 {property.Name}: {newValue}");
}
else if (IsPathProperty(property.Name) &&
!stringValue.Contains(instanceId))
{
// 为路径属性添加实例ID
var newValue = $"{stringValue}_Instance{instanceId}";
property.SetValue(appConfig, newValue);
Console.WriteLine($" 更新 {property.Name}: {newValue}");
string timeStr = startTimeLine.Substring(11).Trim();
if (DateTime.TryParse(timeStr, out DateTime startTime))
{
if (startTime < cutoffTime)
{
Directory.Delete(dir, true);
Debug.WriteLine($"已清理过期实例: {dirInfo.Name}");
}
}
}
}
else if (property.PropertyType == typeof(int) &&
IsPortProperty(property.Name))
else if (dirInfo.CreationTime < cutoffTime)
{
// 调整端口号
var basePort = (int)value;
var newPort = basePort + (int.Parse(instanceId) - 1) * 10;
property.SetValue(appConfig, newPort);
Console.WriteLine($" 更新 {property.Name}: {newPort}");
// 如果没有信息文件,使用目录创建时间
Directory.Delete(dir, true);
Debug.WriteLine($"已清理过期实例: {dirInfo.Name}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"清理实例 {dir} 失败: {ex.Message}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"设置实例配置时出错: {ex.Message}");
//serilogHelper?.Info($"设置实例配置时出错: {ex.Message}");
Debug.WriteLine($"清理实例数据失败: {ex.Message}");
}
}
private static bool IsPortProperty(string propertyName)
{
return propertyName.ToLower().Contains("port");
}
private static bool IsPathProperty(string propertyName)
{
return propertyName.ToLower().Contains("path") ||
propertyName.ToLower().Contains("dir") ||
propertyName.ToLower().Contains("file");
}
public static IHostBuilder CreateHostBuilder(string[] args, string instanceId) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, config) =>
{
// 为每个实例配置独立的Serilog
config.WriteTo.Console(
outputTemplate: $"[{{Timestamp:HH:mm:ss}} {{Level:u3}}] Instance{instanceId}: {{Message:lj}}{{NewLine}}{{Exception}}")
.WriteTo.File(
path: $"./Logs/{DateTime.UtcNow:yyyy-MM-dd}/log-.txt",
rollingInterval: RollingInterval.Day,
outputTemplate: $"[{{Timestamp:yyyy-MM-dd HH:mm:ss}} {{Level:u3}}] Instance{instanceId}: {{Message:lj}}{{NewLine}}{{Exception}}");
})
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureAppConfiguration((context, config) =>
{
// 清除默认配置,从头开始构建
config.Sources.Clear();
// 按优先级添加配置源
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.instance{instanceId}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables("INSTANCE_")
.AddCommandLine(args);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(serverOptions =>
{
Console.WriteLine(int.Parse(instanceId));
serverOptions.ListenAnyIP(1 + (int.Parse(instanceId) - 1));
});
})
.ConfigureServices((context, services) =>
{
services.AddSingleton(new InstanceInfo { Id = instanceId });
});
public class InstanceInfo
{
public string Id { get; set; } = "1";
}
private static string GetInstanceId(string[] args)
{
// 支持多种参数格式
foreach (var arg in args)
{
if (arg.StartsWith("--instance"))
{
if (arg == "--instance")
{
// 查找下一个参数作为实例ID
var index = Array.IndexOf(args, arg);
if (index + 1 < args.Length)
{
return args[index + 1];
}
}
else if (arg.StartsWith("--instance="))
{
return arg.Substring("--instance=".Length);
}
else
{
// 格式: --instance1, --instance2
return arg.Substring("--instance".Length);
}
}
}
// 如果没有指定实例ID使用随机ID避免冲突
Random random = new Random();
return random.Next(0000, 9999).ToString();
}
/// <summary>
/// CreateHostBuilder
/// ConfigureServices
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
private void ConfigureServices(IServiceCollection services)
{
// 注册AppConfig
services.AddSingleton(provider =>
{
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
.UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
// 添加实例特定的配置文件(如果存在)
string instanceConfig = Path.Combine(_instanceDataPath, "Config", "appsettings.instance.json");
if (File.Exists(instanceConfig))
{
webBuilder.UseStartup<Startup>();
});
configurationBuilder.AddJsonFile(instanceConfig, optional: true, reloadOnChange: true);
}
IConfiguration configuration = configurationBuilder.Build();
var appConfig = configuration.GetSection("AppConfig").Get<AppConfig>();
// 设置实例特定的属性
if (appConfig != null)
{
appConfig.InstanceId = _instanceId;
appConfig.InstanceDataPath = _instanceDataPath;
}
return appConfig;
});
//services.AddSingleton<SerilogHelper>();
// 加载程序集
Assembly[] assemblies = {
Assembly.LoadFrom("SlnMesnac.Repository.dll"),
Assembly.LoadFrom("SlnMesnac.Plc.dll"),
Assembly.LoadFrom("SlnMesnac.Rfid.dll"),
Assembly.LoadFrom("SlnMesnac.Common.dll"),
Assembly.LoadFrom("SlnMesnac.TouchSocket.dll"),
Assembly.LoadFrom("SlnMesnac.Business.dll"),
Assembly.LoadFrom("SlnMesnac.Generate.dll")
};
// 扫描并注册程序集中的服务
services.Scan(scan => scan.FromAssemblies(assemblies)
.AddClasses()
.AsImplementedInterfaces()
.AsSelf()
.WithTransientLifetime());
// 注册TcpService - 需要确保每个实例使用不同的端口
services.AddSingleton(provider =>
{
var tcpService = new TcpService();
// 根据实例ID调整端口配置
// 例如tcpService.Port = GetInstanceSpecificPort(_instanceId);
return tcpService;
});
services.AddLogging(x => x.AddSerilog());
// 扫描并注册带有特性的窗口和服务
services.Scan(scan => scan
.FromAssemblyOf<MainWindow>()
.AddClasses(classes => classes.WithAttribute<RegisterAsSingletonAttribute>())
.AsSelf()
.WithSingletonLifetime());
services.Scan(scan => scan
.FromAssemblyOf<MainWindow>()
.AddClasses(classes => classes.WithAttribute<RegisterAsTransientAttribute>())
.AsSelf()
.WithTransientLifetime());
// 注册ORM
services.AddSqlSugarSetup();
services.AddRfidFactorySetup();
}
// Exit事件
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
Log.Information($"系统退出,当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
// 释放资源
// ...
Log.Information($"实例 {_instanceId} 退出,退出时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}");
// 释放互斥体
try
{
_instanceMutex?.ReleaseMutex();
_instanceMutex?.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine($"释放互斥体失败: {ex.Message}");
}
}
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
string errorMsg = $"[实例 {_instanceId}] 发生未处理异常: {e.Exception.Message}";
MessageBox.Show(errorMsg, "应用程序错误", MessageBoxButton.OK, MessageBoxImage.Error);
Log.Error(e.Exception, $"实例 {_instanceId} 全局异常: {e.Exception.Message}");
// 保存异常信息到实例日志目录
try
{
string errorLogFile = Path.Combine(_instanceDataPath, "Logs", $"crash_{DateTime.Now:yyyyMMdd_HHmmss}.log");
File.WriteAllText(errorLogFile,
$"实例ID: {_instanceId}\r\n" +
$"异常时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\r\n" +
$"异常信息: {e.Exception.Message}\r\n" +
$"堆栈跟踪:\r\n{e.Exception.StackTrace}");
}
catch
{
// 忽略文件保存失败
}
e.Handled = true;
}
// 静态辅助方法
/// <summary>
/// 启动一个新的应用程序实例
/// </summary>
public static void StartNewInstance(string instanceId = null)
{
try
{
string exePath = Process.GetCurrentProcess().MainModule?.FileName;
if (!string.IsNullOrEmpty(exePath))
{
string arguments = string.Empty;
if (!string.IsNullOrEmpty(instanceId))
{
arguments = $"/instance:{instanceId}";
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = exePath,
Arguments = arguments,
UseShellExecute = true
};
Process.Start(startInfo);
}
}
catch (Exception ex)
{
MessageBox.Show($"启动新实例失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 获取所有实例的目录
/// </summary>
public static string[] GetAllInstanceDirectories()
{
string appName = Assembly.GetExecutingAssembly().GetName().Name;
string instancesRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
appName,
"Instances"
);
return Directory.Exists(instancesRoot)
? Directory.GetDirectories(instancesRoot)
: Array.Empty<string>();
}
/// <summary>
/// 获取所有运行中的实例信息
/// </summary>
public static InstanceInfo[] GetAllRunningInstances()
{
var instanceDirs = GetAllInstanceDirectories();
var instances = new System.Collections.Generic.List<InstanceInfo>();
foreach (var dir in instanceDirs)
{
try
{
string infoFile = Path.Combine(dir, "instance.info");
if (File.Exists(infoFile))
{
var content = File.ReadAllLines(infoFile);
var instanceId = Path.GetFileName(dir);
var info = new InstanceInfo
{
Id = instanceId,
DataPath = dir,
CreationTime = Directory.GetCreationTime(dir)
};
instances.Add(info);
}
}
catch
{
// 忽略无法读取的实例
}
}
return instances.ToArray();
}
/// <summary>
/// 实例信息类
/// </summary>
public class InstanceInfo
{
public string Id { get; set; }
public string DataPath { get; set; }
public DateTime CreationTime { get; set; }
}

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SlnMesnac.WPF.Attribute
{
public class RegisterAsSingletonAttribute:System.Attribute
{
}
}

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SlnMesnac.WPF.Attribute
{
public class RegisterAsTransientAttribute:System.Attribute
{
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Rougamo />
</Weavers>

@ -18,12 +18,15 @@ using System.Windows.Shapes;
using System.Windows;
using System.Windows.Forms;
using SlnMesnac.WPF.Attribute;
namespace SlnMesnac.WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
[RegisterAsSingletonAttribute]
public partial class MainWindow : Window
{
//托盘

@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using SlnMesnac.Repository;
using SlnMesnac.Repository.service;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.ViewModel.IndexPage;
using System;
using System.Collections.Generic;
@ -24,6 +25,7 @@ namespace SlnMesnac.WPF.Page.IndexPage
/// <summary>
/// AddTaskContent.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class AddTaskContent : Window
{
public Action<AirportTask> _Taskaction;

@ -1,5 +1,6 @@
using ATC_MaterialBind.Entity;
using SlnMesnac.Repository;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.ViewModel.IndexPage;
using System;
using System.Collections.Generic;
@ -21,6 +22,7 @@ namespace SlnMesnac.WPF.Page.IndexPage
/// <summary>
/// ChangeType.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class ChangeType : UserControl
{
ChangeTypeViewModel ChangeTypeView;

@ -17,12 +17,14 @@ using System.Windows.Shapes;
using static Microsoft.WindowsAPICodePack.Shell.PropertySystem.SystemProperties.System;
using Microsoft.Extensions.DependencyInjection;
using Task = System.Threading.Tasks.Task;
using SlnMesnac.WPF.Attribute;
namespace SlnMesnac.WPF.Page.IndexPage
{
/// <summary>
/// DetailTaskContent.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class DetailTaskContent : Window
{
public Action<AirportTask> _Taskaction;

@ -1,4 +1,5 @@
using SlnMesnac.WPF.ViewModel.IndexPage;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.ViewModel.IndexPage;
using System;
using System.Collections.Generic;
using System.Linq;
@ -19,6 +20,7 @@ namespace SlnMesnac.WPF.Page.IndexPage
/// <summary>
/// IndexContent.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class IndexContent : UserControl
{
public IndexContent()

@ -1,5 +1,6 @@
using ATC_MaterialBind.Entity;
using SlnMesnac.Plc;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.ViewModel.IndexPage;
using System;
using System.Collections.Generic;
@ -22,6 +23,7 @@ namespace SlnMesnac.WPF.Page.IndexPage
/// <summary>
/// MaterialBind.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class MaterialBind : UserControl
{
IndexContentViewModel indexContentViewModel;

@ -4,6 +4,7 @@ using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto;
using SlnMesnac.Plc;
using SlnMesnac.Rfid;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.Model;
using SlnMesnac.WPF.ViewModel.IndexPage;
using System;
@ -27,6 +28,7 @@ namespace SlnMesnac.WPF.Page.IndexPage
/// <summary>
/// MaterialBind.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class MiddleWare : UserControl
{
public List<RfidAbsractFactory> rfidList;

@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using SlnMesnac.Rfid;
using SlnMesnac.WPF.Attribute;
using System;
using System.Collections.Generic;
using System.Linq;
@ -19,6 +20,7 @@ namespace SlnMesnac.WPF.Page.IndexPage
/// <summary>
/// SetPower.xaml 的交互逻辑
/// </summary>
[RegisterAsSingletonAttribute]
public partial class SetPower : Window
{
public List<RfidAbsractFactory> rfidList;

@ -43,11 +43,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Lierda.WPFHelper" Version="1.0.3" />
<PackageReference Include="MvvmLightLibs" Version="5.4.1.1" />
<PackageReference Include="NVelocity" Version="1.2.0" />
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
<PackageReference Include="Scrutor" Version="7.0.0" />
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
</ItemGroup>

@ -81,7 +81,7 @@ namespace SlnMesnac.WPF
}
//启用Serilog中间件
app.UseSerilogExtensions();
//app.UseSerilogExtensions();
app.UseTouchSocketExtensions();
//app.UseTouchSocketExtensions();

@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.WindowsAPICodePack.Dialogs;
using SlnMesnac.Config;
using SlnMesnac.Generate;
using SlnMesnac.WPF.Attribute;
using SqlSugar;
using System;
using System.Collections.Generic;
@ -38,6 +39,7 @@ using System.Windows;
#endregion << 版 本 注 释 >>
namespace SlnMesnac.WPF.ViewModel.Generate
{
[RegisterAsSingletonAttribute]
internal class GenerateControlViewModel : ViewModelBase
{
private readonly AppConfig _appConfig;

@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using SlnMesnac.Config;
using SlnMesnac.Repository;
using SlnMesnac.Repository.service;
using SlnMesnac.WPF.Attribute;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -15,6 +16,7 @@ using System.Threading.Tasks;
namespace SlnMesnac.WPF.ViewModel.IndexPage
{
[RegisterAsSingletonAttribute]
public partial class ChangeTypeViewModel : ObservableObject
{
private ILogger<ChangeTypeViewModel> _logger;

@ -34,6 +34,7 @@ using System.Data;
using System.Dynamic;
using System.Drawing;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
using SlnMesnac.WPF.Attribute;
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
@ -59,6 +60,7 @@ using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
#endregion << 版 本 注 释 >>
namespace SlnMesnac.WPF.ViewModel.IndexPage
{
[RegisterAsSingletonAttribute]
public partial class IndexContentViewModel : ObservableObject
{
private ILogger<IndexContentViewModel> _logger;

@ -9,6 +9,7 @@ using SlnMesnac.Config;
using SlnMesnac.Repository;
using SlnMesnac.Rfid;
using SlnMesnac.TouchSocket;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.Model;
using SlnMesnac.WPF.Page.IndexPage;
using SqlSugar;
@ -27,6 +28,7 @@ using Task = System.Threading.Tasks.Task;
namespace SlnMesnac.WPF.ViewModel.IndexPage
{
[RegisterAsSingletonAttribute]
public partial class MiddleWareViewModel : ViewModelBase
{
private ILogger<MiddleWareViewModel> _logger;

@ -9,6 +9,7 @@ using SlnMesnac.Extensions;
using SlnMesnac.Plc;
using SlnMesnac.Repository.service;
using SlnMesnac.TouchSocket;
using SlnMesnac.WPF.Attribute;
using SlnMesnac.WPF.Page.Generate;
using SlnMesnac.WPF.Page.IndexPage;
using System;
@ -16,6 +17,7 @@ using System.Windows;
namespace SlnMesnac.WPF.ViewModel
{
[RegisterAsSingletonAttribute]
public class MainWindowViewModel: ViewModelBase
{
private readonly ILogger<MainWindowViewModel> _logger;

@ -106,7 +106,7 @@ namespace SlnMesnac
});
//启用Serilog中间件
app.UseSerilogExtensions();
//app.UseSerilogExtensions();
//app.UseHttpsRedirection();

Loading…
Cancel
Save