编辑
2025-11-24
C#
00

目录

🎯 解决的核心问题
📊 传统数据查询的三大痛点
💡 我们的智能化解决方案
🔧 技术架构解析
🏗️ 核心技术栈
🧩 系统架构图
💻 核心代码实现
🤖 AI服务初始化
📊 动态图表生成插件
🗄️ 智能数据库查询
📈 实时图表渲染
完整代码
🎨 用户体验设计亮点
🗣️ 自然语言交互
📊 智能图表推荐
⚡ 性能优化技巧
🚀 异步处理优化
🔒 安全性保障
🌟 实际应用场景
🏭 工业生产监控
📊 数据可视化报告
🎯 技术总结与展望
🔥 三个核心技术亮点
🚀 扩展方向建议

看这篇文章前可以先看前面一篇,是在其基本上加了动态了livechat 的生成,在工业4.0时代,传统的SCADA(数据采集与监控系统)数据查询方式已经无法满足现代化生产管理的需求。开发者常常面临这样的困境:海量设备数据无法直观展示,复杂SQL查询让业务人员望而却步,数据分析报告制作耗时费力。

今天,我将分享一个结合了C# WPF、Semantic Kernel AI框架和LiveCharts图表库的创新解决方案,让你轻松打造一个智能化的SCADA数据助手,实现自然语言查询、智能图表生成和数据可视化分析。

🎯 解决的核心问题

📊 传统数据查询的三大痛点

  1. 查询门槛高:业务人员需要掌握复杂的SQL语法
  2. 数据展示单调:纯文本结果缺乏直观性
  3. 分析效率低:手动制作图表耗时耗力

💡 我们的智能化解决方案

  • 自然语言交互:用户可以用中文直接询问"显示所有设备类型的统计"
  • AI智能分析:自动判断查询意图并生成合适的SQL
  • 动态图表生成:根据数据特征自动推荐并生成最适合的可视化图表

🔧 技术架构解析

🏗️ 核心技术栈

  • 前端框架:WPF + LiveCharts(图表可视化)
  • AI引擎:Microsoft Semantic Kernel + OpenAI API
  • 数据库:SQLite(轻量级,易部署)
  • 设计模式:插件化架构 + 异步处理

🧩 系统架构图

Markdown
用户自然语言输入 → AI语义理解 → SQL生成执行 → 数据分析 → 图表可视化

💻 核心代码实现

🤖 AI服务初始化

C#
private void InitializeAI() { try { // 初始化Semantic Kernel var kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: "deepseek-chat", apiKey: Environment.GetEnvironmentVariable("DEEPSEEK_API_KEY") ?? "your-api-key", endpoint: new Uri("https://api.deepseek.com/v1") ); _kernel = kernelBuilder.Build(); // 注册数据库查询和图表生成插件 RegisterDatabaseQueryPlugin(_kernel); RegisterChartPlugin(_kernel); // 初始化聊天历史,设定AI助手角色 _chatHistory = new ChatHistory(); _chatHistory.AddSystemMessage(@"你是一个智能SCADA数据助手,具备数据查询、分析和可视化能力。 当用户的查询涉及数值数据、统计分析、趋势分析时,主动建议或生成图表可视化。"); _chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>(); } catch (Exception ex) { AddSystemMessage($"❌ AI初始化失败: {ex.Message}"); } }

📊 动态图表生成插件

C#
private void RegisterChartPlugin(Kernel kernel) { // 柱状图生成函数 var createBarChartFunction = kernel.CreateFunctionFromMethod( method: (string title, string data, string xLabel, string yLabel) => { try { CreateBarChart(title, data, xLabel, yLabel); return $"✅ 已生成柱状图: {title}"; } catch (Exception ex) { return $"❌ 生成柱状图失败: {ex.Message}"; } }, functionName: "CreateBarChart", description: "创建柱状图。参数:title(图表标题), data(数据,格式:标签1:值1,标签2:值2), xLabel(X轴标签), yLabel(Y轴标签)" ); // 注册所有图表类型插件 kernel.ImportPluginFromFunctions("ChartPlugin", [ createBarChartFunction, createLineChartFunction, createPieChartFunction, suggestChartFunction ]); }

🗄️ 智能数据库查询

C#
private void RegisterDatabaseQueryPlugin(Kernel kernel) { var executeQueryFunction = kernel.CreateFunctionFromMethod( method: (string sql) => { try { using var connection = new SQLiteConnection($"Data Source=test.db;Version=3;"); connection.Open(); // 安全检查:只允许SELECT查询 if (!sql.Trim().ToUpper().StartsWith("SELECT")) { return "⚠️ 出于安全考虑,只允许执行SELECT查询语句。"; } using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); var dt = new DataTable(); dt.Load(reader); var result = FormatQueryResult(dt); // 智能判断是否适合生成图表 if (ShouldSuggestChart(dt, sql)) { result += "\n\n💡 这些数据很适合用图表展示!您可以说'生成图表'来可视化这些数据。"; } return result; } catch (Exception ex) { return $"SQL执行出错: {ex.Message}"; } }, functionName: "ExecuteSQL", description: "执行SQL查询语句并返回格式化的SCADA数据结果" ); kernel.ImportPluginFromFunctions("DatabasePlugin", [executeQueryFunction]); }

📈 实时图表渲染

C#
private void CreateBarChart(string title, string data, string xLabel, string yLabel) { Dispatcher.Invoke(() => { var chart = new CartesianChart { Height = 300, Margin = new Thickness(0, 10, 0, 10) }; var series = new ColumnSeries { Title = title, Values = new ChartValues<double>() }; var labels = new List<string>(); // 解析数据格式:标签1:值1,标签2:值2 var dataItems = data.Split(','); foreach (var item in dataItems) { var parts = item.Split(':'); if (parts.Length == 2) { labels.Add(parts[0].Trim()); if (double.TryParse(parts[1].Trim(), out double value)) { series.Values.Add(value); } } } // 配置图表 chart.Series = new SeriesCollection { series }; chart.AxisX.Add(new Axis { Title = xLabel, Labels = labels }); chart.AxisY.Add(new Axis { Title = yLabel }); AddChartToPanel(title, chart); }); }

完整代码

C#
using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using LiveCharts.Wpf; using LiveCharts; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.OpenAI; using System.Data; using System.Data.SQLite; using System.IO; using LiveCharts.Definitions.Series; using LiveCharts.Wpf.Charts.Base; namespace AppAiDatabaseChart { public partial class MainWindow : Window { private Kernel _kernel; private ChatHistory _chatHistory; private IChatCompletionService _chatCompletionService; public MainWindow() { InitializeComponent(); InitializeAI(); AddSystemMessage("🤖 SCADA智能AI数据助手已启动!我可以帮您查询和分析数据库信息,并生成可视化图表。"); } /// <summary> /// 初始化AI服务 /// </summary> private void InitializeAI() { try { // 初始化Kernel var kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: "deepseek-chat", apiKey: Environment.GetEnvironmentVariable("DEEPSEEK_API_KEY") ?? "sk-xxxx", endpoint: new Uri("https://api.deepseek.com/v1") ); _kernel = kernelBuilder.Build(); // 注册数据库查询插件 RegisterDatabaseQueryPlugin(_kernel); // 注册图表生成插件 RegisterChartPlugin(_kernel); // 初始化聊天历史 _chatHistory = new ChatHistory(); _chatHistory.AddSystemMessage(@"你是一个智能SCADA数据助手,具备数据查询、分析和可视化能力。 你可以帮助用户查询设备信息、测点数据、历史数据、报警信息等,并生成相应的图表。 当用户的查询涉及数值数据、统计分析、趋势分析时,主动建议或生成图表可视化。 请根据用户需求选择合适的工具来协助用户,并提供友好、专业的回复。"); // 获取聊天完成服务 _chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>(); StatusText.Text = "AI助手已准备就绪"; } catch (Exception ex) { AddSystemMessage($"❌ AI初始化失败: {ex.Message}"); StatusText.Text = "AI初始化失败"; } } /// <summary> /// 注册图表生成插件 /// </summary> private void RegisterChartPlugin(Kernel kernel) { // 1. 生成柱状图 var createBarChartFunction = kernel.CreateFunctionFromMethod( method: (string title, string data, string xLabel, string yLabel) => { try { CreateBarChart(title, data, xLabel, yLabel); return $"✅ 已生成柱状图: {title}"; } catch (Exception ex) { return $"❌ 生成柱状图失败: {ex.Message}"; } }, functionName: "CreateBarChart", description: "创建柱状图。参数:title(图表标题), data(数据,格式:标签1:值1,标签2:值2), xLabel(X轴标签), yLabel(Y轴标签)" ); // 2. 生成折线图 var createLineChartFunction = kernel.CreateFunctionFromMethod( method: (string title, string data, string xLabel, string yLabel) => { try { CreateLineChart(title, data, xLabel, yLabel); return $"✅ 已生成折线图: {title}"; } catch (Exception ex) { return $"❌ 生成折线图失败: {ex.Message}"; } }, functionName: "CreateLineChart", description: "创建折线图。参数:title(图表标题), data(数据,格式:标签1:值1,标签2:值2), xLabel(X轴标签), yLabel(Y轴标签)" ); // 3. 生成饼图 var createPieChartFunction = kernel.CreateFunctionFromMethod( method: (string title, string data) => { try { CreatePieChart(title, data); return $"✅ 已生成饼图: {title}"; } catch (Exception ex) { return $"❌ 生成饼图失败: {ex.Message}"; } }, functionName: "CreatePieChart", description: "创建饼图。参数:title(图表标题), data(数据,格式:标签1:值1,标签2:值2)" ); // 4. 智能图表建议 var suggestChartFunction = kernel.CreateFunctionFromMethod( method: (string queryResult, string queryType) => { return SuggestChartType(queryResult, queryType); }, functionName: "SuggestChart", description: "根据查询结果建议合适的图表类型和数据格式" ); kernel.ImportPluginFromFunctions("ChartPlugin", [createBarChartFunction, createLineChartFunction, createPieChartFunction, suggestChartFunction]); } /// <summary> /// 建议图表类型 /// </summary> private string SuggestChartType(string queryResult, string queryType) { var suggestion = new StringBuilder(); suggestion.AppendLine("📊 图表建议:"); if (queryResult.Contains("COUNT") || queryResult.Contains("统计") || queryResult.Contains("数量")) { suggestion.AppendLine("- 推荐使用柱状图显示统计数据"); suggestion.AppendLine("- 可以使用饼图显示比例关系"); } if (queryResult.Contains("时间") || queryResult.Contains("Timestamp") || queryResult.Contains("趋势")) { suggestion.AppendLine("- 推荐使用折线图显示时间趋势"); } if (queryResult.Contains("类型") || queryResult.Contains("分组") || queryResult.Contains("GROUP BY")) { suggestion.AppendLine("- 推荐使用饼图显示分类分布"); } suggestion.AppendLine("\n如需生成图表,请告诉我您希望显示哪些数据!"); return suggestion.ToString(); } /// <summary> /// 创建柱状图 /// </summary> private void CreateBarChart(string title, string data, string xLabel, string yLabel) { Dispatcher.Invoke(() => { var chart = new CartesianChart { Height = 300, Margin = new Thickness(0, 10, 0, 10) }; var series = new ColumnSeries { Title = title, Values = new ChartValues<double>() }; var labels = new List<string>(); // 解析数据 (格式: 标签1:值1,标签2:值2) var dataItems = data.Split(','); foreach (var item in dataItems) { var parts = item.Split(':'); if (parts.Length == 2) { labels.Add(parts[0].Trim()); if (double.TryParse(parts[1].Trim(), out double value)) { series.Values.Add(value); } } } chart.Series = new SeriesCollection { series }; chart.AxisX.Add(new Axis { Title = xLabel, Labels = labels }); chart.AxisY.Add(new Axis { Title = yLabel }); AddChartToPanel(title, chart); }); } /// <summary> /// 创建折线图 /// </summary> private void CreateLineChart(string title, string data, string xLabel, string yLabel) { Dispatcher.Invoke(() => { var chart = new CartesianChart { Height = 300, Margin = new Thickness(0, 10, 0, 10) }; var series = new LineSeries { Title = title, Values = new ChartValues<double>(), PointGeometry = DefaultGeometries.Circle, PointGeometrySize = 8 }; var labels = new List<string>(); // 解析数据 var dataItems = data.Split(','); foreach (var item in dataItems) { var parts = item.Split(':'); if (parts.Length == 2) { labels.Add(parts[0].Trim()); if (double.TryParse(parts[1].Trim(), out double value)) { series.Values.Add(value); } } } chart.Series = new SeriesCollection { series }; chart.AxisX.Add(new Axis { Title = xLabel, Labels = labels }); chart.AxisY.Add(new Axis { Title = yLabel }); AddChartToPanel(title, chart); }); } /// <summary> /// 创建饼图 /// </summary> private void CreatePieChart(string title, string data) { Dispatcher.Invoke(() => { var chart = new PieChart { Height = 300, Margin = new Thickness(0, 10, 0, 10), LegendLocation = LegendLocation.Right }; var series = new SeriesCollection(); // 解析数据 var dataItems = data.Split(','); foreach (var item in dataItems) { var parts = item.Split(':'); if (parts.Length == 2) { var label = parts[0].Trim(); if (double.TryParse(parts[1].Trim(), out double value)) { series.Add(new PieSeries { Title = label, Values = new ChartValues<double> { value }, DataLabels = true }); } } } chart.Series = series; AddChartToPanel(title, chart); }); } /// <summary> /// 将图表添加到面板 /// </summary> private void AddChartToPanel(string title, FrameworkElement chart) { var container = new StackPanel(); // 图表标题 var titleBlock = new TextBlock { Text = $"📊 {title}", FontSize = 14, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 10), HorizontalAlignment = HorizontalAlignment.Center }; container.Children.Add(titleBlock); container.Children.Add(chart); // 添加关闭按钮 var closeButton = new Button { Content = "❌", Width = 30, Height = 30, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0, -10, 0, 0), Background = Brushes.Transparent, BorderThickness = new Thickness(0), Cursor = Cursors.Hand }; var border = new Border { Style = (Style)FindResource("ChartContainer"), Child = container }; closeButton.Click += (s, e) => ChartPanel.Children.Remove(border); container.Children.Add(closeButton); ChartPanel.Children.Add(border); // 更新图表标题 ChartTitleText.Text = $"📈 数据可视化 ({ChartPanel.Children.Count})"; } /// <summary> /// 注册数据库查询插件 /// </summary> private void RegisterDatabaseQueryPlugin(Kernel kernel) { var getSchemaFunction = kernel.CreateFunctionFromMethod( method: () => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在,请先初始化数据库。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); var schema = GetDatabaseSchema(connection); return schema; } catch (Exception ex) { return $"获取数据库结构失败: {ex.Message}"; } }, functionName: "GetDatabaseSchema", description: "获取SCADA数据库的完整结构信息,包括所有表、字段、数据类型等" ); var executeQueryFunction = kernel.CreateFunctionFromMethod( method: (string sql) => { try { var dbFilePath = "test.db"; if (!File.Exists(dbFilePath)) return "数据库文件不存在,请先初始化数据库。"; using var connection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;"); connection.Open(); if (!sql.Trim().ToUpper().StartsWith("SELECT")) { return "⚠️ 出于安全考虑,只允许执行SELECT查询语句。"; } using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); var dt = new DataTable(); dt.Load(reader); if (dt.Rows.Count == 0) return "查询结果为空。"; var result = FormatQueryResult(dt); // 检查是否适合生成图表 if (ShouldSuggestChart(dt, sql)) { result += "\n\n💡 这些数据很适合用图表展示!您可以说'生成图表'来可视化这些数据。"; } return result; } catch (Exception ex) { return $"SQL执行出错: {ex.Message}"; } }, functionName: "ExecuteSQL", description: "执行SQL查询语句并返回格式化的SCADA数据结果,如果适合会建议生成图表" ); kernel.ImportPluginFromFunctions("DatabasePlugin", [getSchemaFunction, executeQueryFunction]); } /// <summary> /// 判断是否应该建议生成图表 /// </summary> private bool ShouldSuggestChart(DataTable dt, string sql) { // 如果结果有数值列且行数适中,建议生成图表 if (dt.Rows.Count > 1 && dt.Rows.Count <= 20) { foreach (DataColumn col in dt.Columns) { if (col.DataType == typeof(int) || col.DataType == typeof(double) || col.DataType == typeof(decimal)) { return true; } } } // 如果SQL包含聚合函数,也建议生成图表 var upperSql = sql.ToUpper(); if (upperSql.Contains("COUNT") || upperSql.Contains("SUM") || upperSql.Contains("AVG") || upperSql.Contains("GROUP BY")) { return true; } return false; } // 输入框键盘事件 /// <summary> /// 输入框键盘事件 /// </summary> private void InputTextBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) { SendMessage(); e.Handled = true; } } private void InitDbButton_Click(object sender, RoutedEventArgs e) { try { InitDbButton.IsEnabled = false; StatusText.Text = "正在初始化数据库..."; AddSystemMessage("🔧 开始初始化SCADA数据库..."); Task.Run(() => { DatabaseInitializer.InitializeScadaDatabase(); Dispatcher.Invoke(() => { AddSystemMessage("✅ SCADA数据库初始化完成!已创建1000条测试记录。\n💡 您现在可以询问:\n- 显示所有设备类型的统计\n- 查询最近的报警趋势\n- 生成设备状态分布图"); StatusText.Text = "数据库初始化完成"; InitDbButton.IsEnabled = true; }); }); } catch (Exception ex) { AddSystemMessage($"❌ 数据库初始化失败: {ex.Message}"); StatusText.Text = "数据库初始化失败"; InitDbButton.IsEnabled = true; } } private void ShowStatsButton_Click(object sender, RoutedEventArgs e) { try { if (!File.Exists("test.db")) { AddSystemMessage("❌ 数据库文件不存在,请先初始化数据库。"); return; } var stats = GetDatabaseStatistics(); AddSystemMessage(stats); } catch (Exception ex) { AddSystemMessage($"❌ 获取统计信息失败: {ex.Message}"); } } private void ClearChatButton_Click(object sender, RoutedEventArgs e) { ChatPanel.Children.Clear(); ChartPanel.Children.Clear(); _chatHistory?.Clear(); _chatHistory?.AddSystemMessage(@"你是一个智能SCADA数据助手,具备数据查询、分析和可视化能力。 你可以帮助用户查询设备信息、测点数据、历史数据、报警信息等,并生成相应的图表。 当用户的查询涉及数值数据、统计分析、趋势分析时,主动建议或生成图表可视化。 请根据用户需求选择合适的工具来协助用户,并提供友好、专业的回复。"); AddSystemMessage("🗑️ 对话和图表已清空,您可以开始新的对话。"); ChartTitleText.Text = "📈 数据可视化"; } private async void SendMessage() { var userInput = InputTextBox.Text.Trim(); if (string.IsNullOrEmpty(userInput)) return; InputTextBox.Text = string.Empty; AddUserMessage(userInput); SendButton.IsEnabled = false; StatusText.Text = "AI正在思考..."; try { _chatHistory.AddUserMessage(userInput); await ProcessAIResponse(); } catch (Exception ex) { AddSystemMessage($"❌ 发生错误: {ex.Message}"); } finally { SendButton.IsEnabled = true; StatusText.Text = "准备就绪"; } } private async Task ProcessAIResponse() { var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, MaxTokens = 2000, Temperature = 0.7 }; var responseTextBlock = new TextBlock { TextWrapping = TextWrapping.Wrap, FontSize = 12, Foreground = new SolidColorBrush(Color.FromRgb(51, 51, 51)) }; var responseBorder = new Border { Style = (Style)FindResource("AssistantMessage"), Child = responseTextBlock }; ChatPanel.Children.Add(responseBorder); ScrollToBottom(); try { var streamResponse = _chatCompletionService.GetStreamingChatMessageContentsAsync( chatHistory: _chatHistory, executionSettings: executionSettings, kernel: _kernel ); string fullResponse = ""; await foreach (var content in streamResponse) { if (!string.IsNullOrEmpty(content.Content)) { fullResponse += content.Content; await Dispatcher.InvokeAsync(() => { responseTextBlock.Text = fullResponse; ScrollToBottom(); }); await Task.Delay(30); } } if (!string.IsNullOrWhiteSpace(fullResponse)) { _chatHistory.AddAssistantMessage(fullResponse); } } catch (Exception ex) { responseTextBlock.Text = $"❌ AI响应失败: {ex.Message}"; } } private void SendButton_Click(object sender, RoutedEventArgs e) { SendMessage(); } private void AddUserMessage(string message) { var textBlock = new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap, FontSize = 12, Foreground = Brushes.White }; var border = new Border { Style = (Style)FindResource("UserMessage"), Child = textBlock }; ChatPanel.Children.Add(border); ScrollToBottom(); } private void AddSystemMessage(string message) { var textBlock = new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap, FontSize = 11, Foreground = new SolidColorBrush(Color.FromRgb(133, 77, 14)) }; var border = new Border { Style = (Style)FindResource("SystemMessage"), Child = textBlock }; ChatPanel.Children.Add(border); ScrollToBottom(); } private void ScrollToBottom() { ChatScrollViewer.ScrollToEnd(); } private string GetDatabaseStatistics() { var stats = new StringBuilder(); stats.AppendLine("📊 SCADA数据库统计信息"); stats.AppendLine("========================"); using var connection = new SQLiteConnection($"Data Source=test.db;Version=3;"); connection.Open(); var tables = new[] { "Devices", "Tags", "RealTimeData", "HistoricalData", "Alarms" }; var displayNames = new[] { "设备", "测点", "实时数据", "历史数据", "报警" }; for (int i = 0; i < tables.Length; i++) { var count = GetTableRecordCount(connection, tables[i]); stats.AppendLine($"📋 {displayNames[i]}: {count} 条记录"); } return stats.ToString(); } private static string GetDatabaseSchema(SQLiteConnection connection) { var schema = new StringBuilder(); schema.AppendLine("📊 SCADA数据库结构信息"); schema.AppendLine("=" + new string('=', 50)); var tables = GetTableNames(connection); foreach (var tableName in tables) { schema.AppendLine($"\n🔹 表名: {tableName}"); schema.AppendLine($" 字段信息:"); var tableInfo = GetTableStructure(connection, tableName); foreach (var column in tableInfo) { schema.AppendLine($" - {column.Name} ({column.Type}) {(column.IsPrimaryKey ? "[主键]" : "")} {(column.IsNotNull ? "[非空]" : "")}"); } var count = GetTableRecordCount(connection, tableName); schema.AppendLine($" 📋 记录数: {count}"); } schema.AppendLine($"\n💡 图表生成示例:"); schema.AppendLine($" - 生成设备类型统计图"); schema.AppendLine($" - 显示报警级别分布饼图"); schema.AppendLine($" - 创建设备状态趋势图"); return schema.ToString(); } private static List<string> GetTableNames(SQLiteConnection connection) { var tables = new List<string>(); string sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { tables.Add(reader.GetString(0)); } return tables; } private static List<ColumnInfo> GetTableStructure(SQLiteConnection connection, string tableName) { var columns = new List<ColumnInfo>(); string sql = $"PRAGMA table_info({tableName})"; using var command = new SQLiteCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { columns.Add(new ColumnInfo { Name = reader.GetString("name"), Type = reader.GetString("type"), IsNotNull = reader.GetBoolean("notnull"), IsPrimaryKey = reader.GetBoolean("pk") }); } return columns; } private static int GetTableRecordCount(SQLiteConnection connection, string tableName) { string sql = $"SELECT COUNT(*) FROM {tableName}"; using var command = new SQLiteCommand(sql, connection); return Convert.ToInt32(command.ExecuteScalar()); } private static string FormatQueryResult(DataTable dt) { var result = new StringBuilder(); result.AppendLine($"📋 查询结果 (共 {dt.Rows.Count} 条记录):"); result.AppendLine(new string('=', 60)); foreach (DataColumn col in dt.Columns) { result.Append($"{col.ColumnName,-15} | "); } result.AppendLine(); result.AppendLine(new string('-', 60)); int maxRows = Math.Min(20, dt.Rows.Count); for (int i = 0; i < maxRows; i++) { foreach (var item in dt.Rows[i].ItemArray) { string value = item?.ToString() ?? "NULL"; if (value.Length > 12) value = value.Substring(0, 12) + "..."; result.Append($"{value,-15} | "); } result.AppendLine(); } if (dt.Rows.Count > 20) { result.AppendLine($"... 还有 {dt.Rows.Count - 20} 条记录未显示"); } return result.ToString(); } } }
XML
<Window x:Class="AppAiDatabaseChart.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" Title="SCADA智能AI数据助手" Height="700" Width="1200" WindowStartupLocation="CenterScreen" Background="#F5F5F5"> <Window.Resources> <!-- 按钮样式 --> <Style x:Key="ModernButton" TargetType="Button"> <Setter Property="Background" Value="#4A90E2"/> <Setter Property="Foreground" Value="White"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Padding" Value="10,5"/> <Setter Property="Margin" Value="5"/> <Setter Property="FontSize" Value="12"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="4"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#357ABD"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 输入框样式 --> <Style x:Key="ModernTextBox" TargetType="TextBox"> <Setter Property="BorderBrush" Value="#DDD"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Padding" Value="8"/> <Setter Property="FontSize" Value="12"/> <Setter Property="Background" Value="White"/> </Style> <!-- 聊天气泡样式 --> <Style x:Key="UserMessage" TargetType="Border"> <Setter Property="Background" Value="#4A90E2"/> <Setter Property="CornerRadius" Value="8"/> <Setter Property="Margin" Value="50,5,10,5"/> <Setter Property="Padding" Value="10"/> <Setter Property="HorizontalAlignment" Value="Right"/> </Style> <Style x:Key="AssistantMessage" TargetType="Border"> <Setter Property="Background" Value="White"/> <Setter Property="CornerRadius" Value="8"/> <Setter Property="Margin" Value="10,5,50,5"/> <Setter Property="Padding" Value="10"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="BorderBrush" Value="#DDD"/> <Setter Property="BorderThickness" Value="1"/> </Style> <Style x:Key="SystemMessage" TargetType="Border"> <Setter Property="Background" Value="#FFF3CD"/> <Setter Property="CornerRadius" Value="4"/> <Setter Property="Margin" Value="10,5"/> <Setter Property="Padding" Value="8"/> <Setter Property="BorderBrush" Value="#FFEAA7"/> <Setter Property="BorderThickness" Value="1"/> </Style> <!-- 图表容器样式 --> <Style x:Key="ChartContainer" TargetType="Border"> <Setter Property="Background" Value="White"/> <Setter Property="CornerRadius" Value="8"/> <Setter Property="Margin" Value="10,5"/> <Setter Property="Padding" Value="15"/> <Setter Property="BorderBrush" Value="#DDD"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="MinHeight" Value="300"/> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 左侧聊天区域 --> <Grid Grid.Column="0"> <Grid.RowDefinitions> <RowDefinition Height="60"/> <RowDefinition Height="*"/> <RowDefinition Height="100"/> </Grid.RowDefinitions> <!-- 顶部工具栏 --> <Border Grid.Row="0" Background="White" BorderBrush="#DDD" BorderThickness="0,0,0,1"> <Grid Margin="15,10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0" Orientation="Vertical"> <TextBlock Text="SCADA智能AI数据助手" FontSize="18" FontWeight="Bold" Foreground="#333"/> <TextBlock x:Name="StatusText" Text="准备就绪" FontSize="12" Foreground="#666"/> </StackPanel> <StackPanel Grid.Column="1" Orientation="Horizontal"> <Button x:Name="InitDbButton" Content="🔧 初始化数据库" Style="{StaticResource ModernButton}" Click="InitDbButton_Click"/> <Button x:Name="ShowStatsButton" Content="📊 数据统计" Style="{StaticResource ModernButton}" Click="ShowStatsButton_Click"/> <Button x:Name="ClearChatButton" Content="🗑️ 清空对话" Style="{StaticResource ModernButton}" Click="ClearChatButton_Click"/> </StackPanel> </Grid> </Border> <!-- 聊天区域 --> <ScrollViewer x:Name="ChatScrollViewer" Grid.Row="1" VerticalScrollBarVisibility="Auto"> <StackPanel x:Name="ChatPanel" Margin="10"/> </ScrollViewer> <!-- 输入区域 --> <Border Grid.Row="2" Background="White" BorderBrush="#DDD" BorderThickness="0,1,0,0"> <Grid Margin="15"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBox x:Name="InputTextBox" Grid.Row="0" Style="{StaticResource ModernTextBox}" TextWrapping="Wrap" AcceptsReturn="True" KeyDown="InputTextBox_KeyDown"/> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0"> <TextBlock Text="Ctrl+Enter 发送" FontSize="10" Foreground="#888" VerticalAlignment="Center" Margin="0,0,10,0"/> <Button x:Name="SendButton" Content="发送" Style="{StaticResource ModernButton}" Click="SendButton_Click" IsEnabled="True"/> </StackPanel> </Grid> </Border> </Grid> <!-- 分隔线 --> <GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" Background="#DDD"/> <!-- 右侧图表区域 --> <Grid Grid.Column="2"> <Grid.RowDefinitions> <RowDefinition Height="40"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- 图表标题 --> <Border Grid.Row="0" Background="White" BorderBrush="#DDD" BorderThickness="0,0,0,1"> <TextBlock x:Name="ChartTitleText" Text="📈 数据可视化" FontSize="14" FontWeight="Bold" Foreground="#333" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <!-- 图表显示区域 --> <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Background="#F9F9F9"> <StackPanel x:Name="ChartPanel" Margin="10"/> </ScrollViewer> </Grid> </Grid> </Window>

image.png

image.png

image.png

image.png

🎨 用户体验设计亮点

🗣️ 自然语言交互

用户可以直接输入:

  • "显示所有设备类型的统计"
  • "查询最近一周的报警趋势"
  • "生成设备状态分布饼图"

📊 智能图表推荐

系统会根据查询结果的数据特征,自动推荐最合适的图表类型:

C#
private bool ShouldSuggestChart(DataTable dt, string sql) { // 数据行数适中且包含数值列 if (dt.Rows.Count > 1 && dt.Rows.Count <= 20) { foreach (DataColumn col in dt.Columns) { if (col.DataType == typeof(int) || col.DataType == typeof(double)) return true; } } // 包含聚合函数的查询 var upperSql = sql.ToUpper(); return upperSql.Contains("COUNT") || upperSql.Contains("SUM") || upperSql.Contains("GROUP BY"); }

⚡ 性能优化技巧

🚀 异步处理优化

C#
private async Task ProcessAIResponse() { var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, MaxTokens = 2000, Temperature = 0.7 // 平衡创造性和准确性 }; // 流式响应处理,提升用户体验 var streamResponse = _chatCompletionService.GetStreamingChatMessageContentsAsync( chatHistory: _chatHistory, executionSettings: executionSettings, kernel: _kernel ); string fullResponse = ""; await foreach (var content in streamResponse) { if (!string.IsNullOrEmpty(content.Content)) { fullResponse += content.Content; // 实时更新UI await Dispatcher.InvokeAsync(() => { responseTextBlock.Text = fullResponse; ScrollToBottom(); }); await Task.Delay(30); // 控制更新频率 } } }

🔒 安全性保障

  • SQL注入防护:严格限制只允许SELECT语句
  • API密钥管理:使用环境变量存储敏感信息
  • 数据验证:对用户输入进行严格校验

🌟 实际应用场景

🏭 工业生产监控

  • 设备状态实时监控:"显示车间A所有设备的运行状态"
  • 能耗分析:"生成本月各设备的能耗对比图"
  • 故障趋势分析:"查看最近30天的设备故障统计"

📊 数据可视化报告

  • 自动报表生成:AI根据查询结果智能选择图表类型
  • 交互式图表:用户可以动态添加、删除图表
  • 多维度分析:支持柱状图、折线图、饼图等多种展示方式

🎯 技术总结与展望

🔥 三个核心技术亮点

  1. 插件化架构:通过Semantic Kernel的Function Calling机制,实现模块化扩展
  2. 流式AI响应:采用异步流处理,大幅提升用户交互体验
  3. 智能图表推荐:基于数据特征自动判断最适合的可视化方案

🚀 扩展方向建议

  • 多数据源支持:扩展至MySQL、SQL Server等企业级数据库
  • 更多图表类型:集成更复杂的统计图表和3D可视化
  • 语音交互:结合语音识别技术,实现真正的"对话式"数据查询

这套解决方案完美诠释了**"让AI为开发者服务"**的理念,通过简单的自然语言就能完成复杂的数据查询和可视化任务。在工业互联网快速发展的今天,这样的智能化工具将大大提升工程师的工作效率。

你在SCADA系统开发中遇到过哪些数据查询和可视化的挑战?欢迎在评论区分享你的经验,或者说说你最希望AI帮你解决的技术难题!


💡 觉得这篇技术分享对你有帮助吗?请转发给更多需要的同行,让我们一起推动智能化开发技术的普及!

相关信息

通过网盘分享的文件:AppAiDatabaseChart.zip 链接: https://pan.baidu.com/s/1kb5YpbKKvtgpDemInFnIsw?pwd=2b5u 提取码: 2b5u --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!