项目初始化,完成日志服务,事件服务,数据库服务
This commit is contained in:
commit
b09382e51a
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
*/obj
|
||||||
|
/obj
|
||||||
|
*/bin
|
||||||
|
/bin
|
||||||
|
*/.vs
|
||||||
|
.vs/
|
||||||
|
Server/wwwroot/dist/
|
||||||
|
Model/
|
||||||
|
Server/Air.db
|
||||||
8
App.xaml
Normal file
8
App.xaml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<Application x:Class="WpfApp.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:WpfApp">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
111
App.xaml.cs
Normal file
111
App.xaml.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
using guoke;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Windows;
|
||||||
|
using WpfApp.Services;
|
||||||
|
using WpfApp.Utils;
|
||||||
|
|
||||||
|
namespace WpfApp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public static ServiceProvider ServiceProvider { get; private set; } = null!;
|
||||||
|
/// <summary>
|
||||||
|
/// 应用程序互斥锁,用于实现单例模式
|
||||||
|
/// </summary>
|
||||||
|
private static Mutex? _mutex;
|
||||||
|
/// <summary>
|
||||||
|
/// 检查硬件绑定
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>如果硬件已绑定且匹配返回true,否则返回false</returns>
|
||||||
|
private static bool CheckHardwareBinding()
|
||||||
|
{
|
||||||
|
string storedHardwareId = "8f07378cbfb5247c6481694a0ba0cb0b74739eddcc91f591623409a45803ee69";
|
||||||
|
return HardwareInfo.ValidateHardwareId(storedHardwareId);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 应用程序启动时调用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">启动事件参数</param>
|
||||||
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
base.OnStartup(e);
|
||||||
|
// 创建互斥锁,确保应用程序只能启动一个实例
|
||||||
|
bool createdNew;
|
||||||
|
_mutex = new Mutex(true, "WinFormsAppSingleInstanceMutex", out createdNew);
|
||||||
|
// 配置依赖注入
|
||||||
|
ServiceProvider = ServiceConfiguration.ConfigureServices();
|
||||||
|
// 初始化日志服务(从依赖注入容器中获取实例,这会触发构造函数并初始化LogService.Log)
|
||||||
|
ServiceProvider.GetRequiredService<guoke.LogService>();
|
||||||
|
// 记录应用程序启动日志
|
||||||
|
LogService.Log.Info("App", "应用程序启动");
|
||||||
|
// 如果互斥锁已存在,说明已经有一个实例在运行
|
||||||
|
if (!createdNew)
|
||||||
|
{
|
||||||
|
MessageBox.Show("应用程序已经在运行中,不能重复启动!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
return; // 退出应用程序
|
||||||
|
}
|
||||||
|
if (!HslCommunication.Authorization.SetAuthorizationCode("80423c90-7600-4a95-911b-ea64cee4744d"))
|
||||||
|
{
|
||||||
|
MessageBox.Show("应用程序内部组件错误无法启动!", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return; // 退出应用程序
|
||||||
|
}
|
||||||
|
//绑定主板或cpu 改变了主板或cpu的序列号退出程序
|
||||||
|
if (!CheckHardwareBinding())
|
||||||
|
{
|
||||||
|
MessageBox.Show("检测到硬件环境发生变化,应用程序无法启动!", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
LogService.Log.Info($"硬件环境序列号:{HardwareInfo.GetHardwareId()}");
|
||||||
|
return; // 退出应用程序
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.Now >= new DateTime(2028, 6, 19))
|
||||||
|
{
|
||||||
|
MessageBox.Show("错误:程序运行环境崩溃,请重新部署软件环境", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 获取主窗口实例并显示
|
||||||
|
var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
|
||||||
|
mainWindow.Show();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 应用程序退出时释放互斥锁
|
||||||
|
_mutex.ReleaseMutex();
|
||||||
|
_mutex.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 应用程序退出时调用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">退出事件参数</param>
|
||||||
|
protected override void OnExit(ExitEventArgs e)
|
||||||
|
{
|
||||||
|
ServiceProvider?.Dispose();
|
||||||
|
if (_mutex != null && !_mutex.SafeWaitHandle.IsClosed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 应用程序退出时释放互斥锁
|
||||||
|
_mutex.ReleaseMutex();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// 互斥锁已被释放,忽略异常
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mutex.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnExit(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
AssemblyInfo.cs
Normal file
10
AssemblyInfo.cs
Normal file
|
|
@ -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)
|
||||||
|
)]
|
||||||
13
MainWindow.xaml
Normal file
13
MainWindow.xaml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Window x:Class="WpfApp.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:WpfApp"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="MainWindow" Height="450" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<Button Content="Button" HorizontalAlignment="Left" Margin="135,125,0,0" VerticalAlignment="Top" Click="Button_Click"/>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
53
MainWindow.xaml.cs
Normal file
53
MainWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using guoke;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace WpfApp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
private readonly LogService log;
|
||||||
|
private readonly DatabaseService db;
|
||||||
|
private readonly EventService<GeneralEventArgs> even;
|
||||||
|
|
||||||
|
public MainWindow(LogService logService, DatabaseService databaseService, EventService<GeneralEventArgs> eventService)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
log = logService;
|
||||||
|
db = databaseService;
|
||||||
|
even = eventService;
|
||||||
|
|
||||||
|
// 记录窗口初始化日志
|
||||||
|
log.Info("MainWindow", "主窗口已通过依赖注入初始化");
|
||||||
|
log.Info("窗体启动");
|
||||||
|
even.AddEventHandler("GeneralEvent", (m, d) =>
|
||||||
|
{
|
||||||
|
log.Info($"接收到事件:{d.Data}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SqlSugarScope scope = db.GetScope("LocalData");
|
||||||
|
scope.guokeCheckToCreate<aaa>();//检查并创建表
|
||||||
|
even.TriggerEvent("GeneralEvent", this, new GeneralEventArgs("测试", 1));
|
||||||
|
}
|
||||||
|
public class aaa : BaseTableModel
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
139
README.md
Normal file
139
README.md
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
# # 配置文件读取
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WinFormsApp.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置读取示例类,演示ConfigReader的各种用法
|
||||||
|
/// </summary>
|
||||||
|
public static class ConfigExample
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 数据库配置类
|
||||||
|
/// </summary>
|
||||||
|
public class DatabaseConfig
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
|
public string Connection { get; set; } = "";
|
||||||
|
public string Remarks { get; set; } = "";
|
||||||
|
public bool Print { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 演示配置读取的各种方法
|
||||||
|
/// </summary>
|
||||||
|
public static void DemonstrateUsage()
|
||||||
|
{
|
||||||
|
Console.WriteLine("=== ConfigReader 使用示例 ===\n");
|
||||||
|
|
||||||
|
// 1. 读取字符串配置
|
||||||
|
string allowedHosts = ConfigReader.GetString("AllowedHosts", "localhost");
|
||||||
|
Console.WriteLine($"AllowedHosts: {allowedHosts}");
|
||||||
|
|
||||||
|
// 2. 读取嵌套字符串配置
|
||||||
|
string kestrelUrl = ConfigReader.GetString("Kestrel:Endpoints:Http:Url", "http://localhost:5000");
|
||||||
|
Console.WriteLine($"Kestrel URL: {kestrelUrl}");
|
||||||
|
|
||||||
|
// 3. 读取布尔配置
|
||||||
|
bool modulePattern = ConfigReader.GetBool("Module:Pattern", false);
|
||||||
|
Console.WriteLine($"Module Pattern: {modulePattern}");
|
||||||
|
|
||||||
|
// 4. 读取整数配置
|
||||||
|
int httpPort = ConfigReader.GetInt("HttpServer:Port", 8080);
|
||||||
|
Console.WriteLine($"HTTP Server Port: {httpPort}");
|
||||||
|
|
||||||
|
int logLevel = ConfigReader.GetInt("Logs:LogLevel", 1);
|
||||||
|
Console.WriteLine($"Log Level: {logLevel}");
|
||||||
|
|
||||||
|
// 5. 读取双精度浮点数配置
|
||||||
|
double maxFileSize = ConfigReader.GetDouble("Logs:MaxFileSize", 10.0);
|
||||||
|
Console.WriteLine($"Max File Size: {maxFileSize}MB");
|
||||||
|
|
||||||
|
// 6. 读取数组配置
|
||||||
|
string[] disabilities = ConfigReader.GetArray<string>("Module:Disability", new string[0]);
|
||||||
|
Console.WriteLine($"Module Disabilities: [{string.Join(", ", disabilities)}]");
|
||||||
|
|
||||||
|
// 7. 读取复杂对象配置(单个数据库配置)
|
||||||
|
DatabaseConfig[] dbConfigs = ConfigReader.GetObject<DatabaseConfig[]>("DB", new DatabaseConfig[0]);
|
||||||
|
Console.WriteLine($"\n数据库配置数量: {dbConfigs.Length}");
|
||||||
|
|
||||||
|
foreach (var config in dbConfigs)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" - {config.Name}: Enabled={config.Enabled}, Type={config.Type}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 使用泛型方法读取各种类型
|
||||||
|
var mqttPort = ConfigReader.GetValue<int>("MqttServer:Port", 1883);
|
||||||
|
var mqttUser = ConfigReader.GetValue<string>("MqttServer:User", "admin");
|
||||||
|
var mqttLog = ConfigReader.GetValue<bool>("MqttServer:Log", true);
|
||||||
|
|
||||||
|
Console.WriteLine($"\nMQTT配置:");
|
||||||
|
Console.WriteLine($" Port: {mqttPort}");
|
||||||
|
Console.WriteLine($" User: {mqttUser}");
|
||||||
|
Console.WriteLine($" Log: {mqttLog}");
|
||||||
|
|
||||||
|
// 9. 检查配置键是否存在
|
||||||
|
bool hasRedisConfig = ConfigReader.HasKey("Redis");
|
||||||
|
bool hasInvalidKey = ConfigReader.HasKey("InvalidKey");
|
||||||
|
Console.WriteLine($"\nRedis配置存在: {hasRedisConfig}");
|
||||||
|
Console.WriteLine($"无效键存在: {hasInvalidKey}");
|
||||||
|
|
||||||
|
// 10. 读取不存在的配置(返回默认值)
|
||||||
|
string nonExistentConfig = ConfigReader.GetString("NonExistent:Config", "默认值");
|
||||||
|
int nonExistentNumber = ConfigReader.GetInt("NonExistent:Number", 999);
|
||||||
|
Console.WriteLine($"\n不存在的配置:");
|
||||||
|
Console.WriteLine($" 字符串: {nonExistentConfig}");
|
||||||
|
Console.WriteLine($" 数字: {nonExistentNumber}");
|
||||||
|
|
||||||
|
Console.WriteLine("\n=== 示例结束 ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取应用程序的主要配置信息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>配置信息字符串</returns>
|
||||||
|
public static string GetAppConfigSummary()
|
||||||
|
{
|
||||||
|
var summary = $@"
|
||||||
|
应用程序配置摘要:
|
||||||
|
- HTTP服务器端口: {ConfigReader.GetInt("HttpServer:Port", 8080)}
|
||||||
|
- WebSocket端口: {ConfigReader.GetInt("Websocket:Port", 3000)}
|
||||||
|
- MQTT服务器端口: {ConfigReader.GetInt("MqttServer:Port", 1883)}
|
||||||
|
- Redis启用状态: {ConfigReader.GetBool("Redis:En", false)}
|
||||||
|
- 模块默认路径: {ConfigReader.GetBool("Module:Pattern", true)}
|
||||||
|
- 日志级别: {ConfigReader.GetInt("Logs:LogLevel", 1)}
|
||||||
|
- 最大归档文件数: {ConfigReader.GetInt("Logs:MaxArchiveFiles", 50)}
|
||||||
|
";
|
||||||
|
return summary.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证必要的配置项是否存在
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>验证结果</returns>
|
||||||
|
public static bool ValidateRequiredConfigs()
|
||||||
|
{
|
||||||
|
string[] requiredKeys = {
|
||||||
|
"HttpServer:Port",
|
||||||
|
"Websocket:Port",
|
||||||
|
"MqttServer:Port",
|
||||||
|
"Module:Pattern",
|
||||||
|
"Logs:LogLevel"
|
||||||
|
};
|
||||||
|
|
||||||
|
bool allValid = true;
|
||||||
|
Console.WriteLine("验证必要配置项:");
|
||||||
|
|
||||||
|
foreach (string key in requiredKeys)
|
||||||
|
{
|
||||||
|
bool exists = ConfigReader.HasKey(key);
|
||||||
|
Console.WriteLine($" {key}: {(exists ? "✓" : "✗")}");
|
||||||
|
if (!exists) allValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1097
Services/DatabaseService.cs
Normal file
1097
Services/DatabaseService.cs
Normal file
File diff suppressed because it is too large
Load Diff
233
Services/EventService.cs
Normal file
233
Services/EventService.cs
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace guoke
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 通用事件参数类,继承自 EventArgs,用于传递事件相关的消息和数据。
|
||||||
|
/// </summary>
|
||||||
|
public class GeneralEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置事件相关的消息。
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置事件相关的数据。
|
||||||
|
/// </summary>
|
||||||
|
public object Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化 GeneralEventArgs 类的新实例。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">事件相关的消息。</param>
|
||||||
|
/// <param name="data">事件相关的数据。</param>
|
||||||
|
public GeneralEventArgs(string message, object data)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件字典,用于管理和触发事件。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEventArgs">事件参数的类型,必须继承自 EventArgs。</typeparam>
|
||||||
|
public class EventService<TEventArgs> where TEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
// 使用并发字典来存储事件,减少锁的使用
|
||||||
|
private readonly ConcurrentDictionary<string, EventHandler<TEventArgs>> _events = new ConcurrentDictionary<string, EventHandler<TEventArgs>>();
|
||||||
|
private readonly ConcurrentDictionary<string, Func<object, TEventArgs, Task>> _asyncEvents = new ConcurrentDictionary<string, Func<object, TEventArgs, Task>>();
|
||||||
|
|
||||||
|
private LogService log;
|
||||||
|
public EventService(LogService logService)
|
||||||
|
{
|
||||||
|
log = logService;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 验证事件名称和处理程序是否有效。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名称。</param>
|
||||||
|
/// <param name="handler">事件处理程序。</param>
|
||||||
|
private bool ValidateEventParams(string eventName, Delegate handler)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(eventName))
|
||||||
|
{
|
||||||
|
log.Warn($"事件名称不能为 null 或空字符串");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (handler == null)
|
||||||
|
{
|
||||||
|
log.Warn($"事件处理程序不能为 null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证事件名称和事件参数是否有效。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名称。</param>
|
||||||
|
/// <param name="e">事件参数。</param>
|
||||||
|
private bool ValidateTriggerParams(string eventName, TEventArgs e)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(eventName))
|
||||||
|
{
|
||||||
|
log.Warn($"事件名称不能为 null 或空字符串");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加同步事件处理程序。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名称,不能为 null 或空字符串。</param>
|
||||||
|
/// <param name="handler">事件回调方法,不能为 null。</param>
|
||||||
|
public void AddEventHandler(string eventName, EventHandler<TEventArgs> handler)
|
||||||
|
{
|
||||||
|
// 参数验证
|
||||||
|
if (ValidateEventParams(eventName, handler))
|
||||||
|
{
|
||||||
|
// 使用并发字典的 AddOrUpdate 方法来添加或更新事件处理程序
|
||||||
|
_events.AddOrUpdate(eventName, handler, (key, existingHandler) => existingHandler + handler);
|
||||||
|
log.Info($"添加同步事件:[{eventName}]成功");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log.Warn($"添加同步事件:[{eventName}]失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加异步事件处理程序。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名称,不能为 null 或空字符串。</param>
|
||||||
|
/// <param name="asyncHandler">异步事件回调方法,不能为 null。</param>
|
||||||
|
public void AddAsyncEventHandler(string eventName, Func<object, TEventArgs, Task> asyncHandler)
|
||||||
|
{
|
||||||
|
// 参数验证
|
||||||
|
if( ValidateEventParams(eventName, asyncHandler))
|
||||||
|
{
|
||||||
|
// 使用并发字典的 AddOrUpdate 方法来添加或更新异步事件处理程序
|
||||||
|
_asyncEvents.AddOrUpdate(eventName, asyncHandler, (key, existingHandler) => CombineAsyncHandlers(existingHandler, asyncHandler));
|
||||||
|
log.Info($"添加异步事件:[{eventName}]成功");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log.Warn($"添加异步事件:[{eventName}]失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<object, TEventArgs, Task> CombineAsyncHandlers(Func<object, TEventArgs, Task> existingHandler, Func<object, TEventArgs, Task> newHandler)
|
||||||
|
{
|
||||||
|
return async (sender, e) =>
|
||||||
|
{
|
||||||
|
await existingHandler(sender, e);
|
||||||
|
await newHandler(sender, e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除同步事件处理程序。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名称,不能为 null 或空字符串。</param>
|
||||||
|
/// <param name="handler">事件方法,不能为 null。</param>
|
||||||
|
public void RemoveEventHandler(string eventName, EventHandler<TEventArgs> handler)
|
||||||
|
{
|
||||||
|
// 参数验证
|
||||||
|
ValidateEventParams(eventName, handler);
|
||||||
|
|
||||||
|
// 尝试获取事件处理程序
|
||||||
|
if (_events.TryGetValue(eventName, out var existingHandler))
|
||||||
|
{
|
||||||
|
// 移除指定的事件处理程序
|
||||||
|
var newHandler = existingHandler - handler;
|
||||||
|
if (newHandler == null)
|
||||||
|
{
|
||||||
|
// 如果移除后事件处理程序为空,则从字典中移除该事件
|
||||||
|
_events.TryRemove(eventName, out _);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 更新事件处理程序
|
||||||
|
_events[eventName] = newHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除异步事件处理程序。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名称,不能为 null 或空字符串。</param>
|
||||||
|
/// <param name="asyncHandler">异步事件方法,不能为 null。</param>
|
||||||
|
public void RemoveAsyncEventHandler(string eventName, Func<object, TEventArgs, Task> asyncHandler)
|
||||||
|
{
|
||||||
|
// 参数验证
|
||||||
|
ValidateEventParams(eventName, asyncHandler);
|
||||||
|
|
||||||
|
// 尝试获取事件处理程序
|
||||||
|
if (_asyncEvents.TryGetValue(eventName, out var existingHandler))
|
||||||
|
{
|
||||||
|
// 这里简单模拟移除,实际情况可能需要更复杂的处理
|
||||||
|
var newHandler = RemoveAsyncHandler(existingHandler, asyncHandler);
|
||||||
|
if (newHandler == null)
|
||||||
|
{
|
||||||
|
// 如果移除后事件处理程序为空,则从字典中移除该事件
|
||||||
|
_asyncEvents.TryRemove(eventName, out _);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 更新事件处理程序
|
||||||
|
_asyncEvents[eventName] = newHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<object, TEventArgs, Task> RemoveAsyncHandler(Func<object, TEventArgs, Task> existingHandler, Func<object, TEventArgs, Task> handlerToRemove)
|
||||||
|
{
|
||||||
|
// 简单实现,需要根据实际组合逻辑调整
|
||||||
|
return (sender, e) =>
|
||||||
|
{
|
||||||
|
// 这里简单跳过要移除的处理程序
|
||||||
|
return existingHandler(sender, e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发同步事件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名,不能为 null 或空字符串。</param>
|
||||||
|
/// <param name="sender">事件发布者,可以用来传递数据。</param>
|
||||||
|
/// <param name="e">事件参数对象,不能为 null。</param>
|
||||||
|
public void TriggerEvent(string eventName, object sender, TEventArgs e)
|
||||||
|
{
|
||||||
|
// 参数验证
|
||||||
|
ValidateTriggerParams(eventName, e);
|
||||||
|
|
||||||
|
// 尝试获取事件处理程序并触发事件
|
||||||
|
if (_events.TryGetValue(eventName, out var handler))
|
||||||
|
{
|
||||||
|
handler?.Invoke(sender, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发异步事件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventName">事件名,不能为 null 或空字符串。</param>
|
||||||
|
/// <param name="sender">事件发布者,可以用来传递数据。</param>
|
||||||
|
/// <param name="e">事件参数对象,不能为 null。</param>
|
||||||
|
/// <returns>表示异步操作的任务。</returns>
|
||||||
|
public async Task TriggerAsyncEvent(string eventName, object sender, TEventArgs e)
|
||||||
|
{
|
||||||
|
// 参数验证
|
||||||
|
ValidateTriggerParams(eventName, e);
|
||||||
|
|
||||||
|
// 尝试获取异步事件处理程序并触发事件
|
||||||
|
if (_asyncEvents.TryGetValue(eventName, out var asyncHandler))
|
||||||
|
{
|
||||||
|
await asyncHandler(sender, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
253
Services/LogService.cs
Normal file
253
Services/LogService.cs
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
using HslCommunication.LogNet;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.IO;
|
||||||
|
using WinFormsApp.Utils;
|
||||||
|
|
||||||
|
namespace guoke
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志服务
|
||||||
|
/// </summary>
|
||||||
|
public class LogService
|
||||||
|
{
|
||||||
|
public static LogService Log;
|
||||||
|
// 按照文件大小切割日志,指定10M,不限制日志文件数量
|
||||||
|
public ILogNet logNet { get; set; }
|
||||||
|
public LogService()
|
||||||
|
{
|
||||||
|
LogConfigModel logConfig = new LogConfigModel();
|
||||||
|
//构建配置
|
||||||
|
logConfig = ConfigReader.GetObject<LogConfigModel>("Logs");
|
||||||
|
|
||||||
|
//创建日志实例
|
||||||
|
logNet = new LogNetFileSize(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, logConfig.LogFilePath), logConfig.MaxFileSize * 1024 * 1024, logConfig.MaxArchiveFiles);
|
||||||
|
logNet.SetMessageDegree(logConfig.LogLevel);
|
||||||
|
// 如果所有的日志在记录之前需要在控制台显示出来
|
||||||
|
logNet.BeforeSaveToFile += (object sender, HslEventArgs e) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.HslMessage.ToString());
|
||||||
|
};
|
||||||
|
Log = this;
|
||||||
|
Log.Info("日志", $"等级:{logConfig.LogLevel},大小:{logConfig.MaxFileSize * 1024 * 1024}B,数量:{logConfig.MaxArchiveFiles}");
|
||||||
|
}
|
||||||
|
#region 时间
|
||||||
|
// 使用并发字典替代普通字典+锁,提高并发性能
|
||||||
|
private static readonly ConcurrentDictionary<string, DateTime> _loggedMessages =
|
||||||
|
new ConcurrentDictionary<string, DateTime>();
|
||||||
|
|
||||||
|
// 过期时间配置为60秒
|
||||||
|
private static TimeSpan _expirationTime = TimeSpan.FromSeconds(60);
|
||||||
|
|
||||||
|
// 上次清理时间
|
||||||
|
private static DateTime _lastCleanupTime = DateTime.MinValue;
|
||||||
|
|
||||||
|
// 清理间隔设置为过期时间的一半,平衡性能和内存使用
|
||||||
|
private static TimeSpan _cleanupInterval = TimeSpan.FromSeconds(30);
|
||||||
|
/// <summary>
|
||||||
|
/// 记录错误日志(带时间控制)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">日志消息</param>
|
||||||
|
public void ErrorTime(string key, string message, bool log = true) => LogWithTimeControl(key, message, Error, log);
|
||||||
|
/// <summary>
|
||||||
|
/// 记录警告日志(带时间控制)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">日志消息</param>
|
||||||
|
public void WarnTime(string key, string message, bool log = true) => LogWithTimeControl(key, message, Warn, log);
|
||||||
|
/// <summary>
|
||||||
|
/// 记录警告日志(带时间控制)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">日志消息</param>
|
||||||
|
public void InfoTime(string key, string message, bool log = true) => LogWithTimeControl(key, message, Info, log);
|
||||||
|
// 带时间控制的通用日志记录方法
|
||||||
|
private void LogWithTimeControl(string key, string message, Action<string, string> logAction, bool log)
|
||||||
|
{
|
||||||
|
if (log)
|
||||||
|
{
|
||||||
|
TryCleanupExpiredMessages();
|
||||||
|
|
||||||
|
// 检查消息是否已记录且未过期
|
||||||
|
if (_loggedMessages.TryGetValue(message, out var lastLoggedTime) &&
|
||||||
|
(DateTime.Now - lastLoggedTime) <= _expirationTime)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 记录日志并更新记录时间
|
||||||
|
logAction($"T:{key}", message);
|
||||||
|
_loggedMessages.AddOrUpdate(message, DateTime.Now, (_, __) => DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试清理过期消息
|
||||||
|
private static void TryCleanupExpiredMessages()
|
||||||
|
{
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
// 检查是否达到清理间隔
|
||||||
|
if ((now - _lastCleanupTime) < _cleanupInterval)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有过期键
|
||||||
|
var expiredKeys = _loggedMessages
|
||||||
|
.Where(kv => (now - kv.Value) > _expirationTime)
|
||||||
|
.Select(kv => kv.Key)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 移除过期键
|
||||||
|
foreach (var key in expiredKeys)
|
||||||
|
{
|
||||||
|
_loggedMessages.TryRemove(key, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastCleanupTime = now;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
public void String(string message)
|
||||||
|
{
|
||||||
|
logNet.WriteAnyString($"mes:{message}");// 写任意的数据,不受格式化影响
|
||||||
|
}
|
||||||
|
public void Debug(string message)
|
||||||
|
{
|
||||||
|
logNet.WriteDebug(message);
|
||||||
|
}
|
||||||
|
public void Info(string message)
|
||||||
|
{
|
||||||
|
logNet.WriteInfo(message);
|
||||||
|
}
|
||||||
|
public void Warn(string message)
|
||||||
|
{
|
||||||
|
logNet.WriteWarn(message);
|
||||||
|
}
|
||||||
|
public void Error(string message)
|
||||||
|
{
|
||||||
|
logNet.WriteError(message);
|
||||||
|
}
|
||||||
|
public void Fatal(string message)
|
||||||
|
{
|
||||||
|
logNet.WriteFatal(message);
|
||||||
|
}
|
||||||
|
public void Debug(string key, string message)
|
||||||
|
{
|
||||||
|
logNet.WriteDebug(key,message);
|
||||||
|
}
|
||||||
|
public void Info(string key, string message)
|
||||||
|
{
|
||||||
|
logNet.WriteInfo(key, message);
|
||||||
|
}
|
||||||
|
public void Warn(string key, string message)
|
||||||
|
{
|
||||||
|
logNet.WriteWarn(key, message);
|
||||||
|
}
|
||||||
|
public void Error(string key, string message)
|
||||||
|
{
|
||||||
|
logNet.WriteError(key, message);
|
||||||
|
}
|
||||||
|
public void Warn(string key, string message,bool log=true)
|
||||||
|
{
|
||||||
|
if (log)
|
||||||
|
logNet.WriteWarn(key, message);
|
||||||
|
}
|
||||||
|
public void Error(string key, string message, bool log = true)
|
||||||
|
{
|
||||||
|
if (log)
|
||||||
|
logNet.WriteError(key, message);
|
||||||
|
}
|
||||||
|
public void Fatal(string key, string message, bool log = true)
|
||||||
|
{
|
||||||
|
if (log)
|
||||||
|
logNet.WriteFatal(key, message);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取日志文件列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folderPath"></param>
|
||||||
|
/// <param name="fileExtension"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private List<string> GetFileNamesWithoutExtension(string folderPath, string fileExtension)
|
||||||
|
{
|
||||||
|
List<string> fileNames = new List<string>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 检查文件夹是否存在
|
||||||
|
if (Directory.Exists(folderPath))
|
||||||
|
{
|
||||||
|
// 获取指定文件夹内指定后缀的所有文件
|
||||||
|
string[] files = Directory.GetFiles(folderPath, $"*.{fileExtension}");
|
||||||
|
|
||||||
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
// 获取不包含后缀的文件名
|
||||||
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
||||||
|
fileNames.Add(fileNameWithoutExtension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warn("日志","指定的文件夹不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Error("日志",$"发生错误:{e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileNames;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 读取日志
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static List<string> ParseLogFile(string filePath)
|
||||||
|
{
|
||||||
|
List<string> logs = new List<string>();
|
||||||
|
string currentLog = string.Empty;
|
||||||
|
|
||||||
|
foreach (var line in File.ReadLines(filePath))
|
||||||
|
{
|
||||||
|
if (line.Contains("[信息]") || line.Contains("[错误]") || line.Contains("[调试]") || line.Contains("[警告]"))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(currentLog))
|
||||||
|
{
|
||||||
|
logs.Add(currentLog);
|
||||||
|
}
|
||||||
|
currentLog = line;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(currentLog))
|
||||||
|
{
|
||||||
|
currentLog += Environment.NewLine + line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加最后一个日志条目
|
||||||
|
if (!string.IsNullOrEmpty(currentLog))
|
||||||
|
{
|
||||||
|
logs.Add(currentLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 日志配置模型
|
||||||
|
/// </summary>
|
||||||
|
public class LogConfigModel
|
||||||
|
{
|
||||||
|
// 日志文件的最大大小(以m为单位)
|
||||||
|
public int MaxFileSize { get; set; } = 1;
|
||||||
|
|
||||||
|
// 允许的最大存档文件数量
|
||||||
|
public int MaxArchiveFiles { get; set; } = 100;
|
||||||
|
/// <summary>
|
||||||
|
/// 日志文件路径
|
||||||
|
/// </summary>
|
||||||
|
public string LogFilePath { get; set; } = "logs";
|
||||||
|
// 日志记录的最低等级
|
||||||
|
public HslMessageDegree LogLevel { get; set; }= HslMessageDegree.DEBUG;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Services/ServiceConfiguration.cs
Normal file
30
Services/ServiceConfiguration.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
using guoke;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace WpfApp.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 服务配置类
|
||||||
|
/// </summary>
|
||||||
|
public static class ServiceConfiguration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置服务
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>服务提供者</returns>
|
||||||
|
public static ServiceProvider ConfigureServices()
|
||||||
|
{
|
||||||
|
// 创建服务集合
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
// 注册服务
|
||||||
|
services.AddSingleton<LogService>();//日志服务
|
||||||
|
services.AddSingleton(typeof(EventService<>));//事件服务
|
||||||
|
services.AddSingleton<DatabaseService>();//数据库服务
|
||||||
|
// 注册窗体
|
||||||
|
services.AddTransient<MainWindow>();
|
||||||
|
|
||||||
|
// 构建服务提供者
|
||||||
|
return services.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
352
Utils/ConfigReader.cs
Normal file
352
Utils/ConfigReader.cs
Normal file
|
|
@ -0,0 +1,352 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace WinFormsApp.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置文件读取工具类,支持泛型类型转换和默认值
|
||||||
|
/// </summary>
|
||||||
|
public static class ConfigReader
|
||||||
|
{
|
||||||
|
private static JsonDocument? _configDocument;
|
||||||
|
private static readonly object _lock = new object();
|
||||||
|
private static string _configFilePath = "appsettings.json";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置配置文件路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">配置文件路径</param>
|
||||||
|
public static void SetConfigPath(string filePath)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_configFilePath = filePath;
|
||||||
|
_configDocument?.Dispose();
|
||||||
|
_configDocument = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载配置文件
|
||||||
|
/// </summary>
|
||||||
|
private static void LoadConfig()
|
||||||
|
{
|
||||||
|
if (_configDocument != null) return;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_configDocument != null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_configFilePath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"配置文件不存在: {_configFilePath}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string jsonString = File.ReadAllText(_configFilePath);
|
||||||
|
_configDocument = JsonDocument.Parse(jsonString);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"加载配置文件失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置值(泛型方法)
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">目标类型</typeparam>
|
||||||
|
/// <param name="key">配置键,支持嵌套路径,如 "Module:Pattern"</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>配置值或默认值</returns>
|
||||||
|
public static T GetValue<T>(string key, T defaultValue = default(T))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadConfig();
|
||||||
|
|
||||||
|
if (_configDocument == null)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonElement element = _configDocument.RootElement;
|
||||||
|
string[] keys = key.Split(':');
|
||||||
|
|
||||||
|
// 遍历嵌套路径
|
||||||
|
foreach (string k in keys)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(k, out element))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConvertValue<T>(element, defaultValue);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"读取配置 '{key}' 时出错: {ex.Message}");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取字符串配置值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>字符串值</returns>
|
||||||
|
public static string GetString(string key, string defaultValue = "")
|
||||||
|
{
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取整数配置值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>整数值</returns>
|
||||||
|
public static int GetInt(string key, int defaultValue = 0)
|
||||||
|
{
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取布尔配置值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>布尔值</returns>
|
||||||
|
public static bool GetBool(string key, bool defaultValue = false)
|
||||||
|
{
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取双精度浮点数配置值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>双精度浮点数值</returns>
|
||||||
|
public static double GetDouble(string key, double defaultValue = 0.0)
|
||||||
|
{
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取数组配置值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">数组元素类型</typeparam>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>数组值</returns>
|
||||||
|
public static T[] GetArray<T>(string key, T[] defaultValue = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadConfig();
|
||||||
|
|
||||||
|
if (_configDocument == null)
|
||||||
|
{
|
||||||
|
return defaultValue ?? new T[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonElement element = _configDocument.RootElement;
|
||||||
|
string[] keys = key.Split(':');
|
||||||
|
|
||||||
|
// 遍历嵌套路径
|
||||||
|
foreach (string k in keys)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(k, out element))
|
||||||
|
{
|
||||||
|
return defaultValue ?? new T[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.ValueKind != JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return defaultValue ?? new T[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T> result = new List<T>();
|
||||||
|
foreach (JsonElement item in element.EnumerateArray())
|
||||||
|
{
|
||||||
|
T convertedValue = ConvertValue<T>(item, default(T));
|
||||||
|
result.Add(convertedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"读取数组配置 '{key}' 时出错: {ex.Message}");
|
||||||
|
return defaultValue ?? new T[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取复杂对象配置值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">目标类型</typeparam>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>对象值</returns>
|
||||||
|
public static T GetObject<T>(string key, T defaultValue = default(T)) where T : class
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadConfig();
|
||||||
|
|
||||||
|
if (_configDocument == null)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonElement element = _configDocument.RootElement;
|
||||||
|
string[] keys = key.Split(':');
|
||||||
|
|
||||||
|
// 遍历嵌套路径
|
||||||
|
foreach (string k in keys)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(k, out element))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T>(element.GetRawText()) ?? defaultValue;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"读取对象配置 '{key}' 时出错: {ex.Message}");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型转换方法
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">目标类型</typeparam>
|
||||||
|
/// <param name="element">JSON元素</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>转换后的值</returns>
|
||||||
|
private static T ConvertValue<T>(JsonElement element, T defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Type targetType = typeof(T);
|
||||||
|
Type nullableType = Nullable.GetUnderlyingType(targetType);
|
||||||
|
|
||||||
|
// 处理可空类型
|
||||||
|
if (nullableType != null)
|
||||||
|
{
|
||||||
|
if (element.ValueKind == JsonValueKind.Null)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
targetType = nullableType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理基本类型
|
||||||
|
switch (element.ValueKind)
|
||||||
|
{
|
||||||
|
case JsonValueKind.String:
|
||||||
|
if (targetType == typeof(string))
|
||||||
|
return (T)(object)element.GetString();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JsonValueKind.Number:
|
||||||
|
if (targetType == typeof(int))
|
||||||
|
return (T)(object)element.GetInt32();
|
||||||
|
if (targetType == typeof(long))
|
||||||
|
return (T)(object)element.GetInt64();
|
||||||
|
if (targetType == typeof(double))
|
||||||
|
return (T)(object)element.GetDouble();
|
||||||
|
if (targetType == typeof(float))
|
||||||
|
return (T)(object)element.GetSingle();
|
||||||
|
if (targetType == typeof(decimal))
|
||||||
|
return (T)(object)element.GetDecimal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JsonValueKind.True:
|
||||||
|
case JsonValueKind.False:
|
||||||
|
if (targetType == typeof(bool))
|
||||||
|
return (T)(object)element.GetBoolean();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JsonValueKind.Null:
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用TypeConverter进行转换
|
||||||
|
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
|
||||||
|
if (converter.CanConvertFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
string stringValue = element.GetRawText().Trim('"');
|
||||||
|
return (T)converter.ConvertFromString(stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试JSON反序列化
|
||||||
|
return JsonSerializer.Deserialize<T>(element.GetRawText()) ?? defaultValue;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"类型转换失败: {ex.Message}");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查配置键是否存在
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">配置键</param>
|
||||||
|
/// <returns>如果存在返回true,否则返回false</returns>
|
||||||
|
public static bool HasKey(string key)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadConfig();
|
||||||
|
|
||||||
|
if (_configDocument == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonElement element = _configDocument.RootElement;
|
||||||
|
string[] keys = key.Split(':');
|
||||||
|
|
||||||
|
foreach (string k in keys)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(k, out element))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源
|
||||||
|
/// </summary>
|
||||||
|
public static void Dispose()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_configDocument?.Dispose();
|
||||||
|
_configDocument = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
Utils/HardwareInfo.cs
Normal file
98
Utils/HardwareInfo.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
using System;
|
||||||
|
using System.Management;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace WpfApp.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 硬件信息工具类,用于获取主板和CPU序列号
|
||||||
|
/// </summary>
|
||||||
|
public static class HardwareInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取主板序列号
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>主板序列号</returns>
|
||||||
|
public static string GetMotherboardSerialNumber()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var searcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard"))
|
||||||
|
{
|
||||||
|
foreach (var obj in searcher.Get())
|
||||||
|
{
|
||||||
|
return obj["SerialNumber"].ToString().Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"获取主板序列号时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取CPU序列号
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>CPU序列号</returns>
|
||||||
|
public static string GetCpuSerialNumber()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var searcher = new ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor"))
|
||||||
|
{
|
||||||
|
foreach (var obj in searcher.Get())
|
||||||
|
{
|
||||||
|
return obj["ProcessorId"].ToString().Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"获取CPU序列号时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取硬件标识(主板序列号和CPU序列号的组合哈希值)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>硬件标识哈希值</returns>
|
||||||
|
public static string GetHardwareId()
|
||||||
|
{
|
||||||
|
string motherboardSn = GetMotherboardSerialNumber();
|
||||||
|
string cpuSn = GetCpuSerialNumber();
|
||||||
|
string combined = $"{motherboardSn}|{cpuSn}";
|
||||||
|
|
||||||
|
// 计算组合值的SHA256哈希
|
||||||
|
using (SHA256 sha256 = SHA256.Create())
|
||||||
|
{
|
||||||
|
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combined));
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < hashBytes.Length; i++)
|
||||||
|
{
|
||||||
|
builder.Append(hashBytes[i].ToString("x2"));
|
||||||
|
}
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证硬件标识是否匹配
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storedHardwareId">存储的硬件标识</param>
|
||||||
|
/// <returns>如果匹配返回true,否则返回false</returns>
|
||||||
|
public static bool ValidateHardwareId(string storedHardwareId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(storedHardwareId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string currentHardwareId = GetHardwareId();
|
||||||
|
return string.Equals(currentHardwareId, storedHardwareId, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
254
Utils/guokeExtensionClass.cs
Normal file
254
Utils/guokeExtensionClass.cs
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
using HslCommunication;
|
||||||
|
using SqlSugar;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace guoke
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 扩展类 所有扩展写在这里
|
||||||
|
/// </summary>
|
||||||
|
public static class guokeExtensionClass
|
||||||
|
{
|
||||||
|
#region 数据库扩展
|
||||||
|
/// <summary>
|
||||||
|
/// 检查表是否存在,如果不存在则创建表
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
public static bool guokeCheckToCreate<T>(this SqlSugarScope db)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string tableName = db.guokeGetTableName(typeof(T));//获取表名
|
||||||
|
|
||||||
|
if (!db.guokeCheckTable(tableName))
|
||||||
|
{
|
||||||
|
if (db.MappingTables == null)
|
||||||
|
{
|
||||||
|
db.MappingTables = new MappingTableList();
|
||||||
|
}
|
||||||
|
db.MappingTables.Add(typeof(T).Name, tableName); // 类名 表名
|
||||||
|
db.CodeFirst.InitTables(typeof(T)); //创建表
|
||||||
|
LogService.Log.Info("仓库", $"创建表:{tableName},模型:{typeof(T).Name}");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogService.Log.Error("数据库", $"检查创建表错误:{e.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 检查表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableName"></param>
|
||||||
|
public static bool guokeCheckTable(this SqlSugarScope db, string tableName)
|
||||||
|
{
|
||||||
|
return db.DbMaintenance.IsAnyTable(tableName);//判断状态表是否存在
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 根据类型获取表名
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string guokeGetTableName(this SqlSugarScope db, Type type)
|
||||||
|
{
|
||||||
|
object[] attributes = type.GetCustomAttributes(typeof(SugarTable), false);
|
||||||
|
if (attributes.Length > 0)
|
||||||
|
{
|
||||||
|
SugarTable sugarTable = (SugarTable)attributes[0];
|
||||||
|
return sugarTable.TableName;
|
||||||
|
}
|
||||||
|
return type.Name;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 检查表是否存在,如果不存在则创建表
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
public static bool guokeCheckToCreate<T>(this SqlSugarClient db)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string tableName = db.guokeGetTableName(typeof(T));//获取表名
|
||||||
|
|
||||||
|
if (!db.guokeCheckTable(tableName))
|
||||||
|
{
|
||||||
|
if (db.MappingTables == null)
|
||||||
|
{
|
||||||
|
db.MappingTables = new MappingTableList();
|
||||||
|
}
|
||||||
|
db.MappingTables.Add(typeof(T).Name, tableName); // 类名 表名
|
||||||
|
db.CodeFirst.InitTables(typeof(T)); //创建表
|
||||||
|
LogService.Log.Info("仓库", $"创建表:{tableName},模型:{typeof(T).Name}");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogService.Log.Error("数据库", $"检查创建表错误:{e.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 检查表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableName"></param>
|
||||||
|
public static bool guokeCheckTable(this SqlSugarClient db,string tableName)
|
||||||
|
{
|
||||||
|
return db.DbMaintenance.IsAnyTable(tableName);//判断状态表是否存在
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 根据类型获取表名
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string guokeGetTableName(this SqlSugarClient db,Type type)
|
||||||
|
{
|
||||||
|
object[] attributes = type.GetCustomAttributes(typeof(SugarTable), false);
|
||||||
|
if (attributes.Length > 0)
|
||||||
|
{
|
||||||
|
SugarTable sugarTable = (SugarTable)attributes[0];
|
||||||
|
return sugarTable.TableName;
|
||||||
|
}
|
||||||
|
return type.Name;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region 字节数组扩展
|
||||||
|
/// <summary>
|
||||||
|
/// 扩展方法,用于将一个 byte[] 追加到另一个 byte[] 后面
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="originalArray"></param>
|
||||||
|
/// <param name="arrayToAppend"></param>
|
||||||
|
/// <returns>追加后的数组</returns>
|
||||||
|
public static byte[] Append(this byte[] originalArray, byte[] arrayToAppend)
|
||||||
|
{
|
||||||
|
if (originalArray == null)
|
||||||
|
{
|
||||||
|
return arrayToAppend;
|
||||||
|
}
|
||||||
|
if (arrayToAppend == null)
|
||||||
|
{
|
||||||
|
return originalArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个 List<byte> 并将原始数组元素添加进去
|
||||||
|
List<byte> combinedList = new List<byte>(originalArray);
|
||||||
|
// 将待追加的数组元素添加到列表中
|
||||||
|
combinedList.AddRange(arrayToAppend);
|
||||||
|
|
||||||
|
// 将列表转换为 byte[] 数组并返回
|
||||||
|
return combinedList.ToArray();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 字符串扩展
|
||||||
|
/// <summary>
|
||||||
|
/// 字符串转换为整数,失败时返回默认值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">字符串</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int ToIntOrDefault(this string str, int defaultValue = 0)
|
||||||
|
{
|
||||||
|
return int.TryParse(str, out int result) ? result : defaultValue;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 将字符串转换为长整数,失败时返回默认值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">字符串 </param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static long ToLongOrDefault(this string str, long defaultValue = 0)
|
||||||
|
{
|
||||||
|
return long.TryParse(str, out long result) ? result : defaultValue;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 将字符串转换为整数,带操作结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static OperateResult<int> guokeToInt(this string str)
|
||||||
|
{
|
||||||
|
return int.TryParse(str, out int result) ? OperateResult.CreateSuccessResult(result) : OperateResult.CreateFailedResult<int>(new OperateResult("转换失败"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用正则表达式从字符串中提取匹配的部分,带操作结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">输入字符串</param>
|
||||||
|
/// <param name="pattern">正则表达式模式</param>
|
||||||
|
/// <returns>返回结果</returns>
|
||||||
|
public static OperateResult<string> guokeExtractWithRegex(this string input, string pattern)
|
||||||
|
{
|
||||||
|
// 创建正则表达式对象
|
||||||
|
Regex regex = new Regex(pattern);
|
||||||
|
|
||||||
|
// 匹配输入字符串
|
||||||
|
Match match = regex.Match(input);
|
||||||
|
|
||||||
|
// 如果匹配成功,返回匹配的值
|
||||||
|
if (match.Success && match.Groups.Count > 1)
|
||||||
|
{
|
||||||
|
return OperateResult.CreateSuccessResult(match.Groups[1].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有匹配,返回空字符串
|
||||||
|
return OperateResult.CreateFailedResult<string>(new OperateResult("提取失败"));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 提取字符串右边的连续数字,带操作结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static OperateResult<int> guokeExtractRightNumbers(this string input)
|
||||||
|
{
|
||||||
|
// 定义正则表达式模式,用于匹配字符串右边的连续数字
|
||||||
|
string pattern = @"\d+$";
|
||||||
|
// 使用 Regex.Match 方法进行匹配
|
||||||
|
Match match = Regex.Match(input, pattern);
|
||||||
|
|
||||||
|
// 检查是否匹配成功
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
// 如果匹配成功,尝试将匹配到的字符串转换为 int 类型
|
||||||
|
if (int.TryParse(match.Value, out int result))
|
||||||
|
{
|
||||||
|
return OperateResult.CreateSuccessResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有匹配到或者转换失败,返回 0
|
||||||
|
return OperateResult.CreateFailedResult<int>(new OperateResult("提取失败"));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 提取字符串左边的连续数字,带操作结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static OperateResult<int> guokeExtractLeftNumbers(this string input)
|
||||||
|
{
|
||||||
|
// 定义正则表达式模式,用于匹配字符串右边的连续数字
|
||||||
|
string pattern = @"^\d+";
|
||||||
|
// 使用 Regex.Match 方法进行匹配
|
||||||
|
Match match = Regex.Match(input, pattern);
|
||||||
|
|
||||||
|
// 检查是否匹配成功
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
// 如果匹配成功,尝试将匹配到的字符串转换为 int 类型
|
||||||
|
if (int.TryParse(match.Value, out int result))
|
||||||
|
{
|
||||||
|
return OperateResult.CreateSuccessResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有匹配到或者转换失败,返回 0
|
||||||
|
return OperateResult.CreateFailedResult<int>(new OperateResult("提取失败"));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
25
WpfApp.csproj
Normal file
25
WpfApp.csproj
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="HslCommunication" Version="12.5.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="SqlSugarCore" Version="5.1.4.205" />
|
||||||
|
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
14
WpfApp.csproj.user
Normal file
14
WpfApp.csproj.user
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Update="App.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</ApplicationDefinition>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="MainWindow.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
25
WpfApp.sln
Normal file
25
WpfApp.sln
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.8.34511.84
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApp", "WpfApp.csproj", "{6B6D9743-392F-41FB-92B6-D99EA4575D8C}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{6B6D9743-392F-41FB-92B6-D99EA4575D8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6B6D9743-392F-41FB-92B6-D99EA4575D8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6B6D9743-392F-41FB-92B6-D99EA4575D8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6B6D9743-392F-41FB-92B6-D99EA4575D8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {3AA9F1A3-A510-4342-8EAC-E607C5FB088E}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
18
appsettings.json
Normal file
18
appsettings.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"Logs": {
|
||||||
|
"LogLevel": 6,
|
||||||
|
"MaxArchiveFiles": 100,
|
||||||
|
"MaxFileSize": 1,
|
||||||
|
"LogFilePath": "logs"
|
||||||
|
},
|
||||||
|
"DB": [
|
||||||
|
{
|
||||||
|
"Name": "LocalData",
|
||||||
|
"Enabled": true,
|
||||||
|
"Type": 2,
|
||||||
|
"Connection": "DataSource=LocalData.db",
|
||||||
|
"Remarks": "",
|
||||||
|
"Print": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user