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.StandardTest/SingleControl.xaml.cs |  916 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 916 insertions(+), 0 deletions(-)

diff --git a/Plugin.StandardTest/SingleControl.xaml.cs b/Plugin.StandardTest/SingleControl.xaml.cs
new file mode 100644
index 0000000..fb4d9a7
--- /dev/null
+++ b/Plugin.StandardTest/SingleControl.xaml.cs
@@ -0,0 +1,916 @@
+锘縰sing HMI.Core.Entity;
+using HMI.Core.Manager;
+using HMI.Core.Setting;
+using HMI.Core.Util;
+using HMI.UI.Util;
+using HMI.Util;
+using NationalInstruments.TestStand.Interop.API;
+using Plugin.StandardTest.Entity;
+using Plugin.StandardTest.Setting;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Protocol;
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Configuration;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Timers;
+using System.Windows;
+using System.Windows.Controls;
+using Util.ManualDebug;
+
+namespace Plugin.StandardTest
+{
+    /// <summary>
+    /// SingleControl.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class SingleControl : UserControl, INotifyPropertyChanged
+    {
+        #region ...
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        protected void Set([CallerMemberName] string name = null)
+        {
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+        }
+
+        protected void Set<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
+        {
+            old = @new;
+            if (propertyName != null)
+            {
+                Set(propertyName);
+            }
+        }
+        #endregion
+        private int index;
+        private string CountIniPath;
+
+        private string status = "Ready";
+
+        private ScreenTouch[] screens;
+        private ScreenGrid screenGrid;
+
+        public string Status
+        {
+            get { return status; }
+            set { Set(ref status, value); }
+        }
+
+        private double totalNum;
+        public double TotalNum
+        {
+            get => totalNum;
+            private set
+            {
+                Set(ref totalNum, value);
+            }
+        }
+
+        public StandardTestUI parent { get; set; }
+
+        private int passNum;
+        public int PassNum
+        {
+            get => passNum;
+            private set { Set(ref passNum, value); }
+        }
+
+        private int failNum;
+        public int FailNum
+        {
+            get => failNum;
+            private set { Set(ref failNum, value); }
+        }
+
+        private double passRate;
+        public double PassRate
+        {
+            get => passRate;
+            private set
+            {
+                Set(ref passRate, value);
+            }
+        }
+
+        public string NumInfo
+        {
+            get => this.lang("Ts.NumInfo", TotalNum, PassNum, FailNum, string.Format("{0:0.00}", PassRate));
+        }
+
+        public string TestInfo
+        {
+            get => this.lang("Ts.TestInfo", Barcode, ElapseTime);
+        }
+
+        private string barcode = string.Empty;
+        public string Barcode
+        {
+            get { return barcode; }
+            set { Set(ref barcode, value); }
+        }
+
+        private string model = string.Empty;
+        public string Model
+        {
+            get { return model; }
+            set { Set(ref model, value); }
+        }
+
+        private string elapseTime = "00:00:00";
+        public string ElapseTime
+        {
+            get { return elapseTime; }
+            set
+            {
+                if (value == "Start Timing")
+                {
+                    this.refreshTimer.Enabled = true;
+                    TestStartTime = DateTime.Now;
+                    StepStartTime = DateTime.Now;
+                }
+                else
+                {
+                    this.refreshTimer.Enabled = false;
+                    if (value != "Stop Timing")
+                    {
+                        Set(ref elapseTime, value + "s");
+                        Set("TestInfo");
+                    }
+                }
+            }
+        }
+
+        private string socketName = string.Empty;
+        public string SocketName
+        {
+            get { return socketName; }
+            set
+            {
+                Set(ref socketName, value);
+            }
+        }
+
+        private ObservableCollection<TestStep> _datasource = new ObservableCollection<TestStep>();
+        public ObservableCollection<TestStep> Datasource
+        {
+            get => _datasource;
+            set
+            {
+                Set(ref _datasource, value);
+            }
+        }
+
+        private DateTime TestStartTime = new DateTime();
+        private DateTime StepStartTime = new DateTime();
+        private TimeSpan TimeCount = new TimeSpan();
+
+        private System.Timers.Timer refreshTimer;
+
+        private string currentStep = "";
+        public string CurrentStep
+        {
+            get => currentStep;
+            set
+            {
+                Set(ref currentStep, value);
+            }
+        }
+
+        private int _probeCount;
+        public int ProbeCount
+        {
+            get => _probeCount;
+            set
+            {
+                Set(ref _probeCount, value);
+                try
+                {
+                    if (ProbeWarn <= 0) probeCountAlarm.Visibility = Visibility.Collapsed;
+                    else if (value >= ProbeWarn) probeCountAlarm.Visibility = Visibility.Visible;
+                    else probeCountAlarm.Visibility = Visibility.Collapsed;
+                }
+                catch
+                {
+
+                }
+            }
+        }
+
+        /// <summary>
+        /// 杩涘害鍊�
+        /// </summary>
+        private int _processValue;
+        public int ProcessValue
+        {
+            get => _processValue;
+            set
+            {
+                Set(ref _processValue, value);
+            }
+        }
+
+        public string ProcessValueLabel
+        {
+            get
+            {
+                if (ProcessValue > 0) return $"{ProcessValue}%";
+                return "";
+            }
+            set
+            {
+            }
+        }
+
+        public void SetSimpleUI(bool flag)
+        {
+            if (flag)
+            {
+                simplePanel.Visibility = Visibility.Visible;
+                detailPanel.Visibility = Visibility.Collapsed;
+            }
+            else
+            {
+                simplePanel.Visibility = Visibility.Collapsed;
+                detailPanel.Visibility = Visibility.Visible;
+            }
+        }
+
+        private int _probeWarn;
+        public int ProbeWarn
+        {
+            get => _probeWarn;
+            private set
+            {
+                Set(ref _probeWarn, value);
+            }
+        }
+
+        private bool isAutoMode;
+        public bool IsAutoMode
+        {
+            get => isAutoMode;
+            set
+            {
+                Set(ref isAutoMode, value);
+            }
+        }
+
+        public SingleControl(int Index, string CountIniPath, int probeWarn)
+        {
+            DataContext = this;
+            this.CountIniPath = CountIniPath;
+            this.index = Index;
+            ProbeWarn = probeWarn;
+
+            refreshTimer = new System.Timers.Timer()
+            {
+                Interval = 100
+            };
+            refreshTimer.Elapsed += RefreshTimer_Elapsed;
+            ReadCounter();
+            InitializeComponent();
+            RefreshProbeCount(null);
+            try
+            {
+                var value = ParameterManager.GetCommonParameterByName("AutoTestMode");
+                IsAutoMode = bool.Parse(value.Value);
+            }
+            catch
+            {
+                IsAutoMode = true;
+            }
+        }
+
+        private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e)
+        {
+            TimeCount = DateTime.Now - TestStartTime;
+            elapseTime = string.Format("{0:00}:{1:00}:{2:00}", TimeCount.Hours, TimeCount.Minutes, TimeCount.Seconds);
+            Set("ElapseTime");
+            Set("TestInfo");
+        }
+
+        /// <summary>
+        /// 鑾峰彇TS鐨勬瘡姝ユ祴璇曠殑鏁版嵁娣诲姞鍒拌〃鏍�
+        /// 椤圭洰鍚嶇О锛屾祴璇曞弬鏁帮紝娴嬭瘯鍊硷紝涓婇檺锛屼笅闄愶紝鍗曚綅锛岀粨鏋�
+        /// </summary>
+        /// <param name="TS_Data"></param>
+        public void AddTestData(string name, string step_name, string value, string HightLimit, string LowLimit, string unit, string Status)
+        {
+            try
+            {
+                var data = new TestStep
+                {
+                    Name = name,
+                    Step = step_name,
+                    Value = value,
+                    LimitUp = HightLimit,
+                    LimitDown = LowLimit,
+                    Unit = unit,
+                    Result = Status
+                };
+                table.Dispatcher.Invoke(new Action(() =>
+                {
+                    var now = DateTime.Now;
+                    var span = now - StepStartTime;
+                    StepStartTime = now;
+                    data.ConsumeTime = $"{(span.TotalMilliseconds / 1000d).ToString("F2")}s";
+                    Datasource.Add(data);
+                    table.ScrollIntoView(data);
+                }));
+            }
+            catch (Exception ex)
+            {
+                //灞忚斀閿欒
+            }
+        }
+
+        public void UpdateStatus(string status)
+        {
+            if (status == "Passed")
+            {
+                UpdateTestCounter(true);
+            }
+            else if (status == "Error" || status == "Failed")
+            {
+                UpdateTestCounter(false);
+            }
+            else if (Status != "Testing" && status == "Testing")
+            {
+                UpdateProbeCount(ProbeCount + 1);
+            }
+            Status = status;
+        }
+
+        public void IniTestData()
+        {
+            Datasource.Clear();
+        }
+
+        /// <summary>
+        /// 璁剧疆娴嬭瘯杩涘害鏉�
+        /// </summary>
+        /// <param name="ProcessVal"></param>
+        public void SetTestProcess(int ProcessVal)
+        {
+            ProcessValue = ProcessVal;
+            Set("ProcessValueLabel");
+        }
+
+        private void UserControl_Loaded(object sender, RoutedEventArgs e)
+        {
+            StartTcpServer();
+            this.UpdateTestCounter(null);
+            Loaded -= UserControl_Loaded;
+        }
+
+        private void UpdateTestCounter(bool? result)
+        {
+            if (result == true)
+            {
+                PassNum += 1;
+                //Interlocked.Increment(ref passNum);
+                //Set("PassNum");
+            }
+            else if (result == false)
+            {
+                FailNum += 1;
+                //Interlocked.Increment(ref failNum);
+                //Set("FailNum");
+            }
+
+            TotalNum = PassNum + FailNum;
+            if (TotalNum != 0)
+            {
+                PassRate = ((double)PassNum / TotalNum);
+            }
+            else
+            {
+                PassRate = 0.0;
+            }
+            Set("NumInfo");
+            this.WriteCounter();
+        }
+
+        private void WriteCounter()
+        {
+            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "Pass",
+                this.passNum.ToString(), this.CountIniPath);
+            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "Fail", this.failNum.ToString(),
+                this.CountIniPath);
+        }
+
+        private void ReadCounter()
+        {
+            try
+            {
+                this.PassNum = Convert.ToInt32(IniFileHelper.ReadIniData("Socket" + this.index.ToString(), "Pass", "", CountIniPath));
+                this.FailNum = Convert.ToInt32(IniFileHelper.ReadIniData("Socket" + this.index.ToString(), "Fail", "", CountIniPath));
+            }
+            catch (Exception ex)
+            {
+                //do nothing
+            }
+        }
+
+        public void ResetCounter()
+        {
+            this.PassNum = 0;
+            this.FailNum = 0;
+            this.UpdateTestCounter(null);
+        }
+
+        public void WriteToExcel(string path)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                try
+                {
+                    string suffix = Path.GetExtension(path);
+                    if (!string.Equals(".xlsx", suffix, StringComparison.OrdinalIgnoreCase) &&
+                    !string.Equals(".xls", suffix, StringComparison.OrdinalIgnoreCase))
+                    {
+                        path = path.Replace(suffix, ".xls");
+                    }
+
+                    FileInfo fi = new FileInfo(path);
+                    if (!fi.Directory.Exists)
+                    {
+                        fi.Directory.Create();  //鏂囦欢涓嶅瓨鍦紝鍒涘缓鏂囦欢
+                    }
+                    List<string[]> rows = new List<string[]>();
+                    rows.Add(new string[] { "娴嬭瘯鏃堕棿", "娴嬭瘯浜�", "娴嬭瘯璁惧", "鏉″舰鐮�", "娴嬭瘯缁撴灉" });
+                    rows.Add(new string[] { DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), UserInfo.User.Name, ParamSetting.StationName, Barcode, Status });
+                    rows.Add(new string[] { "椤圭洰鍚嶇О", "娴嬭瘯鍙傛暟", "娴嬭瘯鍊�", "涓婇檺", "涓嬮檺", "鍗曚綅", "缁撴灉" });
+                    if (Datasource != null && Datasource.Count > 0)
+                    {
+                        foreach (var data in Datasource)
+                        {
+                            rows.Add(new string[] { data.Name, data.Step, data.Value, data.LimitUp, data.LimitDown, data.Unit, data.Result });
+                        }
+                    }
+                    ExcelHelper.ExportExcel(path, rows);
+                }
+                catch (Exception ex)
+                {
+                    StandardTestUI.logger.Error("Write CSV File Failed.", ex);
+                }
+            });
+        }
+
+
+        public void WriteToCSV(string path)
+        {
+            Dispatcher.Invoke(() =>
+            {
+                try
+                {
+
+                    FileInfo fi = new FileInfo(path);
+                    if (!fi.Directory.Exists)
+                    {
+                        fi.Directory.Create();  //鏂囦欢涓嶅瓨鍦紝鍒涘缓鏂囦欢
+                    }
+
+                    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
+                    using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
+                    {
+                        string title = "椤圭洰鍚嶇О,娴嬭瘯鍙傛暟,娴嬭瘯鍊�,涓婇檺,涓嬮檺,鍗曚綅,缁撴灉";
+                        sw.WriteLine(title);
+
+                        if (Datasource != null && Datasource.Count > 0)
+                        {
+                            foreach (var data in Datasource)
+                            {
+                                sw.WriteLine($"{data.Name},{data.Step},{data.Value},{data.LimitUp},{data.LimitDown},{data.Unit},{data.Result}");
+                            }
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    StandardTestUI.logger.Error("Write CSV File Failed.", ex);
+                }
+            });
+        }
+
+        #region TCP鐩稿叧
+
+        private string tcpServerStatus;
+        public string TcpServerStatus
+        {
+            get => tcpServerStatus;
+            set
+            {
+                Set(ref tcpServerStatus, value);
+            }
+        }
+
+        private string tcpServerInfo;
+        public string TcpServerInfo
+        {
+            get => tcpServerInfo;
+            set
+            {
+                Set(ref tcpServerInfo, value);
+            }
+        }
+
+        private TcpServer tcpServer;
+
+        private int GetConfigPort()
+        {
+            try
+            {
+                var customConfig = (Hashtable)ConfigurationManager.GetSection("TestResultConfig");
+                if (customConfig.ContainsKey("PortStart"))
+                {
+                    int port = int.Parse(customConfig["PortStart"].ToString());
+                    return port;
+                }
+                else
+                {
+                    return 6660;
+                }
+            }
+            catch (Exception ex)
+            {
+                return 6660;
+            }
+        }
+
+        private void StartTcpServer()
+        {
+            int port = GetConfigPort();
+            var serverConfig = new ServerConfig()
+            {
+                MaxRequestLength = 1024 * 1024,
+                Port = port + index
+            };
+            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"));
+                }
+                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"));
+                }
+            }
+            catch (Exception ex)
+            {
+                ChangeTcpServerStatus("ERROR", $"{ex.Message}");
+                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;
+            });
+        }
+
+        /// <summary>
+        /// 鍋滄Tcp鏈嶅姟鍣�
+        /// </summary>
+        private void StopTcpServer()
+        {
+            try
+            {
+                tcpServer?.Stop();
+                tcpServer = null;
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+
+        private void TcpServer_NewRequestReceived(TcpSession session, StringRequestInfo requestInfo)
+        {
+            string msg = requestInfo.Body;
+            StandardTestUI.logger.Debug($"TCP Data From {session.RemoteEndPoint}: {msg}.");
+            string[] datas_10015 = msg.Split(';');
+            int cellIndex = int.Parse(datas_10015[1]);
+
+            string strs = datas_10015[2];
+            string[] values = strs.Split(',');
+            Dispatcher.Invoke(() =>
+            {
+                AddTestData("", values[0], values[2], values[4], values[5], values[3], values[6]);
+                //new Info
+                //{
+                //    step = values[0],
+                //    value = values[2],
+                //    unit = values[3],
+                //    limitUp = values[4],
+                //    limitDown = values[5],
+                //    result = values[6],
+                //    updateTime = values[7]
+                //}
+            });
+            //session.Send("1");
+        }
+        #endregion
+
+        public void ShowPausePanel()
+        {
+            pausePanel.Visibility = Visibility.Visible;
+        }
+
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+            Button btn = sender as Button;
+            switch (btn.Name)
+            {
+                case "clear":
+                    UpdateProbeCount(0);
+                    break;
+                case "cancelPauseBtn":
+                    parent.testExecutionWrapper.SetStationGlobalValue($"StationState.IsContinue_{index}", 1, false);
+                    pausePanel.Visibility = Visibility.Collapsed;
+                    break;
+                case "continuePauseBtn":
+                    parent.testExecutionWrapper.SetStationGlobalValue($"StationState.IsContinue_{index}", 1, true);
+                    pausePanel.Visibility = Visibility.Collapsed;
+                    break;
+                case "scannerSetting":
+                    //ScannerSetting setting = new ScannerSetting()
+                    //{
+                    //    Owner = Window.GetWindow(this)
+                    //};
+                    //setting.Open(true, $"Socket{index}", CountIniPath);
+                    ShowScannerPanel();
+                    break;
+                case "scannerOkBtn":
+                    SendSnr2Ts();
+                    break;
+                default:
+                    break;
+            }
+        }
+        public void ShowScannerPanel()
+        {
+            //scannerPanel.Visibility = Visibility.Visible;
+        }
+
+        private void SendSnr2Ts()
+        {
+
+        }
+
+        private void pausePanel_Loaded(object sender, RoutedEventArgs e)
+        {
+            if (sender == pausePanel)
+            {
+                var can = pausePanel;
+                var modal = visualModal;
+                Canvas.SetLeft(modal, (pausePanel.ActualWidth - visualModal.ActualWidth) / 2);
+                Canvas.SetTop(modal, (pausePanel.ActualHeight - visualModal.ActualHeight) / 2);
+            }
+            else
+            {
+                //var can = scannerPanel;
+                //var modal = scannerVisualModal;
+                //Canvas.SetLeft(modal, (scannerPanel.ActualWidth - scannerVisualModal.ActualWidth) / 2);
+                //Canvas.SetTop(modal, (scannerPanel.ActualHeight - scannerVisualModal.ActualHeight) / 2);
+            }
+        }
+
+        public void RefreshScreen(int[][] configs)
+        {
+            //foreach (UIElement item in canvas.Children)
+            //{
+            //    if (item is ScreenTouch)
+            //    {
+            //        canvas.Children.Remove(item);
+            //    }
+            //}
+            screenContainer.Child = null;
+
+
+            screenGrid = new ScreenGrid(1, configs.Length);
+
+            screens = new ScreenTouch[configs.Length];
+
+            int space = 2 * (screens.Length + 1);
+            for (int i = 0; i < configs.Length; i++)
+            {
+                var screen = new ScreenTouch(configs[i][0], configs[i][1])
+                {
+                    Width = Math.Max((tableHeader.ActualHeight / 9 * 16 - space) / screens.Length, 0),
+                    Height = Math.Max((tableHeader.ActualHeight / 9 * 16 - space) / screens.Length / 16 * 9, 0)
+                };
+                screens[i] = screen;
+                screenGrid.AddScreen(screen, 0, i);
+            }
+            screenContainer.Child = screenGrid;
+
+            //canvas.Children.Add(screens[0]);
+            //Canvas.SetBottom(screens[0], 0);
+            //Canvas.SetRight(screens[0], 0);
+            //Panel.SetZIndex(screens[0], 999);
+        }
+
+        public void DrawScreenPoint(int index, int x, int y)
+        {
+            screens[index].DrawPoint(x, y);
+        }
+
+        /// <summary>
+        /// 缁樺浘涔嬪墠娓呯┖宸叉湁鐨�
+        /// </summary>
+        /// <param name="index"></param>
+        /// <param name="points"></param>
+        public void DrawScreenPoints(int index, int[][] points)
+        {
+            screens[index].DrawPoints(points);
+        }
+
+        /// <summary>
+        /// 缁樺浘涔嬪墠涓嶆竻绌哄凡鏈夌殑
+        /// </summary>
+        /// <param name="index"></param>
+        /// <param name="points"></param>
+        public void DrawNewScreenPoints(int index, int[][] points)
+        {
+            screens[index].DrawNewPoints(points);
+        }
+
+        public void ClearScreenPoint(int index)
+        {
+            screens[index].Clear();
+        }
+
+        public void RefreshProbeCount(ProductData product)
+        {
+            try
+            {
+                var num = IniFileHelper.ReadIniData($"Socket{this.index.ToString()}", "ProbeCount", "0", this.CountIniPath);
+                this.ProbeCount = int.Parse(num);
+            }
+            catch (Exception ex)
+            {
+                this.ProbeCount = 0;
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊鎺㈤拡浣跨敤娆℃暟
+        /// </summary>
+        /// <param name="count"></param>
+        private void UpdateProbeCount(int count)
+        {
+            /// 鍐欏埌閰嶇疆鏂囦欢
+            IniFileHelper.WriteIniData("Socket" + this.index.ToString(), "ProbeCount", count.ToString(), this.CountIniPath);
+            ProbeCount = count;
+        }
+
+        public void RefreshProduct(ProductData product)
+        {
+            if (product == null)
+            {
+                screenContainer.Child = null;
+                return;
+            }
+            try
+            {
+                string screenSize = ProductManager.GetParameterByProductAndName(product.Name, "ScreenTouch")?.Value;
+                if (string.IsNullOrEmpty(screenSize))
+                {
+                    screenContainer.Child = null;
+                    return;
+                }
+                string[] arr = screenSize.Split(';');
+                int[][] configs = new int[arr.Length][];
+
+                for (int i = 0; i < arr.Length; i++)
+                {
+                    string[] size = arr[i].Split(',');
+                    configs[i] = new int[2] { int.Parse(size[0]), int.Parse(size[1]) };
+                }
+                RefreshScreen(configs);
+            }
+            catch (Exception ex)
+            {
+                StandardTestUI.logger.Error("RefreshProduct Failed.", ex);
+                screenContainer.Child = null;
+            }
+        }
+
+        private void control_SizeChanged(object sender, SizeChangedEventArgs e)
+        {
+            if (screenGrid == null)
+            {
+                return;
+            }
+            //screenGrid.Height = tableHeader.ActualHeight - 2;
+            //screenGrid.Width = tableHeader.ActualHeight / 9 * 16 - 2;
+
+            int space = 2 * (screens.Length + 1);
+
+            for (int i = 0; i < screens.Length; i++)
+            {
+                var screen = screens[i];
+                screen.Width = Math.Max(Math.Floor((tableHeader.ActualHeight / 9 * 16 - space) / screens.Length), 0);
+                screen.Height = Math.Max(Math.Floor(screen.Width / 16 * 9), 0);
+            }
+        }
+
+        private void screenContainer_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
+        {
+            if (e.ClickCount == 2)
+            {
+                new ScreenTouchWin(screens)
+                {
+                    Owner = Window.GetWindow(this)
+                }.ShowDialog();
+            }
+        }
+
+        private void control_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
+        {
+            if (parent.EnableManualDebug)
+            {
+                ContextMenu contextMenu = new ContextMenu();
+                contextMenu.Closed += ContextMenu_Closed;
+                var item = new MenuItem
+                {
+                    IsCheckable = false,
+                    Name = "manualDebugItem",
+                    Header = this.lang("鎵嬪姩璋冭瘯"),
+                };
+                item.Click += Item_Click; ;
+                contextMenu.Items.Add(item);
+                this.ContextMenu = contextMenu;
+            }
+            else
+            {
+
+                this.ContextMenu = null;
+            }
+        }
+
+        private void Item_Click(object sender, RoutedEventArgs e)
+        {
+            string manualDebugSeq = ParameterManager.GetCommonParameterByName("ManualDebugSeqPath")?.Value;
+            if (string.IsNullOrEmpty(manualDebugSeq))
+            {
+                MsgBoxHelper2.Error("璇峰厛璁剧疆鎵嬪姩璋冭瘯搴忓垪璺緞[ManualDebugSeqPath]!");
+                return;
+            }
+            string modelPath = parent.tsModelPath;
+            var win = new ManualDebugWindow(manualDebugSeq, modelPath, false)
+            {
+                Title = $"宸ヤ綅{this.index}鎵嬪姩璋冭瘯",
+
+                Owner = Window.GetWindow(this),
+                OvenIndex = index,
+                OvenName = "",
+                CellCount = parent.tsSockets.Count,
+
+                CellIndex = 0,
+                CellName = "",
+            };
+            win.ShowDialog();
+        }
+
+        private void ContextMenu_Closed(object sender, RoutedEventArgs e)
+        {
+            this.ContextMenu = null;
+        }
+    }
+}

--
Gitblit v1.9.1