using YiDa_WinForm.Model; using Newtonsoft.Json; using NPOI.SS.UserModel; using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Timers; using System.Windows.Forms; using MQTT_WinformV1.Service; using YiDa_WinForm.Config; using YiDa_WinForm.Service.Mqtt; namespace YiDa_WinForm { public partial class MainForm : Form { // 依赖注入 private readonly MqttClientService _mqttService; private readonly YiDaUploadService _uploadService; private readonly ButtonOperationService _buttonService; // 配方配置 private DataTable _formula; // 配方缓存 private DataTable _immFormulaDic; private DataTable _tcFormulaDic; private List _formulaList1; // 设备1选择的配方 private List _formulaList2; // 设备2选择的配方 private System.Timers.Timer _timer; // 日志文件相关路径 private readonly string _logDirectory = "log"; private string _currentLogFile; private DateTime _lastLogDate = DateTime.MinValue; // ======================================== 初始化相关方法 ======================================== /// /// 构造器 /// public MainForm() { // 初始化设计器方法 InitializeComponent(); _mqttService = new MqttClientService(); _buttonService = new ButtonOperationService(); _uploadService = new YiDaUploadService(); _mqttService.MessageReceived += OnMqttMessage; _formula = new DataTable(); _immFormulaDic = new DataTable(); _tcFormulaDic = new DataTable(); buttonDisconnect.Enabled = false; // 初始化日志目录 InitializeLogDirectory(); } /// /// 初始化日志目录 /// private void InitializeLogDirectory() { if (!Directory.Exists(_logDirectory)) { Directory.CreateDirectory(_logDirectory); } UpdateLogFile(); } /// /// 更新当前日志文件(每日一个新文件) /// private void UpdateLogFile() { DateTime today = DateTime.Now.Date; if (today != _lastLogDate) { _lastLogDate = today; _currentLogFile = Path.Combine(_logDirectory, $"log_{today:yyyyMMdd}.txt"); } } /// /// 加载MainForm信息 /// /// /// private async void Form1_Load(object sender, EventArgs e) { try { MakePanelRound(panelLed); await LoadFormula(); await LoadInjectionMqttDic(); await LoadTemperatureMqttDic(); timer1.Enabled = true; } catch (Exception ex) { AppendLog($"初始化失败:{ex.Message}"); } } /// /// 将指定 Panel 变成圆形 /// /// private static void MakePanelRound(Panel panel) { int size = Math.Min(panel.Width, panel.Height); panel.Size = new Size(size, size); GraphicsPath path = new GraphicsPath(); path.AddEllipse(0, 0, panel.Width, panel.Height); panel.Region = new Region(path); } /// /// 加载配方 /// private async Task LoadFormula() { // 从数据库查询配方信息 DataTable dt = await _buttonService.QueryFormulaAsync(); if (dt != null && dt.Rows.Count > 0) { // 新增列用于拼接 part_number|part_name if (!dt.Columns.Contains("part_number_name")) dt.Columns.Add("part_number_name", typeof(string)); foreach (DataRow row in dt.Rows) { row["part_number_name"] = row["part_number"].ToString() + "|" + row["part_name"].ToString(); } // 使用 LINQ 去重,按 part_number + part_name var uniqueParts = dt.AsEnumerable() .GroupBy(r => r["part_number_name"].ToString()) .Select(g => g.First()) .ToList(); // 新建 DataTable 用于绑定下拉框 DataTable dtUnique = dt.Clone(); // 保留原有列 foreach (var row in uniqueParts) { dtUnique.ImportRow(row); } // 新建空白行 DataRow drNew = dtUnique.NewRow(); drNew["id"] = -1; drNew["part_number_name"] = ""; // 空行值 dtUnique.Rows.InsertAt(drNew, 0); _formula = dt; // 原始配方表(多行) bsPF.DataSource = dtUnique; bsPF2.DataSource = dtUnique; comboBox1.DisplayMember = "part_number_name"; // 显示“零件号|零件名” comboBox1.ValueMember = "part_number_name"; comboBox2.DisplayMember = "part_number_name"; comboBox2.ValueMember = "part_number_name"; // 自定义显示格式:显示“零件号 - 零件名” comboBox1.Format += (s, e) => { if (e.ListItem is DataRowView drv) { e.Value = string.IsNullOrEmpty(drv["part_number_name"].ToString()) ? "" : drv["part_number"].ToString() + " - " + drv["part_name"].ToString(); } }; comboBox2.Format += (s, e) => { if (e.ListItem is DataRowView drv) { e.Value = string.IsNullOrEmpty(drv["part_number_name"].ToString()) ? "" : drv["part_number"].ToString() + " - " + drv["part_name"].ToString(); } }; } } /// /// 加载注塑机MQTT字典 /// private async Task LoadInjectionMqttDic() { DataTable dt = await _buttonService.InitInjectionMqttDic(); if (dt != null) { _immFormulaDic = dt; } } /// /// 加载温控器MQTT字典 /// private async Task LoadTemperatureMqttDic() { DataTable dt = await _buttonService.InitTemperatureMqttDic(); if (dt != null) { _tcFormulaDic = dt; } } // ======================================== 按钮相关方法 ======================================== /// /// 网关连接按钮 /// /// /// private async void buttonConnect_Click(object sender, EventArgs e) { try { AppendLog("正在连接MQTT服务器..."); await _mqttService.MqttClientStartAsync(); AppendLog("连接成功!"); panelLed.BackColor = Color.Green; toolStripStatusLabel1.Text = "已连接"; buttonConnect.Enabled = false; buttonDisconnect.Enabled = true; } catch (Exception ex) { panelLed.BackColor = Color.Red; AppendLog($"连接失败:{ex.Message}"); toolStripStatusLabel1.Text = "连接失败"; } } /// /// 网关断开按钮 /// /// /// private async void buttonDisconnect_Click(object sender, EventArgs e) { try { await _mqttService.MqttClientStopAsync(); AppendLog("已断开连接"); panelLed.BackColor = Color.Red; toolStripStatusLabel1.Text = "未连接"; buttonConnect.Enabled = true; buttonDisconnect.Enabled = false; } catch (Exception ex) { AppendLog($"无法断开连接:{ex.Message}"); } } /// /// 关闭MainForm按钮 /// /// /// private async void MainFormClosing(object sender, FormClosingEventArgs e) { try { await _mqttService.MqttClientStopAsync(); } catch (Exception ex) { AppendLog($"关闭窗口失败:{ex.Message}"); } } /// /// 配方导入按钮 /// /// /// private async void button1_Click(object sender, EventArgs e) { try { //弹出文件选择窗口 OpenFileDialog openFile = new OpenFileDialog(); if (openFile.ShowDialog() == DialogResult.OK) { string filePath = openFile.FileName; DataTable excelDt = GetExcel(filePath); if (excelDt != null && excelDt.Rows.Count > 0) { await _buttonService.SaveFormulaByExcel(excelDt); await LoadFormula(); MessageBox.Show("配方信息已导入!"); } else { MessageBox.Show("不允许导入空表!"); } } } catch (Exception ex) { AppendLog($"配方导入失败:{ex.Message}"); } } /// /// 上传宜搭按钮 /// /// /// private async void button2_Click(object sender, EventArgs e) { try { // 如果 MQTT 没连接,就不允许上传 if (toolStripStatusLabel1.Text != "已连接") { MessageBox.Show("请先连接MQTT网关!"); return; } if (this.comboBox1.Text.Length == 0 && this.comboBox2.Text.Length == 0) { //如果两个下拉框都还没数据,说明没导入过配方,直接中止。 MessageBox.Show("请先导入配方信息,再刷新!"); return; } this.button2.Enabled = false; // 设备1选择配方 string selected1 = this.comboBox1.SelectedValue?.ToString(); if (string.IsNullOrEmpty(selected1) || selected1 == "-1") { _formulaList1 = null; } else { string[] arr1 = selected1.Split('|'); if (arr1.Length == 2) { string partNumber1 = arr1[0]; string partName1 = arr1[1]; _formulaList1 = _formula.AsEnumerable() .Where(r => r.Field("part_number") == partNumber1 && r.Field("part_name") == partName1) .ToList(); } else { _formulaList1 = null; } } // 设备2选择配方 string selected2 = this.comboBox2.SelectedValue?.ToString(); if (string.IsNullOrEmpty(selected2) || selected2 == "-1") { _formulaList2 = null; } else { string[] arr2 = selected2.Split('|'); if (arr2.Length == 2) { string partNumber2 = arr2[0]; string partName2 = arr2[1]; _formulaList2 = _formula.AsEnumerable() .Where(r => r.Field("part_number") == partNumber2 && r.Field("part_name") == partName2) .ToList(); } else { _formulaList2 = null; } } double seconds; if (!double.TryParse(txtLUploadPL.Text, out seconds) || seconds <= 0) { seconds = 10; //控制上传间隔,默认10秒。 } // 立即上传一次 await MqttYiDaUpload(); _timer = new System.Timers.Timer(seconds * 1000); _timer.Elapsed += async (s, evt) => { await MqttYiDaUpload(); }; _timer.AutoReset = true; // 是否重复 _timer.Enabled = true; // 开始计时 } catch (Exception ex) { this.button2.Enabled = true; AppendLog($"上传宜搭失败:{ex.Message}"); } } /// /// 刷新配方 /// /// /// private void btnRefresh_Click(object sender, EventArgs e) { LoadFormula(); } /// /// 中止上传按钮 /// /// /// private void button3_Click(object sender, EventArgs e) { this.button2.Enabled = true; if (this._timer != null) { this._timer.Enabled = false; } AppendLog("停止上传数据"); MessageBox.Show("待程序完成后,将停止自动上传", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } /// /// 刷新秒数 /// /// /// private void txtLUploadPL_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)Keys.Back) { e.Handled = false; return; } if (char.IsDigit(e.KeyChar)) { e.Handled = false; } else if (e.KeyChar == '.' && !txtLUploadPL.Text.Contains(".")) { e.Handled = false; } else { e.Handled = true; } } /// /// 更新时间 /// /// /// private void timer1_Tick(object sender, EventArgs e) { // 每秒更新一次时间显示 this.label4.Text = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"); } // ======================================== 工具方法 ======================================== // /// 获取Excel到Datatable中 /// /// Excel路径 /// 返回DataTable数据 private static DataTable GetExcel(string filePath) { IWorkbook iwkX; using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { iwkX = WorkbookFactory.Create(fs); fs.Close(); } //sheet DataTable dt = new DataTable(); for (int h = 0; h < iwkX.NumberOfSheets; h++) { ISheet sheet = iwkX.GetSheetAt(h); var rows = sheet.GetRowEnumerator(); bool isMove = rows.MoveNext(); //循环sheet if (isMove) { var Cols = (IRow)rows.Current; dt.TableName = sheet.SheetName; for (int i = 0; i < Cols.LastCellNum; i++) { string str = Cols.GetCell(i).ToString(); dt.Columns.Add(Cols.GetCell(i).ToString()); } while (rows.MoveNext()) { var row = (IRow)rows.Current; var dr = dt.NewRow(); for (int i = 0; i < row.LastCellNum; i++) { var cell = row.GetCell(i); if (cell == null) { dr[i] = ""; } else { string strdr = cell.ToString(); dr[i] = cell.ToString(); } } dt.Rows.Add(dr); } } } return dt; } /// /// 上传后续 /// /// /// private void _timer_Tick(object sender, ElapsedEventArgs e) { MqttYiDaUpload(); AppendLog("宜搭数据已上传!"); } /// /// 上传宜搭方法 /// private async Task MqttYiDaUpload() { // 1. 查询4个设备的最新MQTT数据 DataTable mqttData = await _buttonService.GetLatestMqttDataAsync(); if (mqttData == null || mqttData.Rows.Count == 0) { AppendLog("未查询到任何设备的MQTT数据,跳过上传"); return; } // 存储所有设备的Model(后续按part_number排序) List allMqttModels = new List(); List allYiDaModels = new List(); // 2. 遍历4个设备的每条数据,逐个参数生成Model foreach (DataRow row in mqttData.Rows) { string deviceCode = row["device_code"].ToString(); if (string.IsNullOrWhiteSpace(deviceCode)) { AppendLog($"发现空设备编码数据,跳过"); continue; } // ========== 设备基础配置 ========== List formulaList = null; string site = null; DeviceType deviceType = DeviceType.InjectionMoldingMachine; DataTable currentFormulaDic = null; // 按设备编码分配配置 switch (deviceCode) { case "device1": formulaList = _formulaList1; site = "1号注塑机"; deviceType = DeviceType.InjectionMoldingMachine; currentFormulaDic = _immFormulaDic; break; case "device2": formulaList = _formulaList2; site = "2号注塑机"; deviceType = DeviceType.InjectionMoldingMachine; currentFormulaDic = _immFormulaDic; break; case "device3": formulaList = _formulaList1; site = "1号温控器"; deviceType = DeviceType.TemperatureController; currentFormulaDic = _tcFormulaDic; break; case "device4": formulaList = _formulaList2; site = "2号温控器"; deviceType = DeviceType.TemperatureController; currentFormulaDic = _tcFormulaDic; break; default: AppendLog($"未知设备编码:{deviceCode},跳过该设备数据"); continue; } // 校验配方和字典表 if (formulaList == null || formulaList.Count == 0) { AppendLog($"设备{deviceCode}未选择配方,跳过该设备"); continue; } if (currentFormulaDic == null || currentFormulaDic.Rows.Count == 0) { AppendLog($"设备{deviceCode}字典表为空,跳过该设备"); continue; } // ========== 反序列化MQTT数据 ========== string receiveData = row["receive_data"].ToString(); if (string.IsNullOrWhiteSpace(receiveData)) { AppendLog($"设备{deviceCode}的receive_data为空,跳过该设备"); continue; } object dataObj = null; try { if (deviceType == DeviceType.InjectionMoldingMachine) { // device1/2:反序列化为注塑机模型 dataObj = JsonConvert.DeserializeObject(receiveData); } else { // device3/4:反序列化为温控器模型 dataObj = JsonConvert.DeserializeObject(receiveData); } } catch (Exception ex) { AppendLog($"设备{deviceCode}反序列化失败:{ex.Message},原始数据:{receiveData}"); continue; } // ========== 解析参数(注塑机多参数/温控器PV) ========== Dictionary paramDict = new Dictionary(); if (deviceType == DeviceType.InjectionMoldingMachine) { ImmDataModel immData = dataObj as ImmDataModel; paramDict = ParseImmParams(immData?.@params); } else { TcDataModel tcData = dataObj as TcDataModel; paramDict = ParseTcParams(tcData?.@params); // 仅解析PV参数 } if (paramDict.Count == 0) { AppendLog($"设备{deviceCode}无有效参数(注塑机参数为空/温控器PV为空),跳过该设备"); continue; } // ========== 逐个参数生成Model ========== foreach (var kvp in paramDict) { string paramCode = kvp.Key; // 原始参数编码(PV/Alarm/T1等) string paramValue = kvp.Value; // 参数值 // 1. 查字典表获取中文参数名 DataRow[] drs = currentFormulaDic.Select($"param_code = '{paramCode}'"); string paramName = paramCode; if (drs != null && drs.Length > 0) { paramName = drs[0]["param_name"].ToString(); } else { AppendLog($"设备{deviceCode}参数{paramCode}无字典映射,使用原编码作为参数名"); } // 2. 匹配配方数据 DataRow drMatch = formulaList.FirstOrDefault(r => r.Field("parameter_name").Equals(paramName, StringComparison.OrdinalIgnoreCase) || r.Field("parameter_name").Equals(paramCode, StringComparison.OrdinalIgnoreCase)); if (drMatch == null) { AppendLog($"设备{deviceCode}配方中未找到参数[{paramCode}/{paramName}],跳过该参数"); continue; } // 3. 生成MqttModel(单个参数) MqttModel mqttModel = new MqttModel { SupplierCode = drMatch["supplier_code"].ToString(), SupplierName = drMatch["supplier_name"].ToString(), VehicleModel = drMatch["vehicle_model"].ToString(), PartNumber = drMatch["part_number"].ToString(), // 排序依据 PartName = drMatch["part_name"].ToString(), Configuration = drMatch["configuration"]?.ToString() ?? "", ParameterName = paramName, ParameterValue = paramValue, ToleranceLower = drMatch["tolerance_lower"].ToString(), ToleranceUpper = drMatch["tolerance_upper"].ToString(), IsQualification = GetQualificationResult(deviceType, paramName, paramValue, drMatch), LeaderPart = drMatch["leader_part"].ToString(), WorkStation = site, LeaderOutProtection = drMatch["leader_out_protection"].ToString() }; // 4. 生成YiDaModel(单个参数) YiDaModel yiDaModel = new YiDaModel { textField_mha98neu = drMatch["supplier_code"].ToString(), textField_mha98nev = drMatch["supplier_name"].ToString(), textField_mha98new = drMatch["vehicle_model"].ToString(), textField_mha98nex = drMatch["part_number"].ToString(), // 排序依据 textField_mha98ney = drMatch["part_name"].ToString(), textField_mha98nez = drMatch["configuration"]?.ToString() ?? "", textField_mha98nf0 = site, textField_mha98nf1 = paramName, textField_mhx44i2i = paramValue, textField_mhx44i2j = drMatch["tolerance_lower"].ToString(), textField_mhx44i2k = drMatch["tolerance_upper"].ToString(), textField_mha98nf5 = mqttModel.IsQualification, textField_mhlvt8ht = DateTimeOffset.Now.ToUnixTimeMilliseconds(), textField_mha98nf7 = drMatch["leader_part"].ToString(), textField_mha98nfh = drMatch["leader_out_protection"].ToString(), // 其他字段赋空值 textField_mha98nf8 = "", textField_mha98nf9 = "", textField_mha98nfa = "", textField_mha98nfb = "", textField_mha98nfc = "", textField_mha98nfd = "", imageField_mii5s85z = "", textField_mha98nff = "", textField_mha98nfg = "" }; // 5. 添加到总列表 allMqttModels.Add(mqttModel); allYiDaModels.Add(yiDaModel); } } // ========== 核心:按part_number排序(升序) ========== if (allMqttModels.Count > 0 && allYiDaModels.Count > 0) { // 按part_number升序排序(保证两个列表排序一致) var sortedModels = allMqttModels .Select((mqtt, index) => new { Mqtt = mqtt, YiDa = allYiDaModels[index] }) .OrderBy(item => item.Mqtt.PartNumber) // 按part_number升序 .ToList(); // 重新赋值排序后的列表 allMqttModels = sortedModels.Select(item => item.Mqtt).ToList(); allYiDaModels = sortedModels.Select(item => item.YiDa).ToList(); AppendLog($"所有参数Model已按part_number排序,共{allMqttModels.Count}条数据"); } // ========== 统一上传排序后的Model ========== if (allMqttModels.Count > 0 && allYiDaModels.Count > 0) { string token = _uploadService.GetDingDingToken(); if (string.IsNullOrEmpty(token)) { if (InvokeRequired) { BeginInvoke(new Action(() => MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret"))); } else { MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret"); } return; } // WinForm跨线程安全上传 if (InvokeRequired) { BeginInvoke( new Action, List, MainForm>(_uploadService .UploadDatabaseDataToYiDaWithLogging), token, allYiDaModels, allMqttModels, this); } else { _uploadService.UploadDatabaseDataToYiDaWithLogging(token, allYiDaModels, allMqttModels, this); } AppendLog($"成功上传{allMqttModels.Count}条参数数据(按part_number排序)"); } else { AppendLog("无有效参数Model可上传"); } } /// /// 辅助方法:获取合格判定结果 /// private string GetQualificationResult(DeviceType deviceType, string paramName, string paramValue, DataRow drMatch) { try { if (deviceType == DeviceType.InjectionMoldingMachine) { // 注塑机特殊参数判断 if (paramName == "报警信息" || paramName == "Alarm") { return string.Equals(paramValue, "无错误", StringComparison.Ordinal) ? "合格" : "不合格"; } else if (paramName == "开模总数实时" || paramName == "托模次数" || paramName == "ProductCounts") { return "合格"; } } // 数值型参数公差判断(注塑机普通参数/温控器PV) if (float.TryParse(paramValue, out float val) && float.TryParse(drMatch["tolerance_lower"].ToString(), out float lower) && float.TryParse(drMatch["tolerance_upper"].ToString(), out float upper)) { return (val >= lower && val <= upper) ? "合格" : "不合格"; } return "不合格"; } catch { return "不合格"; } } /// /// 解析注塑机模型参数 /// /// /// private Dictionary ParseImmParams(ImmParamsModel paramsModel) { Dictionary paramDict = new Dictionary(); if (paramsModel == null) return paramDict; AddParamIfNotEmpty(paramDict, "Alarm", paramsModel.Alarm); AddParamIfNotEmpty(paramDict, "ProductCounts", paramsModel.ProductCounts); AddParamIfNotEmpty(paramDict, "CT", paramsModel.CT); AddParamIfNotEmpty(paramDict, "T1", paramsModel.T1); AddParamIfNotEmpty(paramDict, "T2", paramsModel.T2); AddParamIfNotEmpty(paramDict, "T3", paramsModel.T3); AddParamIfNotEmpty(paramDict, "T4", paramsModel.T4); AddParamIfNotEmpty(paramDict, "T5", paramsModel.T5); AddParamIfNotEmpty(paramDict, "IP1", paramsModel.IP1); AddParamIfNotEmpty(paramDict, "IP2", paramsModel.IP2); AddParamIfNotEmpty(paramDict, "IP3", paramsModel.IP3); AddParamIfNotEmpty(paramDict, "IP4", paramsModel.IP4); AddParamIfNotEmpty(paramDict, "IP5", paramsModel.IP5); AddParamIfNotEmpty(paramDict, "IV1", paramsModel.IV1); AddParamIfNotEmpty(paramDict, "IV2", paramsModel.IV2); AddParamIfNotEmpty(paramDict, "IV3", paramsModel.IV3); AddParamIfNotEmpty(paramDict, "IV4", paramsModel.IV4); AddParamIfNotEmpty(paramDict, "IV5", paramsModel.IV5); AddParamIfNotEmpty(paramDict, "ITT", paramsModel.ITT); AddParamIfNotEmpty(paramDict, "PP1", paramsModel.PP1); AddParamIfNotEmpty(paramDict, "PP2", paramsModel.PP2); AddParamIfNotEmpty(paramDict, "PP3", paramsModel.PP3); AddParamIfNotEmpty(paramDict, "PV1", paramsModel.PV1); AddParamIfNotEmpty(paramDict, "PV2", paramsModel.PV2); AddParamIfNotEmpty(paramDict, "PV3", paramsModel.PV3); AddParamIfNotEmpty(paramDict, "PT1", paramsModel.PT1); AddParamIfNotEmpty(paramDict, "PT2", paramsModel.PT2); AddParamIfNotEmpty(paramDict, "PT3", paramsModel.PT3); AddParamIfNotEmpty(paramDict, "CC", paramsModel.CC); return paramDict; } /// /// 解析温控器模型参数 /// /// /// private Dictionary ParseTcParams(TcParamsModel paramsModel) { Dictionary paramDict = new Dictionary(); if (paramsModel == null) return paramDict; // 仅解析PV参数(核心需求) AddParamIfNotEmpty(paramDict, "PV", paramsModel.PV); return paramDict; } /// /// 判断参数是否为空 /// /// /// /// private void AddParamIfNotEmpty(Dictionary dict, string key, string value) { if (!string.IsNullOrWhiteSpace(value)) { dict[key] = value.Trim(); } } private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { } // ======================================== 日志相关方法 ======================================== /// /// 接收 MQTT 消息后,将消息日志显示到 UI 控件上 /// /// private void OnMqttMessage(string msg) { if (InvokeRequired) { BeginInvoke(new Action(OnMqttMessage), msg); return; } AppendLog($"收到消息: {msg}"); } /// /// 写入日志到文本框并保存到文件 /// public void AppendLog(string message) { try { if (InvokeRequired) { BeginInvoke(new Action(AppendLog), message); return; } // 格式化带时间戳的日志 string timeStamped = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\r\n"; // 保存到日志文件 SaveToLogFile(timeStamped); // 添加到文本框并限制行数 textBoxLog.AppendText(timeStamped); LimitLogLines(500); } catch (Exception ex) { // 可以在这里添加错误处理,例如写入错误日志 // MessageBox.Show($"日志处理错误: {ex.Message}"); } } /// /// 将日志保存到文件 /// private void SaveToLogFile(string logContent) { try { UpdateLogFile(); // 确保使用当天的日志文件 // 追加写入日志,使用UTF8编码避免中文乱码 File.AppendAllText(_currentLogFile, logContent, System.Text.Encoding.UTF8); } catch (Exception ex) { // 文件写入错误处理 // MessageBox.Show($"日志文件写入错误: {ex.Message}"); } } /// /// 限制日志文本框的最大行数 /// private void LimitLogLines(int maxLines) { // 按换行符分割所有行 string[] lines = textBoxLog.Text.Split(new[] { "\r\n" }, StringSplitOptions.None); // 如果超过最大行数,只保留最后maxLines行 if (lines.Length > maxLines) { // 计算需要保留的起始索引 int startIndex = lines.Length - maxLines; string[] newLines = new string[maxLines]; Array.Copy(lines, startIndex, newLines, 0, maxLines); // 重新设置文本框内容并保持滚动到最底部 textBoxLog.Text = string.Join("\r\n", newLines); // 滚动到最新内容 textBoxLog.SelectionStart = textBoxLog.TextLength; textBoxLog.ScrollToCaret(); } else { // 未超过限制时也滚动到最底部 textBoxLog.SelectionStart = textBoxLog.TextLength; textBoxLog.ScrollToCaret(); } } } }