From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Plugin.BurnIn/ManualDebug/ManualDebug.xaml.cs |  722 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 722 insertions(+), 0 deletions(-)

diff --git a/Plugin.BurnIn/ManualDebug/ManualDebug.xaml.cs b/Plugin.BurnIn/ManualDebug/ManualDebug.xaml.cs
new file mode 100644
index 0000000..b518c73
--- /dev/null
+++ b/Plugin.BurnIn/ManualDebug/ManualDebug.xaml.cs
@@ -0,0 +1,722 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using HMI.Core.Util;
+using HMI.UI.DpUtil;
+using NationalInstruments.TestStand.Interop.API;
+using Plugin.BurnIn.Model;
+using Plugin.TsSupport;
+using SuperSocket.SocketBase.Config;
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Configuration;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Xml.Linq;
+
+namespace Plugin.BurnIn
+{
+    public class BtnConfig
+    {
+        public string Title { get; set; }
+        public string Name { get; set; }
+    }
+
+    // 结果失败判断转换器(支持不区分大小写)
+    public class ResultFailedConverter : IMultiValueConverter
+    {
+        public static readonly ResultFailedConverter Instance = new ResultFailedConverter();
+        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (values.Length > 0 && values[0] is string)
+            {
+                string result = (string)values[0];
+                string upper = result.ToUpperInvariant();
+                return upper == "NG" || upper == "FAIL";
+            }
+            return false;
+        }
+
+        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    // 日志级别枚举
+    public enum LogLevel
+    {
+        Debug = 0,
+        Info = 1,
+        Warn = 2,
+        Error = 3
+    }
+
+    [ObservableObject]
+    public partial class ManualDebug : UserControl
+    {
+        public static readonly DependencyProperty OvenNameProperty = DependencyProperty.Register(
+                            "OvenName",
+                            typeof(string),
+                            typeof(ManualDebug),
+                            new PropertyMetadata(""));
+
+        public static readonly DependencyProperty CellNameProperty = DependencyProperty.Register(
+                            "CellName",
+                            typeof(string),
+                            typeof(ManualDebug),
+                            new PropertyMetadata(""));
+
+        public static readonly DependencyProperty OvenIndexProperty = DependencyProperty.Register(
+                            "OvenIndex",
+                            typeof(int),
+                            typeof(ManualDebug),
+                            new PropertyMetadata(0));
+
+        public static readonly DependencyProperty CellIndexProperty = DependencyProperty.Register(
+                            "CellIndex",
+                            typeof(int),
+                            typeof(ManualDebug),
+                            new PropertyMetadata(-1));
+
+        public static readonly DependencyProperty CellCountProperty = DependencyProperty.Register(
+                            "CellCount",
+                            typeof(int),
+                            typeof(ManualDebug),
+                            new PropertyMetadata(0));
+
+        public string OvenName
+        {
+            get { return (string)GetValue(OvenNameProperty); }
+            set
+            {
+                SetValue(OvenNameProperty, value);
+            }
+        }
+
+        public string CellName
+        {
+            get { return (string)GetValue(CellNameProperty); }
+            set { SetValue(CellNameProperty, value); }
+        }
+
+        public int OvenIndex
+        {
+            get { return (int)GetValue(OvenIndexProperty); }
+            set { SetValue(OvenIndexProperty, value); }
+        }
+
+        public int CellIndex
+        {
+            get { return (int)GetValue(CellIndexProperty); }
+            set { SetValue(CellIndexProperty, value); }
+        }
+
+        public int CellCount
+        {
+            get { return (int)GetValue(CellCountProperty); }
+            set { SetValue(CellCountProperty, value); }
+        }
+
+        public ObservableCollection<TestDataItem> TestData { get; private set; } = new ObservableCollection<TestDataItem>();
+
+        private Dictionary<string, StatusBtn> btnCache = new Dictionary<string, StatusBtn>();
+
+        // 日志级别颜色
+        private readonly SolidColorBrush _debugBrush;
+        private readonly SolidColorBrush _infoBrush;
+        private readonly SolidColorBrush _warnBrush;
+        private readonly SolidColorBrush _errorBrush;
+
+        private ITestExecutionWrapper executionWrapper;
+
+        private TcpServer tcpServer;
+
+        public string seqPath { get; set; }
+        public string modelPath { get; set; } = "C:\\Program Files\\National Instruments\\TestStand 2020\\Components\\Models\\TestStandModels\\ParallelModel.seq";
+
+        [ObservableProperty]
+        private string tcpServerStatus;
+        [ObservableProperty]
+        private string tcpServerInfo;
+
+        public ManualDebug()
+        {
+            _debugBrush = new SolidColorBrush(Color.FromRgb(0x71, 0x80, 0x8A)); // 灰色
+            _infoBrush = new SolidColorBrush(Colors.Black);
+            _warnBrush = new SolidColorBrush(Color.FromRgb(0xD6, 0x9E, 0x2E));  // 橙黄色
+            _errorBrush = new SolidColorBrush(Color.FromRgb(0xC5, 0x30, 0x30));   // 红色
+            this.Loaded += ManualDebug_Loaded;
+            InitializeComponent();
+            RefreshButtons(null);
+        }
+
+        public void HideCells(bool flag)
+        {
+            CellSelectBorder.Visibility = flag ? Visibility.Collapsed : Visibility.Visible;
+        }
+
+        private List<ManualCell> Cells = new List<ManualCell>();
+        private List<StatusCell> StatusCells = new List<StatusCell>();
+
+        public void RefreshGrid(SingleCell[] cells)
+        {
+            if (cells != null)
+            {
+                int i = 0;
+                foreach (var cell in cells)
+                {
+                    var mCell = new ManualCell
+                    {
+                        Title = cell.title,
+                        Index = cell.index
+                    };
+                    mCell.SelectedChanged += MCell_SelectedChanged;
+                    Cells.Add(mCell);
+                    CellGrid.Children.Add(mCell);
+
+                    var sCell = new StatusCell
+                    {
+                        Title = cell.title,
+                        Index = cell.index
+                    };
+                    StatusCells.Add(sCell);
+                    CellResultGrid.Children.Add(sCell);
+
+                    BindingOperations.SetBinding(sCell, StatusCell.StatusProperty, new Binding(nameof(ManualCell.Selected))
+                    {
+                        Source = mCell,
+                        Mode = BindingMode.OneWay,
+                        //UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
+                        Converter = CellStatusConverter.Instance
+                    });
+                }
+
+                UniformGridHelper.RefreshGridInnerBorder(CellGrid);
+                UniformGridHelper.RefreshGridInnerBorder(CellResultGrid);
+            }
+        }
+
+        private void MCell_SelectedChanged(object sender, EventArgs e)
+        {
+            object[] arr = Cells.Select(c => (object)c.Selected).ToArray();
+            SetArray($"Running.CellIndex", PropertyValueTypes.PropValType_Boolean, arr);
+        }
+
+        private void ManualDebug_Loaded(object sender, RoutedEventArgs e)
+        {
+            StartTcpServer();
+            Task.Run(() =>
+            {
+                System.Threading.Thread.Sleep(500);
+                Dispatcher.BeginInvoke(() =>
+                {
+                    InitWrapper();
+                    StartSeq();
+                    mask.Visibility = Visibility.Collapsed;
+                });
+            });
+        }
+
+        private int GetConfigPort()
+        {
+            try
+            {
+                var customConfig = (Hashtable)ConfigurationManager.GetSection("TestResultConfig");
+                if (customConfig.ContainsKey("PortStart"))
+                {
+                    int port = int.Parse(customConfig["PortStart"].ToString()) - 1;
+                    return port;
+                }
+                else
+                {
+                    return 6659;
+                }
+            }
+            catch (Exception ex)
+            {
+                AppendLog(LogLevel.Error, $"读取起始端口错误: {ex.Message} {ex.StackTrace} 使用默认端口6659");
+                return 6659;
+            }
+        }
+
+        /// <summary>
+        /// 启动Tcp服务器
+        /// </summary>
+        [RelayCommand]
+        private void StartTcpServer()
+        {
+            int port = GetConfigPort();
+            var serverConfig = new ServerConfig()
+            {
+                MaxRequestLength = 1024 * 1024,
+                Port = port
+            };
+            try
+            {
+                if (tcpServer == null)
+                {
+                    tcpServer = new TcpServer();
+                    tcpServer.NewRequestReceived += TcpServer_NewRequestReceived;
+                    if (!tcpServer.Setup(serverConfig))
+                    {
+                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
+                        return;
+                    }
+                    if (!tcpServer.Start())
+                    {
+                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.StartFailed"));
+                        return;
+                    }
+                    ChangeTcpServerStatus("RUNNING", this.lang("Tr.TcpServer.Running"));
+                    AppendLog(LogLevel.Debug, $"数据接收服务器启动,端口:{port}");
+                }
+                else if (tcpServer.State != SuperSocket.SocketBase.ServerState.Running)
+                {
+                    if (!tcpServer.Setup(serverConfig))
+                    {
+                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.SettingFailed"));
+                        return;
+                    }
+                    if (!tcpServer.Start())
+                    {
+                        ChangeTcpServerStatus("ERROR", this.lang("Tr.TcpServer.StartFailed"));
+                        return;
+                    }
+                    ChangeTcpServerStatus("RUNNING", this.lang("Tr.TcpServer.Running"));
+                    AppendLog(LogLevel.Debug, $"数据接收服务器启动,端口:{port}");
+                }
+            }
+            catch (Exception ex)
+            {
+                ChangeTcpServerStatus("ERROR", $"{ex.Message}");
+                AppendLog(LogLevel.Error, $"数据接收服务器启动错误: {ex.Message} {ex.StackTrace}");
+                tcpServer?.Stop();
+                tcpServer = null;
+            }
+        }
+
+        /// <summary>
+        /// 更新Tcp服务器状态
+        /// </summary>
+        /// <param name="status"></param>
+        /// <param name="info"></param>
+        private void ChangeTcpServerStatus(string status, string info)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                TcpServerStatus = status;
+                TcpServerInfo = info;
+            });
+        }
+
+        private void TcpServer_NewRequestReceived(TcpSession session, SuperSocket.SocketBase.Protocol.StringRequestInfo requestInfo)
+        {
+            string msg = requestInfo.Body;
+            string[] datas_10015 = msg.Split(';');
+            //int cellIndex = int.Parse(datas_10015[1]);
+
+            string strs = datas_10015[2];
+            string[] arr = strs.Split(',');
+            Dispatcher.Invoke(() =>
+            {
+                AddTestData(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
+            });
+        }
+
+        public void RefreshButtons(List<BtnConfig> settings)
+        {
+            if (settings == null) return;
+
+            for (int i = 0; i < settings.Count; i++)
+            {
+                var setting = settings[i];
+
+                var margin = new Thickness(0, 0, 20, 0);
+                if (i == settings.Count - 1)
+                {
+                    margin = new Thickness(0);
+                }
+
+                var btn = new StatusBtn
+                {
+                    Title = setting.Title,
+                    Tag = setting,
+                    Margin = margin
+                };
+                btn.BtnClick += Btn_BtnClick;
+                btnCache[setting.Name] = btn;
+                btnPanel.Children.Add(btn);
+            }
+        }
+
+        public void StartSeq()
+        {
+            try
+            {
+                executionWrapper?.StartSequence(seqPath, modelPath);
+            }
+            catch (Exception ex)
+            {
+                AppendLog(LogLevel.Error, $"启动测试序列失败 {ex.Message} {ex.StackTrace}");
+            }
+        }
+
+        private void InitWrapper()
+        {
+            try
+            {
+                var wrapper = new TeststandWrapper();
+                var AxApplicationMgr = wrapper.AxApplicationMgr;
+                ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).BeginInit();
+                host.Child = AxApplicationMgr;
+                ((System.ComponentModel.ISupportInitialize)(AxApplicationMgr)).EndInit();
+                wrapper.Init();
+                executionWrapper = wrapper;
+
+                wrapper.UIMessageEvent += Wrapper_UIMessageEvent;
+            }
+            catch (Exception ex)
+            {
+                AppendLog(LogLevel.Error, $"初始化Teststand {ex.Message} {ex.StackTrace}");
+            }
+        }
+
+        private void SetArray(string name, PropertyValueTypes type, object[] arr)
+        {
+            try
+            {
+                var context = ((TeststandWrapper)executionWrapper).context;
+                context.FileGlobals.DeleteSubProperty(name, 2);
+
+                context.FileGlobals.NewSubProperty(name, type, true, null, 1);
+                context.FileGlobals.GetPropertyObject(name, 0).SetNumElements(arr.Length);
+
+                for (int i = 0; i < arr.Length; i++)
+                {
+                    if (type == PropertyValueTypes.PropValType_Boolean)
+                    {
+                        context.FileGlobals.SetValBoolean($"{name}[{i}]", 0, arr[i] == null ? false : (bool)arr[i]);
+                    }
+                    else
+                    {
+                        context.FileGlobals.SetValString($"{name}[{i}]", 0, arr[i]?.ToString());
+                    }
+                    // (type == PropertyValueTypes.PropValType_String)
+                }
+            }
+            catch (Exception ex)
+            {
+                AppendLog(LogLevel.Error, $"写数组 {name} 错误: {ex.Message} {ex.StackTrace}");
+            }
+        }
+
+        private void Wrapper_UIMessageEvent(object sender, UIMessageEvent e)
+        {
+            try
+            {
+                if (e.EventCode == 10000)
+                {
+                    executionWrapper?.SetContext(e.Data);
+
+                    string key = $"Running.OvenName";
+                    executionWrapper?.SetFileGlobalValue(key, 1, OvenName);
+
+                    key = $"Running.OvenIndex";
+                    executionWrapper?.SetFileGlobalValue(key, 1, OvenIndex);
+
+                    //key = $"Running.CellIndex";
+                    //executionWrapper?.SetFileGlobalValue(key, 1, CellIndex);
+                    if (CellIndex >= 0)
+                    {
+                        key = $"Running.CellIndex";
+                        object[] arr = new object[CellCount];
+                        arr[CellIndex] = true;
+                        SetArray(key, PropertyValueTypes.PropValType_Boolean, arr);
+
+                        key = $"Running.CellName";
+                        string[] names = new string[CellCount];
+                        names[CellIndex] = CellName;
+                        SetArray(key, PropertyValueTypes.PropValType_String, names);
+                    }
+                    else
+                    {
+                        string[] names = Cells.Select(c => c.Title).ToArray();
+                        SetArray($"Running.CellName", PropertyValueTypes.PropValType_String, names);
+                    }
+
+
+                    Dispatcher.Invoke(() =>
+                    {
+                        CellGrid.IsEnabled = true;
+                        AppendLog(LogLevel.Debug, "Context初始化完成");
+                    });
+                    return;
+                }
+                if (e.EventCode == 10001)
+                {
+                    string[] arr = e.StrData.Split(',');
+                    Dispatcher.BeginInvoke(() =>
+                    {
+                        AddTestData(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
+                    });
+                    return;
+                }
+                if (e.EventCode == 10002)
+                {
+                    string[] arr = e.StrData.Split(',');
+                    LogLevel level = (LogLevel)int.Parse(arr[0]);
+                    Dispatcher.BeginInvoke(() =>
+                    {
+                        AppendLog(level, arr[1]);
+                    });
+                    return;
+                }
+                if (e.EventCode == 10003)
+                {
+                    string[] arr = e.StrData.Split(',');
+                    DebugStatus status = (DebugStatus)int.Parse(arr[1]);
+                    Dispatcher.BeginInvoke(() =>
+                    {
+                        SetStatus(arr[0], status);
+                    });
+                    return;
+                }
+                if (e.EventCode == 10004)
+                {
+                    int index = Convert.ToInt32(e.NumberData);
+                    //CellStatus status = (CellStatus)Enum.Parse(typeof(CellStatus), e.StrData);
+                    CellStatus status = (CellStatus)int.Parse(e.StrData);
+                    Dispatcher.BeginInvoke(() =>
+                    {
+                        if (index < StatusCells.Count)
+                        {
+                            StatusCells[index].SetCurrentValue(StatusCell.StatusProperty, status);
+                        }
+                    });
+                    return;
+                }
+            }
+            catch (Exception ex)
+            {
+                AppendLog(LogLevel.Error, $"处理UI信息失败 {ex.Message} {ex.StackTrace}");
+            }
+        }
+
+        private void Btn_BtnClick(object sender, RoutedEventArgs e)
+        {
+            var btn = (StatusBtn)sender;
+            BtnConfig config = (BtnConfig)btn.Tag;
+            StartSingleTest(config.Name);
+        }
+
+        private void StartSingleTest(string name)
+        {
+            try
+            {
+                string key = $"Running.ActionName";
+                executionWrapper?.SetFileGlobalValue(key, 1, name);
+            }
+            catch (Exception ex)
+            {
+                AppendLog(LogLevel.Error, $"启动功能失败 {ex.Message} {ex.StackTrace}");
+            }
+        }
+
+        // 设置状态显示
+        private void SetStatus(string labelName, DebugStatus status)
+        {
+            if (btnCache.ContainsKey(labelName))
+            {
+                var btn = btnCache[labelName];
+                btn.Status = status;
+            }
+        }
+
+        // 重置状态显示
+        private void ResetStatus(string labelName)
+        {
+            SetStatus(labelName, DebugStatus.Idle);
+        }
+
+        // 设置测试中状态并禁用按钮
+        private void SetTestingStatus(string labelName, Button button)
+        {
+            SetStatus(labelName, DebugStatus.Testing);
+            button.IsEnabled = false;
+        }
+
+        private void OnClearClick(object sender, RoutedEventArgs e)
+        {
+            TestData.Clear();
+        }
+
+        private void OnClearLogClick(object sender, RoutedEventArgs e)
+        {
+            LogRichTextBox.Document.Blocks.Clear();
+        }
+
+        private void AddTestData(string name, string send, string receive, string unit, string upper, string lower, string result, string time)
+        {
+            TestData.Add(new TestDataItem
+            {
+                Name = name,
+                Send = send,
+                Receive = receive,
+                Unit = unit,
+                UpperLimit = upper,
+                LowerLimit = lower,
+                Result = result,
+                Time = time
+            });
+
+            if (DataGridComm.Items.Count > 0)
+            {
+                DataGridComm.ScrollIntoView(DataGridComm.Items[DataGridComm.Items.Count - 1]);
+            }
+        }
+
+        // 获取日志级别对应的颜色
+        private Brush GetLogLevelBrush(LogLevel level)
+        {
+            switch (level)
+            {
+                case LogLevel.Debug:
+                    return _debugBrush;
+                case LogLevel.Info:
+                    return _infoBrush;
+                case LogLevel.Warn:
+                    return _warnBrush;
+                case LogLevel.Error:
+                    return _errorBrush;
+                default:
+                    return _infoBrush;
+            }
+        }
+
+        // 添加带级别的日志
+        private void AppendLog(LogLevel level, string message)
+        {
+            Dispatcher.BeginInvoke(() =>
+            {
+                string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
+                string levelStr = level.ToString().ToUpper();
+
+                Paragraph? paragraph = LogRichTextBox.Document.Blocks.LastBlock as Paragraph;
+                if (paragraph == null)
+                {
+                    paragraph = new Paragraph();
+                    LogRichTextBox.Document.Blocks.Add(paragraph);
+                }
+
+                // 时间戳(灰色)
+                Run timestampRun = new Run("[" + timestamp + "] ") { Foreground = _debugBrush };
+                paragraph.Inlines.Add(timestampRun);
+
+                // 级别标签
+                Brush levelBrush = GetLogLevelBrush(level);
+                Run levelRun = new Run("[" + levelStr + "] ") { Foreground = levelBrush, FontWeight = FontWeights.SemiBold };
+                paragraph.Inlines.Add(levelRun);
+
+                // 消息内容
+                Run messageRun = new Run(message + "\n") { Foreground = levelBrush };
+                paragraph.Inlines.Add(messageRun);
+
+                LogRichTextBox.ScrollToEnd();
+            });
+        }
+
+        // 兼容旧调用方式,默认 Info 级别
+        private void AppendLog(string message)
+        {
+            AppendLog(LogLevel.Info, message);
+        }
+
+        public void Stop()
+        {
+            try
+            {
+                executionWrapper?.SetFileGlobalValue("Running.StopTest", 1, true);
+                executionWrapper?.WaitForExit();
+                executionWrapper?.Dispose();
+                tcpServer?.Stop();
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+    }
+
+    public class TestDataItem : INotifyPropertyChanged
+    {
+        private string _name = "";
+        private string _receive = "";
+        private string _send = "";
+        private string _unit = "";
+        private string _upperLimit = "";
+        private string _lowerLimit = "";
+        private string _result = "";
+        private string _time = "";
+
+        public string Name
+        {
+            get { return _name; }
+            set { _name = value; OnPropertyChanged("Name"); }
+        }
+
+        public string Receive
+        {
+            get { return _receive; }
+            set { _receive = value; OnPropertyChanged("Receive"); }
+        }
+
+        public string Send
+        {
+            get { return _send; }
+            set { _send = value; OnPropertyChanged("Send"); }
+        }
+
+        public string Unit
+        {
+            get { return _unit; }
+            set { _unit = value; OnPropertyChanged("Unit"); }
+        }
+
+        public string UpperLimit
+        {
+            get { return _upperLimit; }
+            set { _upperLimit = value; OnPropertyChanged("UpperLimit"); }
+        }
+
+        public string LowerLimit
+        {
+            get { return _lowerLimit; }
+            set { _lowerLimit = value; OnPropertyChanged("LowerLimit"); }
+        }
+
+        public string Result
+        {
+            get { return _result; }
+            set { _result = value; OnPropertyChanged("Result"); }
+        }
+
+        public string Time
+        {
+            get { return _time; }
+            set { _time = value; OnPropertyChanged("Time"); }
+        }
+
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        protected virtual void OnPropertyChanged(string propertyName)
+        {
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+        }
+    }
+}

--
Gitblit v1.9.1