From dd43cf013564df94b60bb936c7136714cb75af2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BF=B1=20=E6=B2=88?= <2401809606@qq.com>
Date: Fri, 1 May 2026 14:42:51 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?=
=?UTF-8?q?=E4=BB=B6=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
App.xaml | 9 +
App.xaml.cs | 17 +
AssemblyInfo.cs | 10 +
ConvertMoels/StatenConvert.cs | 48 +++
ConvertMoels/StatenERRConvert.cs | 48 +++
MainWindow.xaml | 72 +++++
MainWindow.xaml.cs | 115 +++++++
ResourceLanguage.Designer.cs | 387 +++++++++++++++++++++++
ResourceLanguage.resx | 228 +++++++++++++
SunlightAggregationManager.csproj | 39 +++
SunlightAggregationManager.slnx | 3 +
UserClass/AsyncTcpServer.cs | 208 ++++++++++++
UserClass/DataBase.cs | 79 +++++
UserClass/IniFile.cs | 83 +++++
UserClass/NetFwManger.cs | 75 +++++
UserClass/SqliteHelper.cs | 510 ++++++++++++++++++++++++++++++
UserClass/TextWriter.cs | 62 ++++
View/SettingPage.xaml | 38 +++
View/SettingPage.xaml.cs | 157 +++++++++
View/StatePage.xaml | 13 +
View/StatePage.xaml.cs | 33 ++
View/UserPage.xaml | 114 +++++++
View/UserPage.xaml.cs | 90 ++++++
View/imgQR.xaml | 17 +
View/imgQR.xaml.cs | 105 ++++++
View/logPage.xaml | 31 ++
View/logPage.xaml.cs | 143 +++++++++
ViewModel/MainWindowViewModel.cs | 231 ++++++++++++++
Window/UserSet.xaml | 12 +
Window/UserSet.xaml.cs | 25 ++
30 files changed, 3002 insertions(+)
create mode 100644 App.xaml
create mode 100644 App.xaml.cs
create mode 100644 AssemblyInfo.cs
create mode 100644 ConvertMoels/StatenConvert.cs
create mode 100644 ConvertMoels/StatenERRConvert.cs
create mode 100644 MainWindow.xaml
create mode 100644 MainWindow.xaml.cs
create mode 100644 ResourceLanguage.Designer.cs
create mode 100644 ResourceLanguage.resx
create mode 100644 SunlightAggregationManager.csproj
create mode 100644 SunlightAggregationManager.slnx
create mode 100644 UserClass/AsyncTcpServer.cs
create mode 100644 UserClass/DataBase.cs
create mode 100644 UserClass/IniFile.cs
create mode 100644 UserClass/NetFwManger.cs
create mode 100644 UserClass/SqliteHelper.cs
create mode 100644 UserClass/TextWriter.cs
create mode 100644 View/SettingPage.xaml
create mode 100644 View/SettingPage.xaml.cs
create mode 100644 View/StatePage.xaml
create mode 100644 View/StatePage.xaml.cs
create mode 100644 View/UserPage.xaml
create mode 100644 View/UserPage.xaml.cs
create mode 100644 View/imgQR.xaml
create mode 100644 View/imgQR.xaml.cs
create mode 100644 View/logPage.xaml
create mode 100644 View/logPage.xaml.cs
create mode 100644 ViewModel/MainWindowViewModel.cs
create mode 100644 Window/UserSet.xaml
create mode 100644 Window/UserSet.xaml.cs
diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..67edea4
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..ca24e02
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,17 @@
+using SunlightAggregationManager.UserClass;
+using System.Configuration;
+using System.Data;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace SunlightAggregationManager
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+
+ }
+
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/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/ConvertMoels/StatenConvert.cs b/ConvertMoels/StatenConvert.cs
new file mode 100644
index 0000000..d0e5a3a
--- /dev/null
+++ b/ConvertMoels/StatenConvert.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Controls;
+
+///
+/// 运行状态变换器
+/// 输入:状态码
+/// 输出:状态文字
+///
+namespace SunlightAggregationManager.ConvertMoels
+{
+ internal class StatenConvert : IValueConverter
+ {
+
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ if (value == null)
+ {
+ return "";
+ }
+ else
+ {
+ string Staten = "";
+ var i = value.ToString();
+
+ if (i == "10") Staten = ResourceLanguage.ERR;//异常
+ if (i == "15") Staten = ResourceLanguage.Offine;//离线
+ if (i == "20") Staten = ResourceLanguage.Online;//在线
+ if (i == "99") Staten = ResourceLanguage.Disable;//禁用
+
+ return Staten;
+ }
+ }
+
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ }
+}
diff --git a/ConvertMoels/StatenERRConvert.cs b/ConvertMoels/StatenERRConvert.cs
new file mode 100644
index 0000000..fe5b528
--- /dev/null
+++ b/ConvertMoels/StatenERRConvert.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Controls;
+
+namespace SunlightAggregationManager.ConvertMoels
+{
+ internal class StatenERRConvert : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ if (value == null)
+ {
+ return "black";
+ }
+ else
+ {
+ if (value.ToString() == "99")
+ {
+ return "red";//零返回红色
+ }
+ else if (value.ToString() == "10")
+ {
+ return "yellow";//零返回黄色
+ }
+ else if (value.ToString() == "20")
+ {
+ return "green";//零返回绿
+ }
+ else
+ {
+ return "black";//非零返回黑色
+ }
+ }
+ }
+
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..7c4a50a
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..1ccc0d7
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,115 @@
+using SunlightAggregationManager.UserClass;
+using SunlightAggregationManager.ViewModel;
+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;
+
+namespace SunlightAggregationManager
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ GlobalLogManager.RegisterTarget(LOG);
+
+ DataContext = new MainWindowViewModel();
+ }
+
+ //标题栏拖动
+ private void GridTitle_MouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.ChangedButton == MouseButton.Left)
+ DragMove();
+ }
+
+ //最大
+ private void maximize_Click(object sender, RoutedEventArgs e)
+ {
+ if (WindowState == WindowState.Maximized)
+ { WindowState = WindowState.Normal; }
+ else
+ { WindowState = WindowState.Maximized; }
+ }
+ //最小
+ private void minimize_Click(object sender, RoutedEventArgs e)
+ {
+ if (WindowState == WindowState.Minimized)
+ { WindowState = WindowState.Normal; }
+ else
+ { WindowState = WindowState.Minimized; }
+ }
+ //关闭
+ private void exit_Click(object sender, RoutedEventArgs e)
+ {
+ // 关闭整个应用程序
+ System.Windows.Application.Current.Shutdown();
+ }
+
+ private void ListViewItem_MouseLeftButtonUp_state(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Visible;
+ Picture.Visibility= Visibility.Collapsed;
+ }
+ private void ListViewItem_MouseLeftButtonUp_serve(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ }
+ private void ListViewItem_MouseLeftButtonUp_log(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ Picture.Content = new View.logPage();
+ }
+ private void ListViewItem_MouseLeftButtonUp_machine(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ }
+ private void ListViewItem_MouseLeftButtonUp_extend(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ }
+ private void ListViewItem_MouseLeftButtonUp_user(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ Picture.Content = new View.UserPage();
+ }
+ private void ListViewItem_MouseLeftButtonUp_Action(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ }
+
+ private void ListViewItem_MouseLeftButtonUp_set(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ Picture.Content = new View.SettingPage();
+ }
+ private void ListViewItem_MouseLeftButtonUp_help(object sender, MouseButtonEventArgs e)
+ {
+ state.Visibility = Visibility.Collapsed;
+ Picture.Visibility = Visibility.Visible;
+ Picture.Content = new View.imgQR();
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/ResourceLanguage.Designer.cs b/ResourceLanguage.Designer.cs
new file mode 100644
index 0000000..e6ba4b6
--- /dev/null
+++ b/ResourceLanguage.Designer.cs
@@ -0,0 +1,387 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace SunlightAggregationManager {
+ using System;
+
+
+ ///
+ /// 一个强类型的资源类,用于查找本地化的字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class ResourceLanguage {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal ResourceLanguage() {
+ }
+
+ ///
+ /// 返回此类使用的缓存的 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SunlightAggregationManager.ResourceLanguage", typeof(ResourceLanguage).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// 查找类似 行为 的本地化字符串。
+ ///
+ public static string Action {
+ get {
+ return ResourceManager.GetString("Action", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 删除 的本地化字符串。
+ ///
+ public static string Delete {
+ get {
+ return ResourceManager.GetString("Delete", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 部门 的本地化字符串。
+ ///
+ public static string Department {
+ get {
+ return ResourceManager.GetString("Department", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 禁用 的本地化字符串。
+ ///
+ public static string Disable {
+ get {
+ return ResourceManager.GetString("Disable", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 启用 的本地化字符串。
+ ///
+ public static string Enable {
+ get {
+ return ResourceManager.GetString("Enable", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 启用HTTP 的本地化字符串。
+ ///
+ public static string EnableHTTP {
+ get {
+ return ResourceManager.GetString("EnableHTTP", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 启用TCP 的本地化字符串。
+ ///
+ public static string EnableTCP {
+ get {
+ return ResourceManager.GetString("EnableTCP", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 启用TLS1.2 的本地化字符串。
+ ///
+ public static string EnableTLS {
+ get {
+ return ResourceManager.GetString("EnableTLS", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 错误 的本地化字符串。
+ ///
+ public static string ERR {
+ get {
+ return ResourceManager.GetString("ERR", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 扩展 的本地化字符串。
+ ///
+ public static string extend {
+ get {
+ return ResourceManager.GetString("extend", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 组 的本地化字符串。
+ ///
+ public static string Groups {
+ get {
+ return ResourceManager.GetString("Groups", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 帮助 的本地化字符串。
+ ///
+ public static string help {
+ get {
+ return ResourceManager.GetString("help", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 历史 的本地化字符串。
+ ///
+ public static string history {
+ get {
+ return ResourceManager.GetString("history", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 信息 的本地化字符串。
+ ///
+ public static string information {
+ get {
+ return ResourceManager.GetString("information", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 语言 的本地化字符串。
+ ///
+ public static string Language {
+ get {
+ return ResourceManager.GetString("Language", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 最后行为时间 的本地化字符串。
+ ///
+ public static string LastActionTime {
+ get {
+ return ResourceManager.GetString("LastActionTime", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 最后登录地址 的本地化字符串。
+ ///
+ public static string LastLoginIP {
+ get {
+ return ResourceManager.GetString("LastLoginIP", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 最后登录终端 的本地化字符串。
+ ///
+ public static string LastLoginTerminal {
+ get {
+ return ResourceManager.GetString("LastLoginTerminal", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 最后登录时间 的本地化字符串。
+ ///
+ public static string LastLoginTime {
+ get {
+ return ResourceManager.GetString("LastLoginTime", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 最后下线时间 的本地化字符串。
+ ///
+ public static string LastOfflineTime {
+ get {
+ return ResourceManager.GetString("LastOfflineTime", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 日志 的本地化字符串。
+ ///
+ public static string log {
+ get {
+ return ResourceManager.GetString("log", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 设备 的本地化字符串。
+ ///
+ public static string machine {
+ get {
+ return ResourceManager.GetString("machine", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 模式 的本地化字符串。
+ ///
+ public static string Mode {
+ get {
+ return ResourceManager.GetString("Mode", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 姓名 的本地化字符串。
+ ///
+ public static string Name {
+ get {
+ return ResourceManager.GetString("Name", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 离线 的本地化字符串。
+ ///
+ public static string Offine {
+ get {
+ return ResourceManager.GetString("Offine", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 在线 的本地化字符串。
+ ///
+ public static string Online {
+ get {
+ return ResourceManager.GetString("Online", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 密码 的本地化字符串。
+ ///
+ public static string Password {
+ get {
+ return ResourceManager.GetString("Password", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 查询 的本地化字符串。
+ ///
+ public static string query {
+ get {
+ return ResourceManager.GetString("query", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 保存 的本地化字符串。
+ ///
+ public static string save {
+ get {
+ return ResourceManager.GetString("save", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 服务 的本地化字符串。
+ ///
+ public static string serve {
+ get {
+ return ResourceManager.GetString("serve", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 服务器 的本地化字符串。
+ ///
+ public static string SERVER {
+ get {
+ return ResourceManager.GetString("SERVER", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 设置 的本地化字符串。
+ ///
+ public static string set {
+ get {
+ return ResourceManager.GetString("set", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 数据库 的本地化字符串。
+ ///
+ public static string SQL {
+ get {
+ return ResourceManager.GetString("SQL", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 状态 的本地化字符串。
+ ///
+ public static string state {
+ get {
+ return ResourceManager.GetString("state", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 测试 的本地化字符串。
+ ///
+ public static string Test {
+ get {
+ return ResourceManager.GetString("Test", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 用户 的本地化字符串。
+ ///
+ public static string user {
+ get {
+ return ResourceManager.GetString("user", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/ResourceLanguage.resx b/ResourceLanguage.resx
new file mode 100644
index 0000000..271d23b
--- /dev/null
+++ b/ResourceLanguage.resx
@@ -0,0 +1,228 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 行为
+
+
+ 删除
+
+
+ 部门
+
+
+ 禁用
+
+
+ 启用
+
+
+ 启用HTTP
+
+
+ 启用TCP
+
+
+ 启用TLS1.2
+
+
+ 错误
+
+
+ 扩展
+
+
+ 组
+
+
+ 帮助
+
+
+ 历史
+
+
+ 信息
+
+
+ 语言
+
+
+ 最后行为时间
+
+
+ 最后登录地址
+
+
+ 最后登录终端
+
+
+ 最后登录时间
+
+
+ 最后下线时间
+
+
+ 日志
+
+
+ 设备
+
+
+ 模式
+
+
+ 姓名
+
+
+ 离线
+
+
+ 在线
+
+
+ 密码
+
+
+ 查询
+
+
+ 保存
+
+
+ 服务
+
+
+ 服务器
+
+
+ 设置
+
+
+ 数据库
+
+
+ 状态
+
+
+ 测试
+
+
+ 用户
+
+
\ No newline at end of file
diff --git a/SunlightAggregationManager.csproj b/SunlightAggregationManager.csproj
new file mode 100644
index 0000000..0e0bee5
--- /dev/null
+++ b/SunlightAggregationManager.csproj
@@ -0,0 +1,39 @@
+
+
+
+ WinExe
+ net10.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ ResourceLanguage.resx
+
+
+ Code
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ ResourceLanguage.Designer.cs
+
+
+
+
diff --git a/SunlightAggregationManager.slnx b/SunlightAggregationManager.slnx
new file mode 100644
index 0000000..dcfb87d
--- /dev/null
+++ b/SunlightAggregationManager.slnx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/UserClass/AsyncTcpServer.cs b/UserClass/AsyncTcpServer.cs
new file mode 100644
index 0000000..5728727
--- /dev/null
+++ b/UserClass/AsyncTcpServer.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text;
+using TouchSocket.Core;
+using TouchSocket.Sockets;
+using SunlightAggregationManager.ViewModel;
+using System.Security.Cryptography.Pkcs;
+
+namespace SunlightAggregationManager.UserClass
+{
+ public class AsyncTcpServer
+ {
+ public class Person
+ {
+ public required string User { get; set; }
+ public required string Password { get; set; }
+ public required string Terminal { get; set; }
+ public required string Dat { get; set; }
+ }
+ public static TcpService service = new TcpService();
+ public static async Task TcpMain(int port1 ,int port2)
+ {
+ NetFwManger.AllowPort(port1, "TCP");//开放7790端口
+ NetFwManger.AllowPort(port2, "TCP");//开放7790端口
+ //TcpService service = new TcpService();
+ service.Connecting = (client, e) => { return EasyTask.CompletedTask; };//有客户端正在连接
+ service.Connected = (client, e) => {return EasyTask.CompletedTask;};//有客户端成功连接
+ service.Closing = (client, e) => { return EasyTask.CompletedTask; };//有客户端正在断开连接,只有当主动断开时才有效。
+ service.Closed = (client, e) => {
+ //离线记录
+ Dictionary myDictOff = new Dictionary();
+ myDictOff.Add("State", 15);
+ myDictOff.Add("LastActionTime", DateTime.Now);
+ myDictOff.Add("LastOfflineTime", DateTime.Now);
+ myDictOff.Add("Note", "Offline");
+ MainWindowViewModel.Updata_Memory(MainWindowViewModel._userData, "LinkID='" + client.Id + "'", myDictOff);
+ Console.WriteLine("[Offline]IP[" + client.IP + "]ID["+ client.Id + "]");
+ return EasyTask.CompletedTask;
+ };//有客户端断开连接
+ service.Received = (client, e) =>{
+ string DAT = e.Memory.Span.ToString(Encoding.UTF8);
+ try
+ {
+ Person? deserializedPerson = JsonSerializer.Deserialize(DAT);
+
+ var _user = MainWindowViewModel.Selet_Memory(MainWindowViewModel._userData, "State", "User='" + deserializedPerson?.User +
+ "' and Password ='" + deserializedPerson?.Password + "'");
+ if (_user != null)
+ {
+ string _dat = _user?.ToString() ?? "0";
+ if (_dat != "99")
+ {
+ if (_dat == "20")
+ {//行为
+ Dictionary myDict = new Dictionary();
+ myDict.Add("LinkID", client.Id);
+ myDict.Add("LastLoginIP", client.IP);
+ myDict.Add("LastActionTime", DateTime.Now);
+ myDict.Add("Note", deserializedPerson?.Dat ?? "unknown");
+ MainWindowViewModel.Updata_Memory(MainWindowViewModel._userData, "User='" + deserializedPerson?.User + "'", myDict);
+
+ Console.WriteLine("[Log on]User[" + deserializedPerson?.User + "]Note[" + (deserializedPerson?.Dat ?? "unknown") + "]");
+ }
+ else
+ {//首次登录记录
+ Dictionary myDict = new Dictionary();
+ myDict.Add("State", 20);
+ myDict.Add("LinkID", client.Id);
+ myDict.Add("LastLoginIP", client.IP);
+ myDict.Add("LastLoginTime", DateTime.Now);
+ myDict.Add("LastLoginTerminal", deserializedPerson?.Terminal ?? "unknown device");
+ myDict.Add("LastActionTime", DateTime.Now);
+ myDict.Add("Note", deserializedPerson?.Dat ?? "unknown");
+ MainWindowViewModel.Updata_Memory(MainWindowViewModel._userData, "User='" + deserializedPerson?.User + "'", myDict);
+
+ Console.WriteLine("[Log on]User[" + deserializedPerson?.User + "]IP[" + client.IP +
+ "]ID[" + client.Id + "]Terminal[" + (deserializedPerson?.Terminal ?? "unknown device") + "]");
+ }
+ }
+ else
+ {//停用账号关闭连接
+ client.SendAsync("Account Deactivated");
+ foreach (var item in AsyncTcpServer.service.Clients)
+ {
+ if (item.Id == client.Id)
+ {
+ item.CloseAsync();
+
+ //在断开连接后,释放对象。
+ item.Dispose();
+ }
+ }
+ }
+ }
+ else
+ { //无效账号关闭连接
+ client.SendAsync("Invalid Account");
+ foreach (var item in AsyncTcpServer.service.Clients)
+ {
+ if (item.Id == client.Id)
+ {
+ item.CloseAsync();
+ item.Dispose();
+ }
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {//错误
+ Console.WriteLine("Tcp:" + ex.ToString());
+ client.SendAsync("Target instruction parsing error manager will close connection");
+ foreach (var item in AsyncTcpServer.service.Clients)
+ {
+ if (item.Id == client.Id)
+ {
+ item.CloseAsync();
+ item.Dispose();
+ }
+ }
+ }
+
+ //string DAT = e.ByteBlock.Span.ToString(Encoding.UTF8).Substring(5);
+ //string SYSDAT = "";// = e.ByteBlock.Span.ToString(Encoding.ASCII).Substring(5);
+ // string SYSKEY = "";
+ // if (DAT.Length >= 16) SYSKEY = DAT.Substring(0, 16);
+ // if (DAT.Length > 16) SYSDAT = DAT.Substring(16);
+
+
+ return EasyTask.CompletedTask;
+ };
+
+ await service.SetupAsync(new TouchSocketConfig()//载入配置
+ // .SetMaxBufferSize(1024 * 1024 * 100)
+ //.SetMinBufferSize(1024 * 1024)
+ .SetListenIPHosts(new IPHost[] { new IPHost(port1), new IPHost(port2) })//同时监听两个地址
+ .ConfigureContainer(a =>//容器的配置顺序应该在最前面
+ {
+ })
+ .ConfigurePlugins(a =>
+ {
+ })
+ );
+ await service.StartAsync();//启动
+
+ Console.WriteLine("TCP Port:"+ port1.ToString());
+ Console.WriteLine("TCP Port:"+ port2.ToString());
+ Console.WriteLine("TCP SERVER:START");
+ }
+ }
+
+ class MyTcpService : TcpService
+ {
+ protected override MyTcpSessionClient NewClient()
+ {
+ return new MyTcpSessionClient();
+ }
+ }
+
+ class MyTcpSessionClient : TcpSessionClient
+ {
+ internal void SetDataHandlingAdapter(SingleStreamDataHandlingAdapter adapter)
+ {
+ base.SetAdapter(adapter);
+ }
+ }
+
+ ///
+ /// 此插件实现,按照不同端口,使用不同适配器。
+ ///
+ /// - 7789端口:使用"**"结尾的数据
+ /// - 7790端口:使用"##"结尾的数据
+ ///
+ ///
+ internal class DifferentProtocolPlugin : PluginBase, ITcpConnectingPlugin, ITcpReceivedPlugin
+ {
+ public async Task OnTcpConnecting(ITcpSession client, ConnectingEventArgs e)
+ {
+ if (client is MyTcpSessionClient sessionClient)
+ {
+ if (sessionClient.ServicePort == 7789)
+ {
+ sessionClient.SetDataHandlingAdapter(new TerminatorPackageAdapter("**"));
+ }
+ else
+ {
+ sessionClient.SetDataHandlingAdapter(new TerminatorPackageAdapter("##"));
+ }
+ }
+
+ await e.InvokeNext();
+ }
+
+ public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e)
+ {
+ //如果是自定义适配器,此处解析时,可以判断e.RequestInfo的类型
+
+ if (client is ITcpSessionClient sessionClient)
+ {
+ sessionClient.Logger.Info($"{sessionClient.GetIPPort()}收到数据,服务器端口:{sessionClient.ServicePort},数据:{e.Memory.Span.ToString(Encoding.UTF8)}");
+ }
+
+ await e.InvokeNext();
+ }
+
+ }
+}
diff --git a/UserClass/DataBase.cs b/UserClass/DataBase.cs
new file mode 100644
index 0000000..80f2f4a
--- /dev/null
+++ b/UserClass/DataBase.cs
@@ -0,0 +1,79 @@
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.Data.SqlClient;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows.Input;
+using TouchSocket.Sockets;
+
+namespace SunlightAggregationManager.UserClass
+{
+ public class DataBase
+ {
+ private string DB_NAME = null!;
+ private string DB_TYPE = null!;
+ private string DB_ID = null!;
+ private string DB_IP = null!;
+ private string DB_PASSWORD = null!;
+
+
+ public void Config(string ip, string name, string type, string id, string password)
+ {
+ DB_IP = ip;
+ DB_ID = id;
+ DB_NAME = name;
+ DB_TYPE = type;
+ DB_PASSWORD = password;
+
+ _= ConfigAsync();
+
+ }
+ public async Task ConfigAsync()
+ {
+ await Task.Delay(3000);
+ //进入后台线程操作
+ await Task.Run(() =>
+ {
+ try
+ {
+ // 构建连接字符串
+ var builder = new SqlConnectionStringBuilder
+ {
+ DataSource = DB_IP,
+ InitialCatalog = DB_NAME,
+ IntegratedSecurity = DB_TYPE == "Windows Authentication",
+ TrustServerCertificate = true,
+ ConnectTimeout = 5
+ };
+
+ if (!builder.IntegratedSecurity)
+ {
+ builder.UserID = DB_ID;
+ builder.Password = DB_PASSWORD;
+ }
+
+ //连接数据库
+ using var conn_SC = new SqlConnection(builder.ConnectionString);
+ conn_SC.Open();
+ using (var cmd = new SqlCommand("SELECT 1", conn_SC))
+ {
+ cmd.ExecuteReader();
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("DataBase Link timeout\n"+ex.ToString());
+ // 失败处理
+ return; // 直接返回,不执行后续代码
+ }
+ Console.WriteLine("DataBase Link succeed");
+ });
+ }
+
+ public void Database(string dat)
+ {
+
+ }
+ }
+}
diff --git a/UserClass/IniFile.cs b/UserClass/IniFile.cs
new file mode 100644
index 0000000..310af47
--- /dev/null
+++ b/UserClass/IniFile.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Input;
+
+namespace SunlightAggregationManager.UserClass
+{
+ internal class IniFile
+ {
+ public class IniFiles
+ {
+ public string path;
+ [DllImport("kernel32")] //返回0表示失败,非0为成功
+ private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
+ [DllImport("kernel32")] //返回取得字符串缓冲区的长度
+ private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
+ ///
+ /// 保存ini文件的路径
+ /// 调用示例:var ini = IniFiles("C:\file.ini");
+ ///
+ ///
+ public IniFiles(string iniPath)
+ {
+ this.path = iniPath;
+ }
+ ///
+ /// 写Ini文件
+ /// 调用示例:ini.IniWritevalue("Server","name","localhost");
+ ///
+ /// [缓冲区]
+ /// 键
+ /// 值
+ public void IniWritevalue(string Section, string Key, string? value)
+ {
+ // 确保路径有效
+ if (!File.Exists(path))
+ {
+ // 如果文件不存在,创建一个空的
+ using (FileStream fs = File.Create(path)) { }
+ }
+
+ // 处理 null 值,避免 API 调用出现问题
+ string safeValue = value ?? string.Empty;
+
+ long result = WritePrivateProfileString(Section, Key, safeValue, path);
+
+ if (result==0)
+ {
+ // 可以选择抛出异常或记录日志
+ // throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
+ }
+ ///
+ /// 读Ini文件
+ /// 调用示例:ini.IniWritevalue("Server","name");
+ ///
+ /// [缓冲区]
+ /// 键
+ /// 值
+ public string IniReadvalue(string Section, string Key)
+ {
+ try
+ {
+ if (!File.Exists(path))
+ {
+ return string.Empty;
+ }
+
+ var sb = new StringBuilder(4096);
+ GetPrivateProfileString(Section, Key, string.Empty, sb, sb.Capacity, path);
+ return sb.ToString();
+ }
+ catch (Exception)
+ {//错误返回0
+ return "0";
+ }
+ }
+
+ }
+ }
+}
diff --git a/UserClass/NetFwManger.cs b/UserClass/NetFwManger.cs
new file mode 100644
index 0000000..344ced9
--- /dev/null
+++ b/UserClass/NetFwManger.cs
@@ -0,0 +1,75 @@
+using NetFwTypeLib;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace SunlightAggregationManager.UserClass
+{
+ public class NetFwManger
+ {
+ private static string GetName(int port, string protocol)
+ {
+ return "port-" + protocol + "-" + port;
+ }
+
+ public static void AllowPort(int port, string protocol)
+ {
+
+ DelPort(port, protocol);
+ //创建一个INetFwRule对象
+ Type type = Type.GetTypeFromProgID("HNetCfg.FwRule") ?? throw new
+ InvalidOperationException("Failed to retrieve HNetCfg.FwRule type. Ensure the COM component is registered.");
+ INetFwRule? rule = (INetFwRule?)Activator.CreateInstance(type);
+
+ //设置规则的属性
+ rule?.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW; //允许连接
+ rule?.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN; //入站规则
+ rule?.Enabled = true; //启用规则
+ rule?.InterfaceTypes = "All"; //适用于所有网络接口
+ rule?.Name = GetName(port, protocol); //规则名称
+ if (protocol.ToLower() == "tcp")
+ {
+ rule?.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; //TCP协议
+ }
+ else
+ {
+ rule?.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP; //UDP协议
+ }
+
+ rule?.LocalPorts = "" + port; //本地端口号
+
+ //获取FirewallPolicy对象
+ Type policyType = Type.GetTypeFromProgID("HNetCfg.FwPolicy2") ?? throw new
+ InvalidOperationException("Failed to retrieve HNetCfg.FwRule type. Ensure the COM component is registered.");
+ INetFwPolicy2? policy = (INetFwPolicy2?)Activator.CreateInstance(policyType);
+
+ //将规则添加到防火墙策略中
+ policy?.Rules.Add(rule);
+ }
+
+ public static void DelPort(int port, string protocol)
+ {
+ //获取FirewallPolicy对象
+ Type policyType = Type.GetTypeFromProgID("HNetCfg.FwPolicy2") ?? throw new
+ InvalidOperationException("Failed to retrieve HNetCfg.FwRule type. Ensure the COM component is registered.");
+ INetFwPolicy2? policy = (INetFwPolicy2?)Activator.CreateInstance(policyType);
+
+ //获取现有的规则集合
+ INetFwRules? rules = policy?.Rules;
+ if (rules != null)
+ {
+ //查找名称的规则并删除它
+ foreach (INetFwRule rule in rules)
+ {
+ if (rule.Name == GetName(port, protocol))
+ {
+ rules.Remove(rule.Name);
+ Console.WriteLine(@"Firewall rule deleted successfully.");
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/UserClass/SqliteHelper.cs b/UserClass/SqliteHelper.cs
new file mode 100644
index 0000000..ec01223
--- /dev/null
+++ b/UserClass/SqliteHelper.cs
@@ -0,0 +1,510 @@
+using Microsoft.Data.Sqlite;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+
+namespace SunlightAggregationManager.UserClass
+{
+
+ // 辅助锁类 (代码中引用了 ClsLock,这里提供一个简单的实现或占位)
+ // 请根据实际情况替换为你项目中的 ClsLock,或使用 ReaderWriterLockSlim
+ public class ClsLock : IDisposable
+ {
+ private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
+
+ public IDisposable Read()
+ {
+ _lock.EnterReadLock();
+ return new DisposableAction(() => _lock.ExitReadLock());
+ }
+
+ public void Dispose() => _lock?.Dispose();
+
+ private class DisposableAction : IDisposable
+ {
+ private readonly Action _action;
+ public DisposableAction(Action action) => _action = action;
+ public void Dispose() => _action?.Invoke();
+ }
+ }
+
+ public class SqliteHelper
+ {
+ #region 字段
+ private SqliteTransaction? dbTrans = null!;
+ private static readonly Dictionary RWL = new Dictionary();
+ private readonly string mDataFile;
+ private readonly string mPassWord = "";
+ private readonly string lockName = "";
+ private SqliteConnection mConn = null!;
+ #endregion
+
+ #region 构造函数
+ public SqliteHelper(string dataFile)
+ {
+ this.mDataFile = dataFile ?? throw new ArgumentNullException(nameof(dataFile));
+ this.mDataFile = dataFile; // 注意:原代码有两次赋值,保留逻辑
+ if (!RWL.ContainsKey(dataFile))
+ {
+ lockName = dataFile;
+ RWL.Add(dataFile, new ClsLock());
+ }
+ }
+
+ public SqliteHelper(string dataFile, string PassWord)
+ {
+ this.mDataFile = dataFile ?? throw new ArgumentNullException(nameof(dataFile));
+ this.mPassWord = PassWord ?? throw new ArgumentNullException(nameof(PassWord));
+ this.mDataFile = dataFile;
+ if (!RWL.ContainsKey(dataFile))
+ {
+ lockName = dataFile;
+ RWL.Add(dataFile, new ClsLock());
+ }
+ }
+ #endregion
+
+ #region 打开/关闭 数据库
+ // 注意:Microsoft.Data.Sqlite.Core 不支持连接字符串构建器,需手动拼接
+ // 加密支持通常需要 Microsoft.Data.Sqlite.Core + SQLitePCLRaw.bundle_green (且加密功能可能受限)
+
+ public void Open()
+ {
+ // 构建连接字符串
+ var connectionString = $"Data Source={mDataFile}";
+ if (!string.IsNullOrWhiteSpace(mPassWord))
+ {
+ connectionString += $";Password={mPassWord}"; // 注意:原生 Microsoft.Data.Sqlite 不支持加密
+ // 如果需要加密,通常需要使用 SQLitePCLRaw 的特定提供程序或第三方库
+ }
+
+ mConn = new SqliteConnection(connectionString);
+
+ // 如果文件不存在,Open() 会自动创建数据库文件
+ // 但表结构需要你自己执行 CREATE TABLE
+ mConn.Open();
+
+ Console.WriteLine("The database was opened successfully");
+ }
+
+ public void Close()
+ {
+ if (this.mConn != null)
+ {
+ try
+ {
+ this.mConn.Close();
+ // 注意:不要在这里移除 RWL,因为这是静态的,可能影响其他实例
+ // if (RWL.ContainsKey(LockName)) { RWL.Remove(LockName); }
+ }
+ catch
+ {
+ Console.WriteLine("Shutdown failed");
+ }
+ }
+ Console.WriteLine("The database was shut down successfully");
+ }
+ #endregion
+
+ #region 事务
+ public void BeginTrain()
+ {
+ EnsureConnection();
+ dbTrans = mConn.BeginTransaction();
+ }
+
+ public void DBCommit()
+ {
+ try
+ {
+ dbTrans?.Commit();
+ dbTrans?.Dispose();
+ dbTrans = null; // 提交后置空
+ }
+ catch (Exception)
+ {
+ dbTrans?.Rollback();
+ dbTrans?.Dispose();
+ dbTrans = null;
+ }
+ }
+ #endregion
+
+ #region 工具
+ // OpenConnection 逻辑已合并到 Open() 方法中,因为 SqliteConnection 构造函数只接受字符串
+
+ public SqliteConnection Connection
+ {
+ get { return mConn; }
+ private set { mConn = value ?? throw new ArgumentNullException(); }
+ }
+
+ protected void EnsureConnection()
+ {
+ if (this.mConn == null)
+ {
+ throw new Exception("SQLiteManager.Connection=null");
+ }
+ if (mConn.State != ConnectionState.Open)
+ {
+ mConn.Open();
+ }
+ }
+
+ public string GetDataFile() => this.mDataFile;
+
+ public bool TableExists(string table)
+ {
+ if (string.IsNullOrEmpty(table)) return false;
+ EnsureConnection();
+
+ // 注意:参数化查询在表名/列名上无效,这里使用字符串拼接(需确保table变量安全)
+ // 或者使用 PRAGMA table_info(table_name)
+ var sql = $"SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{table}'";
+
+ using (var cmd = new SqliteCommand(sql, Connection))
+ {
+ var result = cmd.ExecuteScalar();
+ return Convert.ToInt32(result) > 0;
+ }
+ }
+
+ public bool Vacuum()
+ {
+ try
+ {
+ using (var Command = new SqliteCommand("VACUUM", Connection))
+ {
+ Command.ExecuteNonQuery();
+ }
+ return true;
+ }
+ catch (Exception) // SqliteException
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 将 IDataReader 转换为 DataSet
+ ///
+ /// 已经执行了查询的 DataReader
+ /// 包含数据的 DataSet
+ public DataSet ReaderToDataSet(IDataReader reader)
+ {
+ DataSet ds = new DataSet();
+
+ do
+ {
+ DataTable schemaTable = reader.GetSchemaTable()!;
+ DataTable dataTable = new DataTable();
+
+ if (schemaTable != null)
+ {
+ // 创建列
+ foreach (DataRow schemaRow in schemaTable.Rows)
+ {
+ string colName = schemaRow["ColumnName"].ToString()!;
+ Type dataType = (Type)schemaRow["DataType"];
+
+ // 防止列名重复
+ string uniqueColName = colName;
+ int i = 1;
+ while (dataTable.Columns.Contains(uniqueColName))
+ {
+ uniqueColName = colName + i++;
+ }
+
+ DataColumn column = new DataColumn(uniqueColName, dataType);
+ dataTable.Columns.Add(column);
+ }
+
+ // 读取行
+ while (reader.Read())
+ {
+ object[] values = new object[dataTable.Columns.Count];
+ for (int i = 0; i < dataTable.Columns.Count; i++)
+ {
+ values[i] = reader.IsDBNull(i) ? DBNull.Value : reader.GetValue(i);
+ }
+ dataTable.Rows.Add(values);
+ }
+ }
+ else
+ {
+ // 如果没有 Schema (例如 "SELECT 1" 这种标量查询)
+ // 需要手动创建列
+ for (int i = 0; i < reader.FieldCount; i++)
+ {
+ dataTable.Columns.Add("Column" + i);
+ }
+ while (reader.Read())
+ {
+ object[] values = new object[reader.FieldCount];
+ reader.GetValues(values);
+ dataTable.Rows.Add(values);
+ }
+ }
+
+ ds.Tables.Add(dataTable);
+ } while (reader.NextResult()); // 处理多个结果集
+
+ return ds;
+ }
+
+ #endregion
+
+
+ #region 执行SQL (Reader & Scalar)
+
+ public SqliteDataReader? ExecuteReader(string sql, SqliteParameter[]? paramArr)
+ {
+ if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
+ EnsureConnection();
+
+ // 注意:using 语句会立即释放 Reader,导致无法读取
+ // 这里的 RWL 锁在 using 外部处理,或者调用方自己处理锁
+ // 这里简化逻辑,直接返回 Reader,调用方需自行管理连接状态
+ var cmd = new SqliteCommand(sql, Connection);
+
+ if (paramArr != null)
+ {
+ cmd.Parameters.AddRange(paramArr);
+ }
+
+ // CommandBehavior.CloseConnection 确保 Reader 关闭时连接也关闭
+ // 但这里我们管理连接,所以不加
+ try
+ {
+ // 注意:不要在这里 Dispose Command,否则 Reader 会失效
+ // 返回 Reader,由调用方在读取完毕后关闭
+ return cmd.ExecuteReader();
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine("[SQLITE_ExecuteReader:err]" + ex.ToString());
+ cmd?.Dispose();
+ return null;
+ }
+ }
+
+ public DataSet? ExecuteDataSet(string sql, SqliteParameter[]? paramArr)
+ {
+ var dat = ExecuteReader(sql, paramArr);
+ if (dat == null)
+ {
+ return null;
+ }
+ return ReaderToDataSet(dat);
+ }
+
+ // ExecuteScalar
+ public object? ExecuteScalar(string sql, SqliteParameter[]? paramArr)
+ {
+ if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
+ EnsureConnection();
+
+ using (RWL[lockName].Read())
+ {
+ using (var cmd = new SqliteCommand(sql, Connection))
+ {
+ if (paramArr != null)
+ {
+ cmd.Parameters.AddRange(paramArr);
+ }
+ try
+ {
+ return cmd.ExecuteScalar();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[SQLITE_ExecuteScalar:err]" + ex.ToString());
+ return null;
+ }
+ }
+ }
+ }
+
+ // QueryOne (返回字典或对象)
+ public Dictionary? QueryOne(string table, string conditionCol, object conditionVal)
+ {
+ if (string.IsNullOrEmpty(table)) return null;
+ EnsureConnection();
+
+ string sql = $"SELECT * FROM [{table}]"; // 使用 [] 防止关键字冲突
+ var parameters = new List();
+
+ if (!string.IsNullOrEmpty(conditionCol))
+ {
+ sql += $" WHERE [{conditionCol}] = @{conditionCol}";
+ parameters.Add(new SqliteParameter($"@{conditionCol}", conditionVal));
+ }
+
+ using (var cmd = new SqliteCommand(sql, Connection))
+ {
+ cmd.Parameters.AddRange(parameters.ToArray());
+ using (var reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ var dict = new Dictionary();
+ for (int i = 0; i < reader.FieldCount; i++)
+ {
+ var value = reader.GetValue(i);
+ if (value == DBNull.Value) value = null;
+ dict[reader.GetName(i)] = value;
+ }
+ return dict;
+ }
+ }
+ }
+ return null;
+ }
+ #endregion
+
+ #region 增 删 改
+ // 注意:参数前缀统一为 @
+
+ public int InsertData(string table, Dictionary entity)
+ {
+ if (string.IsNullOrEmpty(table)) throw new ArgumentNullException(nameof(table));
+ EnsureConnection();
+
+ string sql = BuildInsert(table, entity);
+ var parameters = BuildParamArray(entity);
+ return ExecuteNonQuery(sql, parameters);
+ }
+
+ public int Update(string table, Dictionary entity, string where, SqliteParameter[]? whereParams)
+ {
+ if (string.IsNullOrEmpty(table)) throw new ArgumentNullException(nameof(table));
+ EnsureConnection();
+
+ string sql = BuildUpdate(table, entity);
+ var parameters = BuildParamArray(entity);
+
+ if (!string.IsNullOrEmpty(where))
+ {
+ sql += " WHERE " + where;
+ if (whereParams != null)
+ {
+ var combined = new SqliteParameter[parameters.Length + whereParams.Length];
+ parameters.CopyTo(combined, 0);
+ whereParams.CopyTo(combined, parameters.Length);
+ parameters = combined;
+ }
+ }
+
+ return ExecuteNonQuery(sql, parameters);
+ }
+
+ public int Delete(string table, string where, SqliteParameter[]? whereParams)
+ {
+ if (string.IsNullOrEmpty(table)) return 0;
+ EnsureConnection();
+
+ string sql = $"DELETE FROM [{table}]";
+ if (!string.IsNullOrEmpty(where))
+ {
+ sql += " WHERE " + where;
+ }
+
+ return ExecuteNonQuery(sql, whereParams);
+ }
+
+ public int ExecuteNonQuery(string sql, SqliteParameter[]? paramArr)
+ {
+ if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
+ EnsureConnection();
+
+ using (RWL[lockName].Read()) // 注意:写操作通常建议用 Write 锁
+ {
+ using (var cmd = new SqliteCommand(sql, Connection))
+ {
+ if (paramArr != null)
+ {
+ cmd.Parameters.AddRange(paramArr);
+ }
+ try
+ {
+ return cmd.ExecuteNonQuery();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[SQLITE_ExecuteScalar:err]" + ex.ToString());
+ return 0;
+ }
+ }
+ }
+ }
+
+ // 构建 SQL 和 参数的方法保持不变,仅泛型类型变更
+ private SqliteParameter[] BuildParamArray(Dictionary entity)
+ {
+ var list = new List();
+ foreach (string key in entity.Keys)
+ {
+ // 注意:参数名必须包含 @ 前缀
+ list.Add(new SqliteParameter($"@{key}", entity[key]));
+ }
+ return list.ToArray();
+ }
+
+ private string BuildInsert(string table, Dictionary entity)
+ {
+ var buf = new StringBuilder();
+ buf.Append("INSERT INTO ").Append(table).Append(" (");
+ var cols = new List();
+ var vals = new List();
+ foreach (string key in entity.Keys)
+ {
+ cols.Add(key);
+ vals.Add($"@{key}");
+ }
+ buf.Append(string.Join(",", cols)).Append(") VALUES (");
+ buf.Append(string.Join(",", vals)).Append(")");
+ return buf.ToString();
+ }
+
+ private string BuildUpdate(string table, Dictionary entity)
+ {
+ var buf = new StringBuilder();
+ buf.Append("UPDATE ").Append(table).Append(" SET ");
+ var sets = new List();
+ foreach (string key in entity.Keys)
+ {
+ sets.Add($"{key} = @{key}");
+ }
+ buf.Append(string.Join(",", sets));
+ return buf.ToString();
+ }
+
+ // 辅助方法:DataRow 转 Dictionary (由于没有 DataTable,通常直接从 Reader 转)
+ // 这里保留原方法签名,但实际使用中可能需要调整
+ public Dictionary DataTableToDictionary(DataTable dataTable)
+ {
+ var result = new Dictionary();
+ if (dataTable.Rows.Count > 0)
+ {
+ var row = dataTable.Rows[0];
+ foreach (DataColumn column in dataTable.Columns)
+ {
+ var value = row[column];
+ if (value == DBNull.Value) value = null;
+ result[column.ColumnName] = value;
+ }
+ }
+ return result;
+ }
+ #endregion
+ }
+}
diff --git a/UserClass/TextWriter.cs b/UserClass/TextWriter.cs
new file mode 100644
index 0000000..13c8148
--- /dev/null
+++ b/UserClass/TextWriter.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Windows; // WPF 的核心命名空间
+using System.Windows.Controls; // WPF TextBox 所在的命名空间
+using System.Windows.Threading; // 用于 Dispatcher
+
+namespace SunlightAggregationManager.UserClass
+{
+ public class ControlWriter : TextWriter
+ {
+ private readonly TextBox _textbox;
+
+ public ControlWriter(TextBox textbox)
+ {
+ _textbox = textbox;
+ }
+
+ public override void Write(char value) => AppendText(DateTime.Now + ": " + value.ToString());
+ public override void Write(string? value) => AppendText(DateTime.Now + ": " + value);
+ public override void WriteLine(string? value) => AppendText(DateTime.Now + ": "+value + Environment.NewLine);
+
+ private void AppendText(string text)
+ {
+ if (!_textbox.Dispatcher.CheckAccess())
+ {
+ _textbox.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => AppendText(text)));
+ return;
+ }
+ _textbox.AppendText(text);
+ _textbox.ScrollToEnd();
+ }
+
+ public override Encoding Encoding => Encoding.UTF8;
+ }
+
+ //全局日志管理器
+ public static class GlobalLogManager
+ {
+ private static ControlWriter? _currentWriter;
+
+ // 【核心功能】注册目标 TextBox
+ // 子页面加载时调用这个方法
+ public static void RegisterTarget(TextBox logBox)
+ {
+ // 创建新的 Writer
+ _currentWriter = new ControlWriter(logBox);
+
+ // 【关键】将 Console 的输出重定向到这个 Writer
+ // 这样 Console.WriteLine("...") 就会自动写入到 logBox 中
+ Console.SetOut(_currentWriter);
+
+ Console.WriteLine("Start");
+ }
+
+ //提供一个直接写入的方法(如果不使用 Console.WriteLine)
+ public static void Write(string message)
+ {
+ _currentWriter?.WriteLine(message);
+ }
+ }
+}
diff --git a/View/SettingPage.xaml b/View/SettingPage.xaml
new file mode 100644
index 0000000..818835f
--- /dev/null
+++ b/View/SettingPage.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/View/SettingPage.xaml.cs b/View/SettingPage.xaml.cs
new file mode 100644
index 0000000..bb1d8c0
--- /dev/null
+++ b/View/SettingPage.xaml.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+//using System.Data.SqlClient;
+using Microsoft.Data.SqlClient;
+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;
+
+namespace SunlightAggregationManager.View
+{
+ ///
+ /// SettingPage.xaml 的交互逻辑
+ ///
+ public partial class SettingPage : UserControl
+ {
+ //调用配置文件
+ private readonly UserClass.IniFile.IniFiles Configini = new UserClass.IniFile.IniFiles(Convert.ToString(System.AppDomain.CurrentDomain.BaseDirectory) + "Configini.ini");
+
+ public SettingPage()
+ {
+ InitializeComponent();
+
+ string[] Language = { "en-US", "zh-CN", "zh-TW" };
+ string[] SQMOD = { "Windows Authentication", "SQL SERVER" };
+ comboBoxLanguage.ItemsSource = Language;
+ TEXT_SQMOD.ItemsSource = SQMOD;
+ try
+ {
+ comboBoxLanguage.Text = Configini.IniReadvalue("SYS", "Language");
+ TEXT_SQLIP.Text = Configini.IniReadvalue("SQL_SERVER", "SQL1"); //读配置文件
+ TEXT_SQLNAME.Text = Configini.IniReadvalue("SQL_SERVER", "SQL2");
+ TEXT_SQMOD.Text = Configini.IniReadvalue("SQL_SERVER", "SQL3");
+ TEXT_SQLUSER.Text = Configini.IniReadvalue("SQL_SERVER", "SQL4");
+ TEXT_SQLPASWORD.Text = Configini.IniReadvalue("SQL_SERVER", "SQL5");
+ }
+ catch (Exception) { }
+ }
+
+ private async void test_Click(object sender, RoutedEventArgs e)
+ {
+ // UI 更新:显示正在测试
+ textlog.Text = "TEST";
+ // 建议使用静态缓存的画笔,避免频繁创建对象导致内存抖动
+ textlog.Foreground = Brushes.Blue;
+ try
+ {
+ // 构建连接字符串
+ var builder = new SqlConnectionStringBuilder
+ {
+ DataSource = TEXT_SQLIP.Text,
+ InitialCatalog = TEXT_SQLNAME.Text,
+ IntegratedSecurity = TEXT_SQMOD.Text == "Windows Authentication",
+ TrustServerCertificate = true
+ };
+
+ if (!builder.IntegratedSecurity)
+ {
+ builder.UserID = TEXT_SQLUSER.Text;
+ builder.Password = TEXT_SQLPASWORD.Text;
+ }
+
+ // 异步连接数据库
+ await using var conn_SC = new SqlConnection(builder.ConnectionString);
+
+ // 使用 OpenAsync 代替 Open,避免卡死界面
+ await conn_SC.OpenAsync();
+ using (var cmd = new SqlCommand("SELECT 1", conn_SC))
+ {
+ cmd.ExecuteReader();
+ }
+ }
+ catch (Exception)
+ {
+ // 失败处理
+ textlog.Text = "Link timeout";
+ textlog.Foreground = Brushes.Red;
+ return; // 直接返回,不执行后续代码
+ }
+
+ // 成功处理
+ textlog.Text = "Link succeed";
+ textlog.Foreground = Brushes.Green;
+
+ Console.WriteLine("IP = "+TEXT_SQLIP.Text);
+ Console.WriteLine("DataBase = " + TEXT_SQLNAME.Text);
+ Console.WriteLine("Link succeed");
+
+ // 弹出对话框本身会阻塞 UI 线程
+ MessageBoxResult SqlShow = System.Windows.MessageBox.Show(
+ "Whether the connection is successfully saved",
+ "SQL",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Information);
+
+ if (SqlShow == MessageBoxResult.Yes)
+ {
+ Configini.IniWritevalue("SQL_SERVER", "SQL1", TEXT_SQLIP.Text);
+ Configini.IniWritevalue("SQL_SERVER", "SQL2", TEXT_SQLNAME.Text);
+ Configini.IniWritevalue("SQL_SERVER", "SQL3", TEXT_SQMOD.SelectedValue?.ToString());
+ Configini.IniWritevalue("SQL_SERVER", "SQL4", TEXT_SQLUSER.Text);
+ Configini.IniWritevalue("SQL_SERVER", "SQL5", TEXT_SQLPASWORD.Text);
+
+ Console.WriteLine("Save database settings");
+ }
+ }
+
+ private void save_Click(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("SYS", "Language", comboBoxLanguage.SelectedValue?.ToString());
+ Configini.IniWritevalue("SQL_SERVER", "SQL1", TEXT_SQLIP.Text);
+ Configini.IniWritevalue("SQL_SERVER", "SQL2", TEXT_SQLNAME.Text);
+ Configini.IniWritevalue("SQL_SERVER", "SQL3", TEXT_SQMOD.SelectedValue?.ToString());
+ Configini.IniWritevalue("SQL_SERVER", "SQL4", TEXT_SQLUSER.Text);
+ Configini.IniWritevalue("SQL_SERVER", "SQL5", TEXT_SQLPASWORD.Text);
+
+ Console.WriteLine("Save global settings");
+ }
+
+ //是否启用tcp
+ private void EnableTCP_Checked(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("NETWORK", "TCP", "1");
+ }
+
+ private void EnableTCP_Unchecked(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("NETWORK", "TCP", "0");
+ }
+
+ private void EnableHTTP_Checked(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("NETWORK", "HTTP", "1");
+ }
+
+ private void EnableHTTP_Unchecked(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("NETWORK", "HTTP", "0");
+ }
+
+ private void EnableTLS_Checked(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("NETWORK", "TLS", "1");
+ }
+
+ private void EnableTLS_Unchecked(object sender, RoutedEventArgs e)
+ {
+ Configini.IniWritevalue("NETWORK", "TLS", "0");
+ }
+ }
+}
diff --git a/View/StatePage.xaml b/View/StatePage.xaml
new file mode 100644
index 0000000..568aacc
--- /dev/null
+++ b/View/StatePage.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/View/StatePage.xaml.cs b/View/StatePage.xaml.cs
new file mode 100644
index 0000000..df82657
--- /dev/null
+++ b/View/StatePage.xaml.cs
@@ -0,0 +1,33 @@
+using Net.Codecrete.QrCodeGenerator;
+using SunlightAggregationManager.UserClass;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+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 static System.Net.WebRequestMethods;
+
+namespace SunlightAggregationManager.View
+{
+ ///
+ /// StatePage.xaml 的交互逻辑
+ ///
+ public partial class StatePage : UserControl
+ {
+ public StatePage()
+ {
+ InitializeComponent();
+ }
+
+ }
+
+}
diff --git a/View/UserPage.xaml b/View/UserPage.xaml
new file mode 100644
index 0000000..e422dfb
--- /dev/null
+++ b/View/UserPage.xaml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/View/UserPage.xaml.cs b/View/UserPage.xaml.cs
new file mode 100644
index 0000000..debce54
--- /dev/null
+++ b/View/UserPage.xaml.cs
@@ -0,0 +1,90 @@
+using SunlightAggregationManager.UserClass;
+using SunlightAggregationManager.ViewModel;
+using System;
+using System.Collections.Generic;
+using System.Data;
+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 System.Xml.Linq;
+using TouchSocket.Core;
+using TouchSocket.Sockets;
+
+namespace SunlightAggregationManager.View
+{
+ ///
+ /// UserPage.xaml 的交互逻辑
+ ///
+ public partial class UserPage : UserControl
+ {
+ public UserPage()
+ {
+ InitializeComponent();
+ }
+
+ private string user_id = "";
+
+ private void DataGridmachinesdata_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ int rownum = DataGridmachinesdata.SelectedIndex;//获取鼠标选中行并定义变量
+ if (rownum != -1)//判断鼠标定位是否有效
+ {
+ var row_ = DataGridmachinesdata.Columns[0].GetCellContent(DataGridmachinesdata.Items[rownum]);
+ if (row_ is TextBlock textBlock)
+ {
+ user_id = textBlock.Text;//定位第0列,
+ }
+
+
+
+ }
+
+ }
+
+ private void MenuItem_Information(object sender, RoutedEventArgs e)//信息
+ {
+
+ }
+
+ private async void MenuItem_Offine(object sender, RoutedEventArgs e)//下线
+ {
+ var _state = MainWindowViewModel.Selet_Memory(MainWindowViewModel._userData, "State", "User='" + user_id + "'");
+ var _user = MainWindowViewModel.Selet_Memory(MainWindowViewModel._userData, "LinkID", "User='" + user_id +"'");
+
+ if ((_user != null)&&(_state?.ToString()=="20"))
+ {
+ foreach (var item in AsyncTcpServer.service.Clients)
+ {
+ if (item.Id == _user.ToString())
+ {
+ await item.CloseAsync();
+
+ //在断开连接后,释放对象。
+ item.Dispose();
+ }
+ }
+ }
+ }
+
+ private void MenuItem_Enable(object sender, RoutedEventArgs e)//启用账号
+ {
+ MainWindowViewModel.Updata_Memory(MainWindowViewModel._userData, "User='" + user_id + "'", "0", "State");
+ }
+
+ private void MenuItem_Disable(object sender, RoutedEventArgs e)//停用账号
+ {
+ MainWindowViewModel.Updata_Memory(MainWindowViewModel._userData, "User='" + user_id + "'", "99", "State");
+ }
+
+
+
+
+ }
+}
diff --git a/View/imgQR.xaml b/View/imgQR.xaml
new file mode 100644
index 0000000..30af8e7
--- /dev/null
+++ b/View/imgQR.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/View/imgQR.xaml.cs b/View/imgQR.xaml.cs
new file mode 100644
index 0000000..91f077c
--- /dev/null
+++ b/View/imgQR.xaml.cs
@@ -0,0 +1,105 @@
+using Net.Codecrete.QrCodeGenerator;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+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.Media.TextFormatting;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using static System.Net.WebRequestMethods;
+
+namespace SunlightAggregationManager.View
+{
+ ///
+ /// StatePage.xaml 的交互逻辑
+ ///
+ public partial class imgQR : UserControl
+ {
+ //调用配置文件
+ private readonly UserClass.IniFile.IniFiles Configini = new UserClass.IniFile.IniFiles(Convert.ToString(System.AppDomain.CurrentDomain.BaseDirectory) + "Configini.ini");
+
+
+ public imgQR()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// 生成二维码
+ /// 调用示例: imgQR_CREATE("TEST", imgQR);;
+ ///
+ /// [字符串]
+ /// Image控件
+ void imgQR_CREATE(string DAT, System.Windows.Controls.Image image)
+ {
+ // 生成二维码对象
+ QrCode qr = QrCode.EncodeText(DAT, QrCode.Ecc.Medium);
+
+ // 手动绘制 Bitmap (不使用扩展方法)
+ int scale = 10; // 每个点的像素大小
+ int border = 4; // 边距
+ int size = (qr.Size + border * 2) * scale;
+ // 创建位图
+ using (Bitmap bmp = new Bitmap(size, size))
+ {
+ using (Graphics gfx = Graphics.FromImage(bmp))
+ {
+ // 填充背景(白色)
+ gfx.Clear(System.Drawing.Color.White);
+
+ // 绘制二维码点阵(黑色)
+ using (System.Drawing.Brush brush = new SolidBrush(System.Drawing.Color.Black))
+ {
+ for (int y = 0; y < qr.Size; y++)
+ {
+ for (int x = 0; x < qr.Size; x++)
+ {
+ if (qr.GetModule(x, y))
+ {
+ System.Drawing.Rectangle rect = new System.Drawing.Rectangle(
+ (x + border) * scale,
+ (y + border) * scale,
+ scale, scale);
+ gfx.FillRectangle(brush, rect);
+ }
+ }
+ }
+ }
+ }
+
+ // 转换为 WPF 的 BitmapImage 并显示
+ using (MemoryStream ms = new MemoryStream())
+ {
+ bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
+ ms.Position = 0;
+
+ BitmapImage bitmapImage = new BitmapImage();
+ bitmapImage.BeginInit();
+ bitmapImage.StreamSource = ms;
+ bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
+ bitmapImage.EndInit();
+
+ // 赋值给 Image 控件 (假设控件名为 imgQR)
+ image.Source = bitmapImage;
+ }
+ }
+ }
+
+ private void imgQR_Loaded(object sender, RoutedEventArgs e)
+ {
+ string text_APP = "TEST"; // 要生成二维码的内容
+ string text_LINK = Configini.IniReadvalue("SYS", "LINK"); // 要生成二维码的内容
+
+ imgQR_CREATE(text_APP, imgQR_app);
+ imgQR_CREATE(text_LINK, imgQR_link);
+ }
+ }
+}
diff --git a/View/logPage.xaml b/View/logPage.xaml
new file mode 100644
index 0000000..7611b92
--- /dev/null
+++ b/View/logPage.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/View/logPage.xaml.cs b/View/logPage.xaml.cs
new file mode 100644
index 0000000..80dfeea
--- /dev/null
+++ b/View/logPage.xaml.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+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;
+
+namespace SunlightAggregationManager.View
+{
+ ///
+ /// logPage.xaml 的交互逻辑
+ ///
+ public partial class logPage : UserControl
+ {
+ public logPage()
+ {
+ InitializeComponent();
+ }
+
+ private readonly string Log_time = DateTime.Now.ToString("yyyy-MM-dd");
+ DataTable logdataTable = new DataTable(); //建立缓存
+ long TEXT_L;
+
+ private void logPage_Loaded(object sender, RoutedEventArgs e)//页面打开事件
+ {
+ comboBox_log.ItemsSource = new string[3] { "Log", "ERR", "Exchange" };
+ comboBox_log.Text = "Log";
+
+ logdataTable.Columns.Add("Name", typeof(string));
+ logdataTable.Columns.Add("Length", typeof(int));
+ logdataTable.Columns.Add("CreationTimeUtc", typeof(string));
+ logdataTable.Columns.Add("LastWriteTimeUtc", typeof(string));
+
+ DirectoryInfo loginfo = new DirectoryInfo(System.Environment.CurrentDirectory + "\\Log"); //new文件夹
+ logdataTable.Clear();
+ foreach (var item in loginfo.GetFiles())
+ {
+ DataRow FileRow = logdataTable.NewRow();
+ FileRow["Name"] = item.Name;
+ FileRow["Length"] = item.Length / 1024;
+ FileRow["CreationTimeUtc"] = item.CreationTimeUtc;
+ FileRow["LastWriteTimeUtc"] = item.LastWriteTimeUtc;
+ logdataTable.Rows.Add(FileRow);
+ }
+ logdataTable.DefaultView.Sort = "Name DESC";
+ gridLog.ItemsSource = logdataTable.DefaultView.ToTable().DefaultView;
+ }
+
+ private void LOG_file()//建立列表
+ {
+ DirectoryInfo loginfo = new DirectoryInfo(System.Environment.CurrentDirectory + "\\" + comboBox_log.Text); //new文件夹
+ logdataTable.Clear();
+ foreach (var item in loginfo.GetFiles())
+ {
+ DataRow FileRow = logdataTable.NewRow();
+ FileRow["Name"] = item.Name;
+ FileRow["Length"] = item.Length / 1024;
+ FileRow["CreationTimeUtc"] = item.CreationTimeUtc;
+ FileRow["LastWriteTimeUtc"] = item.LastWriteTimeUtc;
+ logdataTable.Rows.Add(FileRow);
+ }
+ logdataTable.DefaultView.Sort = "Name DESC";
+ gridLog.ItemsSource = logdataTable.DefaultView.ToTable().DefaultView;
+ }
+
+ private void DataGrid_gridLogClick(object sender, MouseButtonEventArgs e)//数据表双击事件
+ {
+ int rownum = gridLog.SelectedIndex;//获取鼠标选中行并定义变量
+ if (rownum != -1)//判断鼠标定位是否有效
+ {
+ /*定位选中行及指定列单元格文本信息*/
+ //获取单元格内容
+ var cellContent = gridLog.Columns[0].GetCellContent(gridLog.Items[rownum]);
+ //安全转换为 TextBlock 并获取文本
+ var textValue = (cellContent as TextBlock)?.Text;
+ if (!string.IsNullOrEmpty(textValue))
+ {
+ LOGDATA_file(textValue);//
+ }
+
+ var Content_L = gridLog.Columns[1].GetCellContent(gridLog.Items[rownum]);
+ var Value_L = (Content_L as TextBlock)?.Text.ToString();
+ if (!string.IsNullOrEmpty(Value_L))
+ {
+ TEXT_L = long.Parse(Value_L) * 1024 + 1024;
+ }
+ }
+ }
+
+ ///
+ /// 读取文件显示到前端
+ /// 调用示例: LOGDATA_file(file path);
+ ///
+ /// 文件路径
+ private async void LOGDATA_file(string dat) //读取文件显示到前端
+ {
+ Logtext.SelectAll();
+ Logtext.Cut();
+ Logtext.Document = new FlowDocument();
+ string filePath = System.Environment.CurrentDirectory + "\\" + comboBox_log.Text + "\\";
+ try
+ {
+ Logtext.AppendText("Loading...");
+ string log_ = "";
+ string log_DAT = await Task.Run(() =>
+ {
+ // 使用StreamReader读取文件
+ using (StreamReader reader = new StreamReader(filePath + dat))
+ {
+ // 读取文件直到文件的末尾
+ while (!reader.EndOfStream)
+ {
+ // 添加文件的每一行到RichTextBox
+ log_ = log_ + reader.ReadLine() + "\n";
+ }
+ }
+ return log_;
+ });
+ Logtext.SelectAll();
+ Logtext.Cut();
+ Logtext.AppendText(log_DAT);
+ }
+ catch (Exception ex)
+ {
+ // 处理可能发生的任何异常
+ MessageBox.Show("Error reading file: " + ex.Message);
+ }
+ }
+
+ private void comboBox_log_DropDownClosed(object sender, EventArgs e)
+ {
+ LOG_file();
+ }
+ }
+}
diff --git a/ViewModel/MainWindowViewModel.cs b/ViewModel/MainWindowViewModel.cs
new file mode 100644
index 0000000..75d3d5d
--- /dev/null
+++ b/ViewModel/MainWindowViewModel.cs
@@ -0,0 +1,231 @@
+using CommunityToolkit.Mvvm;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using SunlightAggregationManager.UserClass;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Net;
+using System.Reflection.PortableExecutable;
+using System.Security.Cryptography;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Xml.Linq;
+
+namespace SunlightAggregationManager.ViewModel
+{
+ public partial class MainWindowViewModel : ObservableObject
+ {
+ //调用配置文件
+ private static UserClass.IniFile.IniFiles Configini = new UserClass.IniFile.IniFiles(Convert.ToString(System.AppDomain.CurrentDomain.BaseDirectory) + "Configini.ini");
+ public static string SQLIP=""; //读配置文件
+ public static string SQLNAME="";
+ public static string SQMOD= "";
+ public static string SQLUSER = "";
+ public static string SQLPASWORD = "";
+ public static string MachineName = "SERVER";
+ public static string TCP_E = "0";
+ public static string HTTP_E = "0";
+ public static string TLS_E = "0";
+ public static UserClass.SqliteHelper SQLiteHelpers = null!; //定义数据库
+ private readonly string DBAddress = Environment.CurrentDirectory + "\\DataBase\\SunlightAggregationManager.db"; //数据库路径
+
+ public static DataTable ActionLog = new DataTable();
+
+ [ObservableProperty]
+ public static DataTable _machines = new DataTable(); //设备表缓存
+ [ObservableProperty]
+ public static DataTable _userData = new DataTable();
+
+
+ public MainWindowViewModel()
+ {
+ SQLIP = Configini.IniReadvalue("SQL_SERVER", "SQL1"); //读配置文件
+ SQLNAME = Configini.IniReadvalue("SQL_SERVER", "SQL2");
+ SQMOD = Configini.IniReadvalue("SQL_SERVER", "SQL3");
+ SQLUSER = Configini.IniReadvalue("SQL_SERVER", "SQL4");
+ SQLPASWORD = Configini.IniReadvalue("SQL_SERVER", "SQL5");
+
+ MachineName = Configini.IniReadvalue("SYS", "Name");
+
+ TCP_E = Configini.IniReadvalue("NETWORK", "TCP");
+ HTTP_E = Configini.IniReadvalue("NETWORK", "HTTP");
+ TLS_E = Configini.IniReadvalue("NETWORK", "TLS");
+
+ //本地数据库(sqlite)
+ try
+ {
+ SQLiteHelpers = new UserClass.SqliteHelper(DBAddress); //数据库连接路径
+ SQLiteHelpers.Open(); //打开数据库
+ UserData = SQLiteHelpers.ExecuteDataSet("select * from USER order by UserID asc", null)?.Tables[0] ?? new DataTable();
+ Machines = SQLiteHelpers.ExecuteDataSet("select * from Machines order by ID asc", null)?.Tables[0] ?? new DataTable();
+ ActionLog = (SQLiteHelpers.ExecuteDataSet("select * from ActionLog", null)?.Tables[0] ?? new DataTable()).Clone();
+ // SQLiteHelpers.Close();
+
+ if (UserData.Columns.Contains("UserID"))
+ {
+ UserData.Columns.Remove("UserID");
+ }
+ DataRow[] dataRows = UserData.Select("State<90");
+ foreach (DataRow row in dataRows)
+ {
+ row["State"] = 0;
+ row["LinkID"] = 0;
+ }
+
+
+ UserData.RowChanged += UserData_Updata;//注册userdata表更新事件
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.ToString());
+ }
+
+ //运行数据库
+ DataBase dataBase = new DataBase();
+ dataBase.Config(SQLIP, SQLNAME, SQMOD, SQLUSER, SQLPASWORD);
+
+ if (TCP_E == "1")//启动tcp(内网直连)
+ {
+ int P1, P2;
+ try
+ {
+ P1 =int.Parse( Configini.IniReadvalue("NETWORK", "TCP_PORT1"));
+ }
+ catch (Exception) { P1 = 7789; }
+ try
+ {
+ P2 = int.Parse(Configini.IniReadvalue("NETWORK", "TCP_PORT2"));
+ }
+ catch (Exception) { P2 = 7790; }
+
+ using var _ = AsyncTcpServer.TcpMain(P1,P2);
+ }
+ }
+
+
+ private void UserData_Updata(object sender, DataRowChangeEventArgs e)
+ {
+ Dictionary row_entity = new Dictionary();
+ Dictionary userlog = new Dictionary();
+
+ if (e.Action == DataRowAction.Change)
+ {
+ //修改的行回传
+ foreach (DataColumn col in UserData.Columns)
+ {
+ row_entity.Add(col.ColumnName, e.Row.Field