using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Windows; using System.Windows.Controls; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Text.Json; using System.Threading; using System.Windows.Documents; using System.Windows.Media; using ArcGIS.Desktop.Core; using ArcGIS.Desktop.Core.Geoprocessing; using LinkToolAddin.client; using LinkToolAddin.common; using LinkToolAddin.host; using LinkToolAddin.host.llm; using LinkToolAddin.host.llm.entity; using LinkToolAddin.message; using LinkToolAddin.resource; using LinkToolAddin.server; using log4net; using log4net.Appender; using log4net.Config; using log4net.Layout; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol.Types; using ModelContextProtocol.Server; using Newtonsoft.Json; namespace LinkToolAddin.ui.dockpane { public class ItemModel { public string Content { get; set; } } /// /// Interaction logic for DialogDockpaneView.xaml /// public partial class DialogDockpaneView : UserControl { private static ILog log = LogManager.GetLogger(typeof(DialogDockpaneView)); private List idList = new List(); private ConcurrentDictionary messageDict = new ConcurrentDictionary(); private ConcurrentDictionary borderItemsDict = new ConcurrentDictionary(); public DialogDockpaneView() { InitLogger(); InitializeComponent(); DataContext = this; } private async void TestServer_OnClick(object sender, RoutedEventArgs e) { log.Info("TestServer Clicked"); } protected void InitLogger() { // 1. 创建控制台输出器(Appender) var consoleAppender = new ConsoleAppender { Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline"), Threshold = log4net.Core.Level.Info // 仅输出 Info 及以上级别 }; consoleAppender.ActivateOptions(); // 激活配置 // 2. 创建文件滚动输出器(按大小滚动) var fileAppender = new RollingFileAppender { File = Path.Combine("Logs", "D:\\linktool_app.log"), // 日志文件路径 AppendToFile = true, // 追加模式 RollingStyle = RollingFileAppender.RollingMode.Size, // 按文件大小滚动 MaxSizeRollBackups = 10, // 保留 10 个历史文件 MaximumFileSize = "1MB", // 单个文件最大 1MB StaticLogFileName = true, // 固定文件名(否则自动追加序号) Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline"), Threshold = log4net.Core.Level.Info // 仅输出 Info 及以上级别 }; fileAppender.ActivateOptions(); // 激活配置 // 3. 直接通过 BasicConfigurator 注册 Appender BasicConfigurator.Configure(consoleAppender, fileAppender); log = LogManager.GetLogger(typeof(DialogDockpaneView)); // 测试日志输出 log.Debug("Debug 日志(控制台可见)"); log.Info("Info 日志(控制台和文件可见)"); log.Error("Error 日志(严重问题)"); } private void SendButton_OnClick(object sender, RoutedEventArgs e) { string question = QuestionTextbox.Text; string defaultGdbPath = Project.Current.DefaultGeodatabasePath; string gdbPath = @""; long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); MessageListItem userMsg = new ChatMessageItem() { content = question, role = "user", type = MessageType.CHAT_MESSAGE, id = timestamp.ToString() }; Border userMsgBoder = GetUserChatBorder(userMsg); idList.Add(timestamp.ToString()); messageDict[timestamp.ToString()] = userMsg; QuestionTextbox.Text = ""; borderItemsDict[timestamp.ToString()] = userMsgBoder; ChatHistoryStackPanel.Children.Add(userMsgBoder); Gateway.SendMessageStream(question,"qwen-max",defaultGdbPath,NewMessage_Recall); } public void NewMessage_Recall(MessageListItem msg) { string msgId = msg.id; log.Info(msg.content); if (!idList.Contains(msgId)) { //不存在该消息,需添加到ListView中 if (msg.content == "") { return; } idList.Add(msgId); messageDict[msgId] = msg; if (msg.role == "user") { if (msg.type == MessageType.TOOL_MESSAGE) { Border border = GetToolChatBorder(msg); borderItemsDict[msgId] = border; ChatHistoryStackPanel.Children.Add(border); }else if (msg.type == MessageType.CHAT_MESSAGE) { Border border = GetUserChatBorder(msg); borderItemsDict[msgId] = border; ChatHistoryStackPanel.Children.Add(border); } } else { Border border = GetAiChatBorder(msg); borderItemsDict[msgId] = border; ChatHistoryStackPanel.Children.Add(border); } } else { //已有该消息,只需要修改内容 messageDict[msgId] = msg; if (msg.role == "user") { if (msg.type == MessageType.TOOL_MESSAGE) { Border borderItem = borderItemsDict[msgId]; Grid grid = borderItem.Child as Grid; TextBlock textBlock = grid.Children[1] as TextBlock; textBlock.Text = (msg as ToolMessageItem).toolName; }else if (msg.type == MessageType.CHAT_MESSAGE) { Border borderItem = borderItemsDict[msgId]; Grid grid = borderItem.Child as Grid; TextBox textBox = grid.Children[1] as TextBox; textBox.Text = msg.content; } } else { Border borderItem = borderItemsDict[msgId]; Grid grid = borderItem.Child as Grid; TextBox textBox = grid.Children[1] as TextBox; textBox.Text = msg.content; } } } private Border GetAiChatBorder(MessageListItem msg) { Border border = new Border(); border.Margin = new Thickness(8, 12, 8, 12); border.BorderThickness = new Thickness(0); // border.Background = Brushes.DarkSeaGreen; Grid grid = new Grid(); Image icon = new Image() { Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.linktool.png"), Stretch = Stretch.Fill, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top }; grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)}); grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)}); TextBox textBox = new TextBox(); grid.Children.Add(icon); grid.Children.Add(textBox); Grid.SetColumn(icon, 0); Grid.SetColumn(textBox, 1); textBox.Background = Brushes.Transparent; textBox.Text = msg.content; textBox.TextWrapping = TextWrapping.Wrap; border.Child = grid; return border; } private Border GetUserChatBorder(MessageListItem msg) { Border border = new Border(); border.Margin = new Thickness(8, 12, 8, 12); border.BorderThickness = new Thickness(0); // border.Background = Brushes.DarkSeaGreen; Grid grid = new Grid(); Image icon = new Image() { Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.user.png"), Stretch = Stretch.Fill, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top }; grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)}); grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)}); TextBox textBox = new TextBox(); grid.Children.Add(icon); grid.Children.Add(textBox); Grid.SetColumn(icon, 1); Grid.SetColumn(textBox, 0); textBox.Background = Brushes.Transparent; textBox.Text = msg.content; textBox.TextWrapping = TextWrapping.Wrap; border.Child = grid; return border; } private Border GetToolChatBorder(MessageListItem msg) { Border border = new Border(); border.Margin = new Thickness(24); border.Padding = new Thickness(8); border.BorderThickness = new Thickness(1); border.BorderBrush = Brushes.Gray; // border.Background = Brushes.DarkSeaGreen; Grid grid = new Grid(); Image icon = new Image() { Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.tool.png"), Stretch = Stretch.Fill, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; icon.Margin = new Thickness(0, 0, 8, 0); grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)}); grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)}); TextBlock textBlock = new TextBlock(); textBlock.Text = (msg as ToolMessageItem).toolName; textBlock.TextWrapping = TextWrapping.Wrap; grid.Children.Add(icon); grid.Children.Add(textBlock); Grid.SetColumn(icon, 0); Grid.SetColumn(textBlock, 1); border.Child = grid; return border; } private void TestButton_OnClick(object sender, RoutedEventArgs e) { long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); MessageListItem toolMessageItem = new ToolMessageItem { toolName = "toolName", toolParams = new Dictionary(), type = MessageType.TOOL_MESSAGE, status = "success", content = "JsonConvert.SerializeObject(toolResponse)", id = (timestamp + 1).ToString(), role = "user" }; NewMessage_Recall(toolMessageItem); } } }