2026-02-05 14:45:33 +08:00

2178 lines
92 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using YiDa_WinForm.Config;
using YiDa_WinForm.Model;
using YiDa_WinForm.Service;
using YiDa_WinForm.Utils;
using NPOI.SS.UserModel;
namespace YiDa_WinForm
{
public partial class MainForm : Form
{
/// <summary>
/// 依赖注入
/// </summary>
private readonly MqttClientService _mqttService;
private readonly YiDaUploadService _uploadService;
private readonly ButtonOperationService _buttonService;
/// <summary>
/// 日志文件相关路径
/// </summary>
private readonly string _logDirectory = "log";
private string _currentLogFile;
private DateTime _lastLogDate = DateTime.MinValue;
/// <summary>
/// 配方配置
/// </summary>
private DataTable _formulas; // 原始配方表(含多行,未去重)
private DataTable _dtMqttDic; // MQTT 参数字典表
private List<DataRow> _formulasListOne; // 设备1选择的配方多行
private List<DataRow> _formulasListTwo; // 设备2选择的配方多行
/// <summary>
/// 定时任务
/// </summary>
// 通用上传定时器
private System.Timers.Timer _timer;
// 设备1
private System.Timers.Timer _scrapAlarmTimerOne; // 报废上传定时任务
private int _lastScrapIntervalOne = 60; // 报废间隔
private static bool _isScrapImageUploadedOne = false; // 报废图片是否上传
private static bool _isScrapImageUploadedInCycleOne = false;
private System.Timers.Timer _rodAlarmTimerOne; // 连杆测试定时任务
private int _lastRodIntervalOne = 60; // 连杆间隔
private static bool _isRodImageUploadedOne = false; // 连杆图片是否上传
private static bool _isRodUploadedInCycleOne = false;
private static bool _isScrapImageWorkNoticeOne = false; // 报废图片工作通知标记
private static bool _isRodImageWorkNoticeOne = false; // 连杆图片工作通知标记
// 设备2
private System.Timers.Timer _scrapAlarmTimerTwo; // 报废上传定时任务
private int _lastScrapIntervalTwo = 60; // 报废间隔
private static bool _isScrapImageUploadedTwo = false; // 报废图片是否上传
private static bool _isScrapImageUploadedInCycleTwo = false;
private System.Timers.Timer _rodAlarmTimerTwo; // 连杆测试定时任务
private int _lastRodIntervalTwo = 60; // 连杆间隔
private static bool _isRodImageUploadedTwo = false; // 连杆图片是否上传
private static bool _isRodUploadedInCycleTwo = false;
private static bool _isScrapImageWorkNoticeTwo = false; // 报废图片工作通知标记
private static bool _isRodImageWorkNoticeTwo = false; // 连杆图片工作通知标记
// 连杆工作时间配置
private System.Timers.Timer _morningRodAlarmTimerOne; // 早上检查点定时器(工作开始+30分钟
private System.Timers.Timer _eveningRodAlarmTimerOne; // 晚上检查点定时器(工作结束-30分钟
private System.Timers.Timer _morningRodAlarmTimerTwo; // 早上检查点定时器(工作开始+30分钟
private System.Timers.Timer _eveningRodAlarmTimerTwo; // 晚上检查点定时器(工作结束-30分钟
private bool _isMorningRodUploadedOne = false; // 早上标记工作开始后30分钟前是否上传
private bool _isEveningRodUploadedOne = false; // 晚上标记工作结束前30分钟前是否上传
private bool _isMorningRodUploadedTwo = false; // 早上标记工作开始后30分钟前是否上传
private bool _isEveningRodUploadedTwo = false; // 晚上标记工作结束前30分钟前是否上传
public static TimeSpan _workStartTimeOne = new TimeSpan(9, 0, 0);
public static TimeSpan _workEndTimeOne = new TimeSpan(17, 30, 0);
private TimeSpan _morningCheckTimeOne; // 早上检查节点_workStartTime + 30分钟
private TimeSpan _eveningCheckTimeOne; // 晚上检查节点_workEndTime + 30分钟
public static TimeSpan _workStartTimeTwo = new TimeSpan(9, 0, 0);
public static TimeSpan _workEndTimeTwo = new TimeSpan(17, 30, 0);
private TimeSpan _morningCheckTimeTwo; // 早上检查节点_workStartTime + 30分钟
private TimeSpan _eveningCheckTimeTwo; // 晚上检查节点_workEndTime + 30分钟
// ======================================== 初始化相关方法 ========================================
public MainForm()
{
// 初始化设计器方法
InitializeComponent();
// 订阅日志事件
LogHelper.OnLogGenerated += LogHelper_OnLogGenerated;
// 初始化服务实例
_buttonService = new ButtonOperationService();
_mqttService = new MqttClientService();
_uploadService = new YiDaUploadService();
_buttonService.MessageReceived += (msg) => { LogHelper.AppendLog($"服务层消息:{msg}"); };
// 绑定回调事件
_mqttService.MessageReceived += OnMqttMessage;
_uploadService.ScrapUploadStatusChangedOne += OnScrapUploadStatusChangedOne;
_uploadService.ScrapUploadStatusChangedTwo += OnScrapUploadStatusChangedTwo;
_uploadService.RodUploadStatusChangedOne += OnRodUploadStatusChangedOne;
_uploadService.RodUploadStatusChangedTwo += OnRodUploadStatusChangedTwo;
// UI初始化配置
buttonDisconnect.Enabled = false;
// 初始化日志目录
InitializeLogDirectory();
// 初始化配方相关变量
_formulas = new DataTable();
_dtMqttDic = new DataTable();
_formulasListOne = new List<DataRow>();
_formulasListTwo = new List<DataRow>();
InitDailyRodDeviceOneCheckTimes();
InitDailyRodDeviceTwoCheckTimes();
}
/// <summary>
/// 初始化日志目录
/// </summary>
private void InitializeLogDirectory()
{
if (!Directory.Exists(_logDirectory))
{
Directory.CreateDirectory(_logDirectory);
}
UpdateLogFile();
}
/// <summary>
/// 更新当前日志文件(每日一个新文件)
/// </summary>
private void UpdateLogFile()
{
DateTime today = DateTime.Now.Date;
if (today != _lastLogDate)
{
_lastLogDate = today;
_currentLogFile = Path.Combine(_logDirectory, $"log_{today:yyyyMMdd}.txt");
}
}
/// <summary>
/// 加载MainForm信息
/// </summary>
private async void Form1_Load(object sender, EventArgs e)
{
try
{
MakePanelRound(panelLed);
await InitFormulas(); // 移植:加载配方
InitMqttDic(); // 移植加载MQTT字典
timer1.Enabled = true;
// 初始化时,若下拉框未选择,禁用所有按钮
UpdateDevice1ButtonStates();
UpdateDevice2ButtonStates();
// 绑定下拉框的选择变化事件
comboBox1.SelectedIndexChanged += comboBox1_SelectedIndexChanged;
comboBox2.SelectedIndexChanged += comboBox2_SelectedIndexChanged;
}
catch (Exception ex)
{
LogHelper.AppendLog($"初始化失败:{ex.Message}");
}
}
/// <summary>
/// 将指定 Panel 变成圆形
/// </summary>
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);
}
// ======================================== 配方核心方法 ========================================
/// <summary>
/// 查询配方并去重
/// </summary>
private async Task InitFormulas()
{
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);
_formulas = 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();
}
};
}
}
/// <summary>
/// 初始化MQTT字典
/// </summary>
private async void InitMqttDic()
{
DataTable dt = await _buttonService.InitMqttDic();
if (dt != null)
{
_dtMqttDic = dt;
}
}
// ======================================== 按钮相关方法 ========================================
/// <summary>
/// 网关连接按钮
/// </summary>
private async void buttonConnect_Click(object sender, EventArgs e)
{
try
{
LogHelper.AppendLog("正在连接MQTT服务器...");
await _mqttService.MqttClientStartAsync();
LogHelper.AppendLog("连接成功!");
panelLed.BackColor = Color.Green;
toolStripStatusLabel1.Text = "已连接";
buttonConnect.Enabled = false;
buttonDisconnect.Enabled = true;
}
catch (Exception ex)
{
panelLed.BackColor = Color.Red;
LogHelper.AppendLog($"连接失败:{ex.Message}");
toolStripStatusLabel1.Text = "连接失败";
}
}
/// <summary>
/// 网关断开按钮
/// </summary>
private async void buttonDisconnect_Click(object sender, EventArgs e)
{
try
{
await _mqttService.MqttClientStopAsync();
LogHelper.AppendLog("已断开连接");
panelLed.BackColor = Color.Red;
toolStripStatusLabel1.Text = "未连接";
buttonConnect.Enabled = true;
buttonDisconnect.Enabled = false;
}
catch (Exception ex)
{
LogHelper.AppendLog($"无法断开连接:{ex.Message}");
}
}
/// <summary>
/// 配方导入按钮
/// </summary>
private async void button1_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "Excel文件|*.xlsx;*.xls";
if (openFile.ShowDialog() != DialogResult.OK)
{
return;
}
// 显示加载提示
this.Cursor = Cursors.WaitCursor;
LogHelper.AppendLog("正在导入配方,请稍候...");
string filePath = openFile.FileName;
// 优化:使用 Task.Run 异步执行不阻塞UI线程
DataTable excelDt = await Task.Run(() => ExcelHelper.GetExcel(filePath));
if (excelDt == null || excelDt.Rows.Count == 0)
{
MessageBox.Show("Excel解析后无有效数据不允许导入");
LogHelper.AppendLog("Excel无有效数据导入中止");
return;
}
// excel导入列
string[] requiredColumns = { "供应商代码", "供应商名称", "车型", "零件号", "零件名", "参数名" };
var missingColumns = requiredColumns.Where(col => !excelDt.Columns.Contains(col)).ToList();
if (missingColumns.Count > 0)
{
string msg = $"Excel缺少必要列{string.Join("", missingColumns)},导入失败";
MessageBox.Show(msg);
LogHelper.AppendLog(msg);
return;
}
// 异步执行批量插入
await _buttonService.SaveFormulaByExcel(excelDt);
// 异步刷新配方
await InitFormulas();
MessageBox.Show("配方信息已导入!");
LogHelper.AppendLog($"配方导入成功:{Path.GetFileName(filePath)},共{excelDt.Rows.Count}行");
}
catch (Exception ex)
{
LogHelper.AppendLog($"配方导入失败:{ex.Message}");
MessageBox.Show($"导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
// 恢复光标
this.Cursor = Cursors.Default;
}
}
/// <summary>
/// 上传宜搭按钮
/// </summary>
private async void button2_Click(object sender, EventArgs e)
{
try
{
if (toolStripStatusLabel1.Text != "已连接") //如果 MQTT 没连接,就不允许上传。
{
MessageBox.Show("请先连接MQTT网关");
return;
}
if (this.comboBox1.Text.Length == 0 && this.comboBox2.Text.Length == 0)
{
//如果两个下拉框都还没数据,说明没导入过配方,直接中止。
MessageBox.Show("请先导入配方信息,再刷新!");
return;
}
string selection = null;
// 判断是否选择有效下拉框
bool hasSelection1 = comboBox1.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox1.SelectedValue?.ToString());
bool hasSelection2 = comboBox2.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox2.SelectedValue?.ToString());
// 1. 重置静态上传标记(每次连接都重新开始判断)
_isScrapImageUploadedOne = false;
_isRodImageUploadedOne = false;
_isScrapImageUploadedTwo = false;
_isRodImageUploadedTwo = false;
_isRodImageWorkNoticeOne = false;
_isRodImageWorkNoticeTwo = false;
_isScrapImageWorkNoticeOne = false;
_isScrapImageWorkNoticeTwo = false;
LogHelper.AppendLog("重置上传标记:连杆未上传、报废未上传");
string selectedContent1 = string.Empty;
string selectedContent2 = string.Empty;
// 获取选中项的DataRowView增加判空
DataRowView drv1 = comboBox1.SelectedItem as DataRowView;
// 先判断drv1是否为null再访问字段
selectedContent1 = drv1 != null ? (drv1["part_name"]?.ToString()?.Trim() ?? string.Empty) : string.Empty;
// 获取选中项的DataRowView增加判空
DataRowView drv2 = comboBox2.SelectedItem as DataRowView;
// 先判断drv2是否为null再访问字段
selectedContent2 = drv2 != null ? (drv2["part_name"]?.ToString()?.Trim() ?? string.Empty) : string.Empty;
if (hasSelection1 && hasSelection2)
{
selection = "3";
StartScrapOneAlarmTimers();
StartScrapTwoAlarmTimers();
if (selectedContent1.Contains("连杆2") && selectedContent2.Contains("连杆2"))
{
StartRodOneAlarmTimers();
StartDailyRodDeviceOneFixedTimers();
StartRodTwoAlarmTimers();
StartDailyRodDeviceTwoFixedTimers();
}
}
else if (hasSelection1)
{
selection = "1";
StartScrapOneAlarmTimers();
if (selectedContent1.Contains("连杆2"))
{
StartRodOneAlarmTimers();
StartDailyRodDeviceOneFixedTimers();
}
}
else if (hasSelection2)
{
selection = "2";
StartScrapTwoAlarmTimers();
if (selectedContent2.Contains("连杆2"))
{
StartRodTwoAlarmTimers();
StartDailyRodDeviceTwoFixedTimers();
}
}
else
{
MessageBox.Show("未选择数据!");
}
this.button2.Enabled = false;
// 设备1选择配方
string selected1 = this.comboBox1.SelectedValue?.ToString();
if (string.IsNullOrEmpty(selected1) || selected1 == "-1")
{
_formulasListOne = null;
}
else
{
string[] arr1 = selected1.Split('|');
if (arr1.Length == 2)
{
string partNumber1 = arr1[0];
string partName1 = arr1[1];
_formulasListOne = _formulas.AsEnumerable()
.Where(r => r.Field<string>("part_number") == partNumber1
&& r.Field<string>("part_name") == partName1)
.ToList();
}
else
{
_formulasListOne = null;
}
}
// 设备2选择配方
string selected2 = this.comboBox2.SelectedValue?.ToString();
if (string.IsNullOrEmpty(selected2) || selected2 == "-1")
{
_formulasListTwo = null;
}
else
{
string[] arr2 = selected2.Split('|');
if (arr2.Length == 2)
{
string partNumber2 = arr2[0];
string partName2 = arr2[1];
_formulasListTwo = _formulas.AsEnumerable()
.Where(r => r.Field<string>("part_number") == partNumber2
&& r.Field<string>("part_name") == partName2)
.ToList();
}
else
{
_formulasListTwo = null;
}
}
double seconds;
if (!double.TryParse(txtLUploadPL.Text, out seconds) || seconds <= 0)
{
seconds = 10; //控制上传间隔默认10秒。
}
// 立即上传一次
await MqttYiDaUpload(selection, null);
_timer = new System.Timers.Timer(seconds * 1000);
_timer.Elapsed += async (s, evt) => { await MqttYiDaUpload(selection, null); };
_timer.AutoReset = true; // 是否重复
_timer.Enabled = true; // 开始计时
LogHelper.AppendLog($"自动上传已启动,间隔:{seconds}秒");
}
catch (Exception ex)
{
this.button2.Enabled = true;
LogHelper.AppendLog($"上传宜搭失败:{ex.Message}");
}
}
/// <summary>
/// 中止上传按钮
/// </summary>
private void button3_Click(object sender, EventArgs e)
{
button2.Enabled = true;
if (_timer != null)
{
_timer.Enabled = false;
_timer.Dispose();
_timer = null;
LogHelper.AppendLog("已停止自动上传数据");
MessageBox.Show("已停止自动上传", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
LogHelper.AppendLog("无运行中的上传定时器");
MessageBox.Show("当前无自动上传任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
StopDeviceOneAlarmTimers();
StopDeviceTwoAlarmTimers();
StopDailyRodDeviceOneFixedTimers();
StopDailyRodDeviceTwoFixedTimers();
}
/// <summary>
/// 报废上传凭证按钮设备1
/// </summary>
private async void button4_Click(object sender, EventArgs e)
{
// 传入间隔值
using (var scrapForm = new ScrapUploadForm(_lastScrapIntervalOne, "1"))
{
DialogResult result = scrapForm.ShowDialog(this);
if (result == DialogResult.OK)
{
// 1. 提取用户本次选择结果
int interval = scrapForm.ScrapTimedIntervalOne;
_lastScrapIntervalOne = interval;
// 2. 上传逻辑
bool isImageUploadSuccess = false;
using (OpenFileDialog openFile = new OpenFileDialog())
{
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadScrapDeviceOneAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("1", "device1");
MqttYiDaUpload("1", "device1");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isScrapImageUploadedOne = true;
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
}
/// <summary>
/// 报废上传凭证按钮设备2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button7_Click(object sender, EventArgs e)
{
// 传入间隔值
using (var scrapForm = new ScrapUploadForm(_lastScrapIntervalTwo, "2"))
{
DialogResult result = scrapForm.ShowDialog(this);
if (result == DialogResult.OK)
{
// 1. 提取用户本次选择结果
int interval = scrapForm.ScrapTimedIntervalTwo;
_lastScrapIntervalTwo = interval;
// 2. 上传逻辑
bool isImageUploadSuccess = false;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadScrapDeviceTwoAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("1", "device2");
MqttYiDaUpload("2", "device2");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isScrapImageUploadedTwo = true;
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
/// <summary>
/// 连杆测试上传按钮设备1
/// </summary>
private async void button5_Click(object sender, EventArgs e)
{
if (!button5.Enabled)
{
StopDeviceOneAlarmTimers();
return;
}
using (var selectForm = new RodTestUploadForm(_lastRodIntervalOne, "1"))
{
DialogResult result = selectForm.ShowDialog(this);
if (result == DialogResult.OK)
{
int interval = selectForm.RodTimedIntervalOne;
_lastRodIntervalOne = interval;
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
bool isImageUploadSuccess = false;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadRodTestDeviceOneAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device1");
MqttYiDaUpload("2", "device1");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedOne = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeOne && timeOfDay < _morningCheckTimeOne)
{
_isMorningRodUploadedOne = true;
}
if (timeOfDay > _workEndTimeOne || timeOfDay < _eveningCheckTimeOne)
{
_isEveningRodUploadedOne = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
/// <summary>
/// 配置按钮设备1
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button6_Click(object sender, EventArgs e)
{
try
{
if (!button6.Enabled)
{
StopDailyRodDeviceOneFixedTimers();
return;
}
using (var configForm = new WorkTimeConfigForm(_workStartTimeOne, _workEndTimeOne, "1"))
{
DialogResult result = configForm.ShowDialog(this);
if (result == DialogResult.OK)
{
_workStartTimeOne = configForm.WorkStartTimeOne;
_workEndTimeOne = configForm.WorkEndTimeOne;
// ========== 重新初始化早晚连杆检查节点 ==========
InitDailyRodDeviceOneCheckTimes();
// ========== 如果网关已连接,重启早晚固定检查定时器 ==========
if (toolStripStatusLabel1.Text == "已连接")
{
StartDailyRodDeviceOneFixedTimers();
}
LogHelper.AppendLog($"工作时间配置更新:{_workStartTimeOne:hh\\:mm} - {_workEndTimeOne:hh\\:mm}");
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
bool isImageUploadSuccess = true;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess =
await _uploadService.UploadRodTestDeviceOneAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device1");
MqttYiDaUpload("2", "device1");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedOne = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeOne && timeOfDay < _morningCheckTimeOne)
{
_isMorningRodUploadedOne = true;
}
if (timeOfDay > _workEndTimeOne || timeOfDay < _eveningCheckTimeOne)
{
_isEveningRodUploadedOne = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
catch (Exception ex)
{
LogHelper.AppendLog($"工作时间配置失败:{ex.Message}");
MessageBox.Show($"配置失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 时间配置设备2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button9_Click(object sender, EventArgs e)
{
try
{
if (!button9.Enabled)
{
StopDailyRodDeviceTwoFixedTimers();
return;
}
using (var configForm = new WorkTimeConfigForm(_workStartTimeTwo, _workEndTimeTwo, "2"))
{
DialogResult result = configForm.ShowDialog(this);
if (result == DialogResult.OK)
{
_workStartTimeTwo = configForm.WorkStartTimeTwo;
_workEndTimeTwo = configForm.WorkEndTimeTwo;
// ========== 重新初始化早晚连杆检查节点 ==========
InitDailyRodDeviceTwoCheckTimes();
// ========== 如果网关已连接,重启早晚固定检查定时器 ==========
if (toolStripStatusLabel1.Text == "已连接")
{
StartDailyRodDeviceTwoFixedTimers();
}
LogHelper.AppendLog($"工作时间配置更新:{_workStartTimeTwo:hh\\:mm} - {_workEndTimeTwo:hh\\:mm}");
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
bool isImageUploadSuccess = true;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess =
await _uploadService.UploadRodTestDeviceTwoAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device2");
MqttYiDaUpload("2", "device2");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedTwo = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeTwo && timeOfDay < _morningCheckTimeTwo)
{
_isMorningRodUploadedTwo = true;
}
if (timeOfDay > _workEndTimeTwo || timeOfDay < _eveningCheckTimeTwo)
{
_isEveningRodUploadedTwo = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
catch (Exception ex)
{
LogHelper.AppendLog($"工作时间配置失败:{ex.Message}");
MessageBox.Show($"配置失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 连杆测试上传按钮设备2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button8_Click(object sender, EventArgs e)
{
if (!button8.Enabled)
{
StopDeviceTwoAlarmTimers();
return;
}
using (var selectForm = new RodTestUploadForm(_lastRodIntervalTwo, "2"))
{
DialogResult result = selectForm.ShowDialog(this);
if (result == DialogResult.OK)
{
int interval = selectForm.RodTimedIntervalTwo;
_lastRodIntervalTwo = interval;
// 3. 上传逻辑
bool isImageUploadSuccess = false;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadRodTestDeviceTwoAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device2");
MqttYiDaUpload("2", "device2");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedTwo = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeTwo && timeOfDay < _morningCheckTimeTwo)
{
_isMorningRodUploadedTwo = true;
}
if (timeOfDay > _workEndTimeTwo || timeOfDay < _eveningCheckTimeTwo)
{
_isEveningRodUploadedTwo = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
/// <summary>
/// 关闭MainForm按钮
/// </summary>
private async void MainFormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (_timer != null)
{
_timer.Enabled = false;
_timer.Dispose();
}
StopDeviceOneAlarmTimers();
StopDeviceTwoAlarmTimers();
StopDailyRodDeviceOneFixedTimers();
StopDailyRodDeviceTwoFixedTimers();
await _mqttService.MqttClientStopAsync();
}
catch (Exception ex)
{
LogHelper.AppendLog($"关闭窗口失败:{ex.Message}");
}
}
/// <summary>
/// 刷新配方按钮
/// </summary>
private async void btnRefresh_Click(object sender, EventArgs e)
{
try
{
await InitFormulas();
InitMqttDic();
MessageBox.Show("配方已刷新!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
UpdateDevice1ButtonStates();
UpdateDevice2ButtonStates();
}
catch (Exception ex)
{
LogHelper.AppendLog($"刷新配方失败:{ex.Message}");
MessageBox.Show($"刷新配方失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 上传间隔输入框校验
/// </summary>
private void txtLUploadPL_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Back)
{
e.Handled = false;
return;
}
if (char.IsDigit(e.KeyChar) || (e.KeyChar == '.' && !txtLUploadPL.Text.Contains(".")))
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}
/// <summary>
/// 更新时间显示
/// </summary>
private void timer1_Tick(object sender, EventArgs e)
{
label4.Text = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
}
// ======================================== 事件绑定 ========================================
// 设备1下拉框选择变化事件
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateDevice1ButtonStates();
}
// 设备2下拉框选择变化事件
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateDevice2ButtonStates();
}
// 更新设备1对应按钮的状态
private void UpdateDevice1ButtonStates()
{
// 判断下拉框是否有有效选择
bool hasSelection = comboBox1.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox1.SelectedValue?.ToString());
// 判断是否选中连杆2
string selectedContent = string.Empty;
if (hasSelection)
{
// 获取选中项的DataRowView
DataRowView drv = comboBox1.SelectedItem as DataRowView;
// 取零件名part_name字段这是你的配方表中实际存储"连杆2"的字段
selectedContent = drv["part_name"]?.ToString()?.Trim() ?? string.Empty;
}
// 判断是否包含"连杆2"
button6.Enabled = hasSelection && selectedContent.Contains("连杆2");
button5.Enabled = hasSelection && selectedContent.Contains("连杆2");
button4.Enabled = hasSelection; // 报废上传凭证1
}
// 更新设备2对应按钮的状态
private void UpdateDevice2ButtonStates()
{
bool hasSelection = comboBox2.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox2.SelectedValue?.ToString());
// 判断是否选中连杆2
string selectedContent = string.Empty;
if (hasSelection)
{
// 获取选中项的DataRowView
DataRowView drv = comboBox2.SelectedItem as DataRowView;
// 取零件名part_name字段这是你的配方表中实际存储"连杆2"的字段
selectedContent = drv["part_name"]?.ToString()?.Trim() ?? string.Empty;
}
// 判断是否包含"连杆2"
button9.Enabled = hasSelection && selectedContent.Contains("连杆2");
button8.Enabled = hasSelection && selectedContent.Contains("连杆2");
button7.Enabled = hasSelection; // 报废上传凭证2
}
// ======================================== 定时报警核心方法 ========================================
/// <summary>
/// 启动报废定时任务前30分钟上传
/// </summary>
private void StartScrapOneAlarmTimers()
{
// 设备1
if (_scrapAlarmTimerOne != null)
{
_scrapAlarmTimerOne.Enabled = false;
_scrapAlarmTimerOne.Dispose();
}
int scrapIntervalMsOne = _lastScrapIntervalOne * 60 * 1000; // 分钟 → 毫秒
_scrapAlarmTimerOne = new System.Timers.Timer(scrapIntervalMsOne);
_scrapAlarmTimerOne.AutoReset = false; // 关键:一次性执行,不循环
_scrapAlarmTimerOne.Elapsed += ScrapAlarmTimerOne_Elapsed;
_scrapAlarmTimerOne.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备1报废上传定时已启动若{_lastScrapIntervalOne}分钟内未上传凭证,将触发钉钉报警");
}
private void StartScrapTwoAlarmTimers()
{
// 设备2
if (_scrapAlarmTimerTwo != null)
{
_scrapAlarmTimerTwo.Enabled = false;
_scrapAlarmTimerTwo.Dispose();
}
int scrapIntervalMsTwo = _lastScrapIntervalTwo * 60 * 1000; // 分钟 → 毫秒
_scrapAlarmTimerTwo = new System.Timers.Timer(scrapIntervalMsTwo);
_scrapAlarmTimerTwo.AutoReset = false; // 关键:一次性执行,不循环
_scrapAlarmTimerTwo.Elapsed += ScrapAlarmTimerTwo_Elapsed;
_scrapAlarmTimerTwo.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备1报废上传定时已启动若{_lastScrapIntervalTwo}分钟内未上传凭证,将触发钉钉报警");
}
/// <summary>
/// 启动连杆定时任务前30分钟上传
/// </summary>
private void StartRodOneAlarmTimers()
{
// 设备1
if (_rodAlarmTimerOne != null)
{
_rodAlarmTimerOne.Enabled = false;
_rodAlarmTimerOne.Dispose();
}
int rodIntervalMsOne = _lastRodIntervalOne * 60 * 1000; // 分钟 → 毫秒
_rodAlarmTimerOne = new System.Timers.Timer(rodIntervalMsOne);
_rodAlarmTimerOne.AutoReset = false; // 关键:一次性执行,不循环
_rodAlarmTimerOne.Elapsed += RodAlarmTimerOne_Elapsed;
_rodAlarmTimerOne.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备1连杆定时已启动若{_lastRodIntervalOne}分钟内未上传凭证,将触发钉钉报警");
}
private void StartRodTwoAlarmTimers()
{
// 设备2
if (_rodAlarmTimerTwo != null)
{
_rodAlarmTimerTwo.Enabled = false;
_rodAlarmTimerTwo.Dispose();
}
int scrapIntervalMsTwo = _lastRodIntervalTwo * 60 * 1000; // 分钟 → 毫秒
_rodAlarmTimerTwo = new System.Timers.Timer(scrapIntervalMsTwo);
_rodAlarmTimerTwo.AutoReset = false; // 关键:一次性执行,不循环
_rodAlarmTimerTwo.Elapsed += RodAlarmTimerTwo_Elapsed;
_rodAlarmTimerTwo.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备2连杆上传定时已启动若{_lastRodIntervalTwo}分钟内未上传凭证,将触发钉钉报警");
}
/// <summary>
/// 启动连杆定时任务设备1早晚检查
/// </summary>
private void StartDailyRodDeviceOneFixedTimers()
{
// 先停止并释放原有定时器(防止重复启动)
StopDailyRodDeviceOneFixedTimers();
// 重置早晚标记
ResetDailyRodMarks();
DateTime now = DateTime.Now;
DayOfWeek weekEnum = now.DayOfWeek;
if (weekEnum.Equals(DayOfWeek.Monday))
{
_morningCheckTimeOne = new TimeSpan(10, 30, 0);
_workStartTimeOne = new TimeSpan(10, 0, 0);
}
// 1. 启动早上检查定时器(若目标时间未过)
long morningDelayMs = CalculateDelayToTargetTime(_morningCheckTimeOne, "morning");
if (morningDelayMs > 0 && _morningCheckTimeOne > _workStartTimeOne)
{
_morningRodAlarmTimerOne = new System.Timers.Timer(morningDelayMs);
_morningRodAlarmTimerOne.AutoReset = false; // 一次性执行,不循环
_morningRodAlarmTimerOne.Elapsed += MorningRodDeviceOneAlarmTimer_Elapsed;
_morningRodAlarmTimerOne.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"早上连杆检查定时器已启动:将在{_morningCheckTimeOne:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("早上检查节点已过当天当前时间,跳过早上连杆检查定时器启动");
_isMorningRodUploadedOne = true; // 标记为已检查,避免重复判断
}
// 2. 启动晚上检查定时器(若目标时间未过)
long eveningDelayMs = CalculateDelayToTargetTime(_eveningCheckTimeOne, "evening");
if (eveningDelayMs > 0 && _eveningCheckTimeOne > _workEndTimeOne)
{
_eveningRodAlarmTimerOne = new System.Timers.Timer(eveningDelayMs);
_eveningRodAlarmTimerOne.AutoReset = false; // 一次性执行,不循环
_eveningRodAlarmTimerOne.Elapsed += EveningRodDeviceOneAlarmTimer_Elapsed;
_eveningRodAlarmTimerOne.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"晚上连杆检查定时器已启动:将在{_eveningCheckTimeOne:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("晚上检查节点已过当天当前时间,跳过晚上连杆检查定时器启动");
_isEveningRodUploadedOne = true; // 标记为已检查,避免重复判断
}
}
/// <summary>
/// 启动连杆定时任务设备2早晚检查
/// </summary>
private void StartDailyRodDeviceTwoFixedTimers()
{
// 先停止并释放原有定时器(防止重复启动)
StopDailyRodDeviceTwoFixedTimers();
// 重置早晚标记
ResetDailyRodMarks();
DateTime now = DateTime.Now;
DayOfWeek weekEnum = now.DayOfWeek;
if (weekEnum.Equals(DayOfWeek.Monday))
{
_morningCheckTimeTwo = new TimeSpan(10, 30, 0);
_workStartTimeTwo = new TimeSpan(10, 0, 0);
}
// 1. 启动早上检查定时器(若目标时间未过)
long morningDelayMs = CalculateDelayToTargetTime(_morningCheckTimeTwo, "morning");
if (morningDelayMs > 0 && _morningCheckTimeTwo > _workStartTimeTwo)
{
_morningRodAlarmTimerTwo = new System.Timers.Timer(morningDelayMs);
_morningRodAlarmTimerTwo.AutoReset = false; // 一次性执行,不循环
_morningRodAlarmTimerTwo.Elapsed += MorningRodDeviceTwoAlarmTimer_Elapsed;
_morningRodAlarmTimerTwo.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"早上连杆检查定时器已启动:将在{_morningCheckTimeTwo:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("早上检查节点已过当天当前时间,跳过早上连杆检查定时器启动");
_isMorningRodUploadedTwo = true; // 标记为已检查,避免重复判断
}
// 2. 启动晚上检查定时器(若目标时间未过)
long eveningDelayMs = CalculateDelayToTargetTime(_eveningCheckTimeTwo, "evening");
if (eveningDelayMs > 0 && _eveningCheckTimeTwo > _workEndTimeTwo)
{
_eveningRodAlarmTimerTwo = new System.Timers.Timer(eveningDelayMs);
_eveningRodAlarmTimerTwo.AutoReset = false; // 一次性执行,不循环
_eveningRodAlarmTimerTwo.Elapsed += EveningRodDeviceTwoAlarmTimer_Elapsed;
_eveningRodAlarmTimerTwo.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"晚上连杆检查定时器已启动:将在{_eveningCheckTimeTwo:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("晚上检查节点已过当天当前时间,跳过晚上连杆检查定时器启动");
_isEveningRodUploadedTwo = true; // 标记为已检查,避免重复判断
}
}
/// <summary>
/// 终止所有报警定时任务并重置标记
/// </summary>
private void StopDeviceOneAlarmTimers()
{
// 终止报废定时
if (_scrapAlarmTimerOne != null)
{
_scrapAlarmTimerOne.Enabled = false;
_scrapAlarmTimerOne.Dispose();
_scrapAlarmTimerOne = null;
}
if (_scrapAlarmTimerTwo != null)
{
_scrapAlarmTimerTwo.Enabled = false;
_scrapAlarmTimerTwo.Dispose();
_scrapAlarmTimerTwo = null;
}
LogHelper.AppendLog("报废上传报警定时已终止");
// 重置静态上传标记
_isScrapImageUploadedOne = false;
_isRodImageUploadedOne = false;
}
/// <summary>
/// 终止所有报警定时任务并重置标记
/// </summary>
private void StopDeviceTwoAlarmTimers()
{
// 终止报废定时
if (_rodAlarmTimerOne != null)
{
_rodAlarmTimerOne.Enabled = false;
_rodAlarmTimerOne.Dispose();
_rodAlarmTimerOne = null;
}
if (_rodAlarmTimerTwo != null)
{
_rodAlarmTimerTwo.Enabled = false;
_rodAlarmTimerTwo.Dispose();
_rodAlarmTimerTwo = null;
}
LogHelper.AppendLog("连杆上传报警定时已终止");
// 重置静态上传标记
_isScrapImageUploadedTwo = false;
_isRodImageUploadedTwo = false;
}
/// <summary>
/// 跳过报废报警设备1
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ScrapAlarmTimerOne_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 使用静态标记 _isScrapImageUploaded 判断是否报警(核心:上传成功则不报警)
if (!_isScrapImageUploadedOne)
{
string alarmContent = $"报废上传超时未上传凭证!<br>当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false, "1")));
_isScrapImageWorkNoticeOne = true;
}
else
{
LogHelper.AppendLog($"报废上传{_lastScrapIntervalOne}分钟内已上传凭证,无需触发报警");
}
// 3. 销毁定时器,重置标记
_scrapAlarmTimerOne?.Dispose();
_scrapAlarmTimerOne = null;
_isScrapImageUploadedOne = false;
}
/// <summary>
/// 跳过报废报警设备2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ScrapAlarmTimerTwo_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 使用静态标记 _isScrapImageUploaded 判断是否报警(核心:上传成功则不报警)
if (!_isScrapImageUploadedTwo)
{
string alarmContent = $"报废上传超时未上传凭证!<br>当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false, "2")));
_isScrapImageWorkNoticeTwo = true;
}
else
{
LogHelper.AppendLog($"报废上传{_lastScrapIntervalTwo}分钟内已上传凭证,无需触发报警");
}
// 3. 销毁定时器,重置标记
_scrapAlarmTimerTwo?.Dispose();
_scrapAlarmTimerTwo = null;
_isScrapImageUploadedTwo = false;
}
/// <summary>
/// 跳过连杆报警设备1
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RodAlarmTimerOne_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 2. 使用静态标记 _isRodImageUploaded 判断是否报警
if (!_isRodImageUploadedOne)
{
string alarmContent = $"连杆测试超时未上传图片!<br>当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true, "1")));
_isRodImageWorkNoticeOne = true;
}
else
{
LogHelper.AppendLog($"连杆测试{_lastRodIntervalOne}分钟内已上传图片,无需触发报警");
}
// 3. 销毁定时器,重置标记
_rodAlarmTimerOne?.Dispose();
_rodAlarmTimerOne = null;
_isRodImageUploadedOne = false;
}
/// <summary>
/// 跳过连杆报警设备2
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RodAlarmTimerTwo_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 2. 使用静态标记 _isRodImageUploaded 判断是否报警
if (!_isRodImageUploadedTwo)
{
string alarmContent = $"连杆测试超时未上传图片!<br>当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true, "2")));
_isRodImageWorkNoticeTwo = true;
}
else
{
LogHelper.AppendLog($"连杆测试{_lastRodIntervalTwo}分钟内已上传图片,无需触发报警");
}
// 3. 销毁定时器,重置标记
_rodAlarmTimerTwo?.Dispose();
_rodAlarmTimerTwo = null;
_isRodImageUploadedTwo = false;
}
/// <summary>
/// 发送钉钉报警
/// </summary>
/// <param name="alarmContent"></param>
/// <param name="isRodAlarm"></param>
private async void SendDingDingAlarm(string alarmContent, bool isRodAlarm, string deviceCode)
{
try
{
LogHelper.AppendLog($"【钉钉报警】{(isRodAlarm ? "" : "")}{alarmContent}");
await _uploadService.SendDingDingTextAlarmAsync(alarmContent, deviceCode, isRodAlarm);
// if (deviceCode.Contains("1"))
// {
// _isScrapImageWorkNoticeOne = true;
// }
// else
// {
// _isScrapImageWorkNoticeTwo = true;
// }
// MessageBox.Show($"已发送钉钉{(isRodAlarm ? "连杆" : "报废")}报警:{alarmContent}", "报警提示", MessageBoxButtons.OK,
// MessageBoxIcon.Warning);
}
catch (Exception ex)
{
LogHelper.AppendLog($"{(isRodAlarm ? "" : "")}钉钉报警发送失败:{ex.Message}");
}
}
// ======================================== 上传状态回调方法(连杆+报废) ========================================
/// <summary>
/// 报废上传状态回调设备1
/// </summary>
private void OnScrapUploadStatusChangedOne(object sender, ScrapUploadStatusEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<object, ScrapUploadStatusEventArgs>(OnScrapUploadStatusChangedOne), sender, e);
return;
}
_isScrapImageUploadedInCycleOne = e.IsUploadSuccess;
// 上传成功时更新静态标记
if (e.IsUploadSuccess)
{
_isScrapImageUploadedOne = true;
LogHelper.AppendLog("设备1报废凭证上传成功");
}
LogHelper.AppendLog($"报废上传状态:{(e.IsUploadSuccess ? "" : "")},消息:{e.Message}");
}
/// <summary>
/// 报废上传状态回调设备2
/// </summary>
private void OnScrapUploadStatusChangedTwo(object sender, ScrapUploadStatusEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<object, ScrapUploadStatusEventArgs>(OnScrapUploadStatusChangedOne), sender, e);
return;
}
_isScrapImageUploadedInCycleTwo = e.IsUploadSuccess;
// 上传成功时更新静态标记
if (e.IsUploadSuccess)
{
_isScrapImageUploadedTwo = true;
LogHelper.AppendLog("设备2报废凭证上传成功");
}
LogHelper.AppendLog($"报废上传状态:{(e.IsUploadSuccess ? "" : "")},消息:{e.Message}");
}
/// <summary>
/// 连杆上传状态回调设备1
/// </summary>
private void OnRodUploadStatusChangedOne(object sender, RodUploadStatusEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<object, RodUploadStatusEventArgs>(OnRodUploadStatusChangedOne), sender, e);
return;
}
_isRodUploadedInCycleOne = e.IsUploadSuccess;
// 上传成功时更新静态标记
if (e.IsUploadSuccess)
{
_isRodImageUploadedOne = true;
LogHelper.AppendLog("连杆图片上传成功,标记为「已上传」,定时结束后将不触发报警");
// // ========== 保留:同步更新早晚连杆上传标记(未到节点时) ==========
// TimeSpan currentTime = DateTime.Now.TimeOfDay;
//
// // 早上节点前上传,更新早上标记
// if (currentTime < _morningCheckTime && !_isMorningRodUploaded)
// {
// _isMorningRodUploaded = true;
// LogHelper.AppendLog("连杆图片上传成功,同步标记:早上无需触发强制上传报警");
// }
// // 晚上节点前上传,更新晚上标记
// else if (currentTime >= _morningCheckTime && currentTime < _eveningCheckTime && !_isEveningRodUploaded)
// {
// _isEveningRodUploaded = true;
// LogHelper.AppendLog("连杆图片上传成功,同步标记:晚上无需触发强制上传报警");
// }
}
LogHelper.AppendLog($"连杆上传状态:{(e.IsUploadSuccess ? "" : "")},消息:{e.Message}");
}
/// <summary>
/// 连杆上传状态回调设备2
/// </summary>
private void OnRodUploadStatusChangedTwo(object sender, RodUploadStatusEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<object, RodUploadStatusEventArgs>(OnRodUploadStatusChangedTwo), sender, e);
return;
}
_isRodUploadedInCycleTwo = e.IsUploadSuccess;
// 上传成功时更新静态标记
if (e.IsUploadSuccess)
{
_isRodImageUploadedTwo = true;
LogHelper.AppendLog("连杆图片上传成功,标记为「已上传」,定时结束后将不触发报警");
// // ========== 保留:同步更新早晚连杆上传标记(未到节点时) ==========
// TimeSpan currentTime = DateTime.Now.TimeOfDay;
//
// // 早上节点前上传,更新早上标记
// if (currentTime < _morningCheckTime && !_isMorningRodUploaded)
// {
// _isMorningRodUploaded = true;
// LogHelper.AppendLog("连杆图片上传成功,同步标记:早上无需触发强制上传报警");
// }
// // 晚上节点前上传,更新晚上标记
// else if (currentTime >= _morningCheckTime && currentTime < _eveningCheckTime && !_isEveningRodUploaded)
// {
// _isEveningRodUploaded = true;
// LogHelper.AppendLog("连杆图片上传成功,同步标记:晚上无需触发强制上传报警");
// }
}
LogHelper.AppendLog($"连杆上传状态:{(e.IsUploadSuccess ? "" : "")},消息:{e.Message}");
}
/// <summary>
/// 初始化早晚连杆检查节点时间(基于配置的工作时间)
/// </summary>
private void InitDailyRodDeviceOneCheckTimes()
{
// 计算早上检查节点:工作开始时间 + 30分钟
_morningCheckTimeOne = _workStartTimeOne.Add(TimeSpan.FromMinutes(2));
// 计算晚上检查节点:工作结束时间 + 30分钟
_eveningCheckTimeOne = _workEndTimeOne.Add(TimeSpan.FromMinutes(2));
// 校验:防止晚上检查节点早于早上检查节点(避免用户配置异常)
if (_eveningCheckTimeOne <= _morningCheckTimeOne)
{
LogHelper.AppendLog("警告工作时间配置异常结束时间前30分钟早于开始时间后30分钟早晚连杆检查功能将失效");
}
}
/// <summary>
/// 初始化早晚连杆检查节点时间设备2
/// </summary>
private void InitDailyRodDeviceTwoCheckTimes()
{
// 计算早上检查节点:工作开始时间 + 2分钟
_morningCheckTimeTwo = _workStartTimeTwo.Add(TimeSpan.FromMinutes(2));
// 计算晚上检查节点:工作结束时间 + 2分钟
_eveningCheckTimeTwo = _workEndTimeTwo.Add(TimeSpan.FromMinutes(2));
// 校验:防止晚上检查节点早于早上检查节点(避免用户配置异常)
if (_eveningCheckTimeTwo <= _morningCheckTimeTwo)
{
LogHelper.AppendLog("警告工作时间配置异常结束时间前30分钟早于开始时间后30分钟早晚连杆检查功能将失效");
}
}
/// <summary>
/// 计算当前时间到目标检查节点的毫秒数(用于设置定时器延迟)
/// </summary>
/// <param name="targetTime">目标检查节点(当天的时间点)</param>
/// <returns>延迟毫秒数(若目标时间已过,返回-1</returns>
private long CalculateDelayToTargetTime(TimeSpan targetTime, string dayOrNight)
{
DateTime now = DateTime.Now;
// 拼接当天的目标日期时间
DateTime targetDateTime = new DateTime(now.Year, now.Month, now.Day,
targetTime.Hours, targetTime.Minutes, targetTime.Seconds);
TimeSpan delay;
if (targetDateTime <= now)
{
return -1;
}
// 计算延迟毫秒数
delay = targetDateTime - now;
return (long)delay.TotalMilliseconds;
}
/// <summary>
/// 停止设备1早晚连杆检查定时器
/// </summary>
private void StopDailyRodDeviceOneFixedTimers()
{
// 仅停止设备1的定时器
if (_morningRodAlarmTimerOne != null)
{
_morningRodAlarmTimerOne.Enabled = false;
_morningRodAlarmTimerOne.Dispose();
_morningRodAlarmTimerOne = null;
}
if (_eveningRodAlarmTimerOne != null)
{
_eveningRodAlarmTimerOne.Enabled = false;
_eveningRodAlarmTimerOne.Dispose();
_eveningRodAlarmTimerOne = null;
}
LogHelper.AppendLog("设备1早晚连杆检查定时器已停止");
}
/// <summary>
/// 停止设备2早晚连杆检查定时器
/// </summary>
private void StopDailyRodDeviceTwoFixedTimers()
{
// 仅停止设备2的定时器
if (_morningRodAlarmTimerTwo != null)
{
_morningRodAlarmTimerTwo.Enabled = false;
_morningRodAlarmTimerTwo.Dispose();
_morningRodAlarmTimerTwo = null;
}
if (_eveningRodAlarmTimerTwo != null)
{
_eveningRodAlarmTimerTwo.Enabled = false;
_eveningRodAlarmTimerTwo.Dispose();
_eveningRodAlarmTimerTwo = null;
}
LogHelper.AppendLog("设备2早晚连杆检查定时器已停止");
}
/// <summary>
/// 重置早晚连杆上传标记(每日启动定时器或网关断开时调用)
/// </summary>
private void ResetDailyRodMarks()
{
_isMorningRodUploadedOne = false;
_isEveningRodUploadedOne = false;
_isMorningRodUploadedTwo = false;
_isEveningRodUploadedTwo = false;
LogHelper.AppendLog("早晚连杆上传标记已重置:早上未上传、晚上未上传");
}
/// <summary>
/// 早上连杆检查定时器回调(工作开始+30分钟
/// </summary>
private void MorningRodDeviceOneAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// 1. 判断是否已上传,未上传则触发报警
if (!_isMorningRodUploadedOne)
{
DateTime now = DateTime.Now;
string morningAlarmContent =
$"【早上连杆强制上传报警】<br>当前时间{now:yyyy-MM-dd HH:mm:ss}<br>已超过工作开始后30分钟{_morningCheckTimeOne:hh\\:mm}<br>未上传连杆测试图片!";
this.Invoke(new Action(() => SendDingDingAlarm(morningAlarmContent, true, "1")));
_isRodImageWorkNoticeOne = true;
}
else
{
LogHelper.AppendLog($"早上{_morningCheckTimeOne:hh\\:mm}:连杆图片已上传,无需触发强制上传报警");
}
// 2. 销毁早上定时器,标记为已检查(避免重复触发)
_morningRodAlarmTimerOne?.Dispose();
_morningRodAlarmTimerOne = null;
_isMorningRodUploadedOne = true;
}
/// <summary>
/// 晚上连杆检查定时器回调(工作结束+30分钟
/// </summary>
private void EveningRodDeviceOneAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// 1. 判断是否已上传,未上传则触发报警
if (!_isEveningRodUploadedOne)
{
DateTime now = DateTime.Now;
string eveningAlarmContent =
$"【晚上连杆强制上传报警】<br>当前时间{now:yyyy-MM-dd HH:mm:ss}<br>已超过工作结束前30分钟{_eveningCheckTimeOne:hh\\:mm}<br>未上传连杆测试图片!";
this.Invoke(new Action(() => SendDingDingAlarm(eveningAlarmContent, true, "1")));
_isRodImageWorkNoticeOne = true;
}
else
{
LogHelper.AppendLog($"晚上{_eveningCheckTimeOne:hh\\:mm}:连杆图片已上传,无需触发强制上传报警");
}
// 2. 销毁晚上定时器,标记为已检查(避免重复触发)
_eveningRodAlarmTimerOne?.Dispose();
_eveningRodAlarmTimerOne = null;
_isEveningRodUploadedOne = true;
}
/// <summary>
/// 早上连杆检查定时器回调(工作开始+30分钟
/// </summary>
private void MorningRodDeviceTwoAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// 1. 判断是否已上传,未上传则触发报警
if (!_isMorningRodUploadedTwo)
{
DateTime now = DateTime.Now;
string morningAlarmContent =
$"【早上连杆强制上传报警】<br>当前时间{now:yyyy-MM-dd HH:mm:ss}<br>已超过工作开始后30分钟{_morningCheckTimeTwo:hh\\:mm}<br>未上传连杆测试图片!";
this.Invoke(new Action(() => SendDingDingAlarm(morningAlarmContent, true, "2")));
_isRodImageWorkNoticeTwo = true;
}
else
{
LogHelper.AppendLog($"早上{_morningCheckTimeTwo:hh\\:mm}:连杆图片已上传,无需触发强制上传报警");
}
// 2. 销毁早上定时器,标记为已检查(避免重复触发)
_morningRodAlarmTimerTwo?.Dispose();
_morningRodAlarmTimerTwo = null;
_isMorningRodUploadedTwo = true;
}
/// <summary>
/// 晚上连杆检查定时器回调(工作结束+30分钟
/// </summary>
private void EveningRodDeviceTwoAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// 1. 判断是否已上传,未上传则触发报警
if (!_isEveningRodUploadedTwo)
{
DateTime now = DateTime.Now;
string eveningAlarmContent =
$"【晚上连杆强制上传报警】<br>当前时间{now:yyyy-MM-dd HH:mm:ss}<br>已超过工作结束前30分钟{_eveningCheckTimeTwo:hh\\:mm}<br>未上传连杆测试图片!";
this.Invoke(new Action(() => SendDingDingAlarm(eveningAlarmContent, true, "2")));
_isRodImageWorkNoticeTwo = true;
}
else
{
LogHelper.AppendLog($"晚上{_eveningCheckTimeTwo:hh\\:mm}:连杆图片已上传,无需触发强制上传报警");
}
// 2. 销毁晚上定时器,标记为已检查(避免重复触发)
_eveningRodAlarmTimerTwo?.Dispose();
_eveningRodAlarmTimerTwo = null;
_isEveningRodUploadedTwo = true;
}
// ======================================== 辅助方法 ========================================
private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// 适配移植的配方变量,刷新选中配方
string selected1 = this.comboBox1.SelectedValue?.ToString();
string selected2 = this.comboBox2.SelectedValue?.ToString();
RefreshSelectedFormula(selected1, selected2);
// await MqttYiDaUpload();
}
/// <summary>
/// 刷新选中配方(辅助方法)
/// </summary>
private void RefreshSelectedFormula(string selected1, string selected2)
{
if (string.IsNullOrEmpty(selected1) || selected1 == "-1")
{
_formulasListOne = null;
return;
}
string[] arr1 = selected1.Split('|');
if (arr1.Length == 2)
{
string partNumber1 = arr1[0];
string partName1 = arr1[1];
_formulasListOne = _formulas.AsEnumerable()
.Where(r => r.Field<string>("part_number") == partNumber1
&& r.Field<string>("part_name") == partName1)
.ToList();
}
if (string.IsNullOrEmpty(selected2) || selected2 == "-1")
{
_formulasListTwo = null;
return;
}
string[] arr2 = selected2.Split('|');
if (arr2.Length == 2)
{
string partNumber2 = arr2[0];
string partName2 = arr2[1];
_formulasListTwo = _formulas.AsEnumerable()
.Where(r => r.Field<string>("part_number") == partNumber2
&& r.Field<string>("part_name") == partName2)
.ToList();
}
}
// ======================================== 核心上传方法 ========================================
private async Task MqttYiDaUpload(string selection, string device)
{
try
{
DataTable dtMqtt = await _buttonService.GetLatestMqttDataAsync(selection, device);
if (dtMqtt == null || dtMqtt.Rows.Count == 0)
{
LogHelper.AppendLog("未查询到MQTT设备数据跳过本次上传");
return;
}
LogHelper.AppendLog($"本次读取到{dtMqtt.Rows.Count}条MQTT设备数据");
foreach (DataRow dr in dtMqtt.Rows)
{
string deviceCode = dr["device_code"].ToString();
LogHelper.AppendLog($"开始处理设备[{deviceCode}]数据");
// ======================================== 使用移植的配方列表变量 ========================================
List<DataRow> formulaList = null;
string site = null;
if (string.Equals(deviceCode, "device1", StringComparison.Ordinal))
{
formulaList = _formulasListOne;
site = "1号设备";
}
else if (string.Equals(deviceCode, "device2", StringComparison.Ordinal))
{
formulaList = _formulasListTwo;
site = "2号设备";
}
if (formulaList == null || formulaList.Count == 0)
{
LogHelper.AppendLog($"设备[{deviceCode}]未选择配方,跳过");
continue;
}
string strMessage = dr["receive_data"].ToString();
if (string.IsNullOrEmpty(strMessage))
{
LogHelper.AppendLog($"设备[{deviceCode}]无有效receive_data跳过");
continue;
}
SQLDataModel data = JsonConvert.DeserializeObject<SQLDataModel>(strMessage);
if (data == null || data.@params == null)
{
LogHelper.AppendLog($"设备[{deviceCode}]反序列化后无params数据跳过");
continue;
}
Type paramsType = data.@params.GetType();
PropertyInfo[] properties = paramsType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
List<MqttModel> mqttLists = new List<MqttModel>();
List<YiDaModel> yidaLists = new List<YiDaModel>();
foreach (PropertyInfo prop in properties)
{
string paramCode = prop.Name;
object value = prop.GetValue(data.@params);
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
continue;
}
// ======================================== 使用移植的MQTT字典进行参数映射 ========================================
DataRow[] drs = _dtMqttDic.Select($"param_code = '" + paramCode + "'");
string paramName = paramCode;
if (drs != null && drs.Length > 0)
{
paramName = drs[0]["param_name"].ToString();
}
DataRow drMatch =
formulaList.FirstOrDefault(r => r.Field<string>("parameter_name") == paramName);
if (drMatch == null)
{
LogHelper.AppendLog($"设备[{deviceCode}]配方中未找到参数[{paramName}],跳过");
continue;
}
YiDaModel yidaNew = BuildYiDaModel(drMatch, paramName, value, site);
MqttModel mqttNew = BuildMqttModel(drMatch, paramName, value, site);
mqttLists.Add(mqttNew);
yidaLists.Add(yidaNew);
}
int len = Math.Min(mqttLists.Count, yidaLists.Count);
if (len > 0)
{
List<MqttModel> mqttLists1 = mqttLists.Take(len).ToList();
List<YiDaModel> yidaLists1 = yidaLists.Take(len).ToList();
string token = _uploadService.GetDingDingToken();
if (string.IsNullOrEmpty(token))
{
LogHelper.AppendLog("获取 token 失败,请检查 AppKey/AppSecret");
this.Invoke(new Action(() =>
{
MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret", "错误", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}));
return;
}
this.Invoke(new Action(() =>
{
_uploadService.UploadDatabaseDataToYiDaWithLogging(token, yidaLists1, mqttLists1, this);
}));
LogHelper.AppendLog($"设备[{deviceCode}]成功上传{len}条数据到宜搭");
}
else
{
LogHelper.AppendLog($"设备[{deviceCode}]无有效参数数据可上传");
}
}
}
catch (Exception ex)
{
LogHelper.AppendLog($"MqttYiDaUpload执行失败{ex.Message}");
this.Invoke(new Action(() =>
{
MessageBox.Show($"上传失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}));
}
}
private MqttModel BuildMqttModel(DataRow drMatch, string paramName, object value, string site)
{
MqttModel mqttNew = new MqttModel();
mqttNew.SupplierCode = drMatch["supplier_code"].ToString();
mqttNew.SupplierName = drMatch["supplier_name"].ToString();
mqttNew.VehicleModel = drMatch["vehicle_model"].ToString();
mqttNew.PartNumber = drMatch["part_number"].ToString();
mqttNew.PartName = drMatch["part_name"].ToString();
mqttNew.ParameterName = paramName;
mqttNew.ParameterValue = value.ToString();
mqttNew.ToleranceLower = drMatch["tolerance_lower"].ToString();
mqttNew.ToleranceUpper = drMatch["tolerance_upper"].ToString();
mqttNew.LeaderPart = drMatch["leader_part"].ToString();
mqttNew.WorkStation = site;
mqttNew.LeaderOutProtection = drMatch["leader_out_protection"].ToString();
mqttNew.IsQualification = JudgeQualification(paramName, value, drMatch, site);
return mqttNew;
}
private YiDaModel BuildYiDaModel(DataRow drMatch, string paramName, object value, string site)
{
YiDaModel yidaNew = new YiDaModel();
yidaNew.textField_mha98neu = drMatch["supplier_code"].ToString();
yidaNew.textField_mha98nev = drMatch["supplier_name"].ToString();
yidaNew.textField_mha98new = drMatch["vehicle_model"].ToString();
yidaNew.textField_mha98nex = drMatch["part_number"].ToString();
yidaNew.textField_mha98ney = drMatch["part_name"].ToString();
yidaNew.textField_mha98nf1 = paramName;
yidaNew.textField_mhx44i2i = value.ToString();
yidaNew.textField_mhx44i2j = drMatch["tolerance_lower"].ToString();
yidaNew.textField_mhx44i2k = drMatch["tolerance_upper"].ToString();
yidaNew.textField_mha98nf7 = drMatch["leader_part"].ToString();
yidaNew.textField_mha98nf0 = site;
yidaNew.textField_mha98nfh = drMatch["leader_out_protection"].ToString();
yidaNew.textField_mhlvt8ht =
Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))
.TotalMilliseconds);
yidaNew.textField_mha98nf5 = JudgeQualification(paramName, value, drMatch, site);
return yidaNew;
}
private string JudgeQualification(string paramName, object value, DataRow drMatch, string site)
{
if (paramName == "报警信息")
{
return string.Equals(value?.ToString(), "无错误", StringComparison.Ordinal) ? "合格" : "不合格";
}
else if (paramName == "报废图片" || paramName == "连杆测试")
{
bool isNoticeTriggered = false;
if (paramName == "报废图片")
{
isNoticeTriggered = (site == "1号设备" && _isScrapImageWorkNoticeOne)
|| (site == "2号设备" && _isScrapImageWorkNoticeTwo);
}
else if (paramName == "连杆测试")
{
isNoticeTriggered = (site == "1号设备" && _isRodImageWorkNoticeOne)
|| (site == "2号设备" && _isRodImageWorkNoticeTwo);
}
if (isNoticeTriggered)
{
// 重置对应设备的标记
if (paramName == "报废图片")
{
if (site == "1号设备") _isScrapImageWorkNoticeOne = false;
if (site == "2号设备") _isScrapImageWorkNoticeTwo = false;
}
else if (paramName == "连杆测试")
{
if (site == "1号设备") _isRodImageWorkNoticeOne = false;
if (site == "2号设备") _isRodImageWorkNoticeTwo = false;
}
return "不合格";
}
else
{
return "合格";
}
}
else if (paramName == "开模总数实时" || paramName == "托模次数")
{
return "合格";
}
else
{
if (float.TryParse(value?.ToString(), out float paramValue) &&
float.TryParse(drMatch["tolerance_lower"].ToString(), out float toleranceLower) &&
float.TryParse(drMatch["tolerance_upper"].ToString(), out float toleranceUpper))
{
return (paramValue >= toleranceLower && paramValue <= toleranceUpper) ? "合格" : "不合格";
}
else
{
LogHelper.AppendLog(
$"数值转换失败:参数名={paramName},参数值={value},下公差={drMatch["tolerance_lower"]},上公差={drMatch["tolerance_upper"]}");
return "不合格";
}
}
}
// ======================================== 日志相关方法 ========================================
private void OnMqttMessage(string msg)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<string>(OnMqttMessage), msg);
return;
}
LogHelper.AppendLog($"收到消息: {msg}");
}
private void LogHelper_OnLogGenerated(string logContent)
{
Action appendAction = () => AppendLogToTextBox(logContent);
if (textBoxLog.InvokeRequired)
{
textBoxLog.BeginInvoke(appendAction); // 改用BeginInvoke避免阻塞
}
else
{
appendAction();
}
}
// 定义日志最大行数
private const int MAX_LOG_LINES = 100;
/// <summary>
/// 日志到WinForm弹窗
/// </summary>
/// <param name="logContent"></param>
private void AppendLogToTextBox(string logContent)
{
// 解决跨线程访问控件的问题WinForm必备
if (textBoxLog.InvokeRequired)
{
textBoxLog.Invoke(new Action<string>(AppendLogToTextBox), logContent);
return;
}
// 1. 拆分现有日志为行列表(修复原代码空行问题)
List<string> logLines = textBoxLog.Text
.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
.ToList();
// 2. 添加新日志行原代码错误地添加了logContent而非newLog这里统一处理
logLines.Add(logContent);
// 3. 如果超过最大行数,删除最旧的行(核心逻辑优化)
if (logLines.Count > MAX_LOG_LINES)
{
// 移除超出部分的最旧日志(从列表头部删除)
logLines.RemoveRange(0, logLines.Count - MAX_LOG_LINES);
}
// 4. 重新拼接日志内容(避免末尾多余空行)
textBoxLog.Text = string.Join("\r\n", logLines);
// 5. 保持滚动到底部
textBoxLog.SelectionStart = textBoxLog.Text.Length;
textBoxLog.ScrollToCaret();
}
// ======================================== 未实现方法 ========================================
private void txtLUploadPL_TextChanged(object sender, EventArgs e)
{
}
private void panel1_Paint_1(object sender, PaintEventArgs e)
{
}
private void label6_Click(object sender, EventArgs e)
{
}
private void label8_Click(object sender, EventArgs e)
{
}
private void panel3_Paint(object sender, PaintEventArgs e)
{
}
private void toolStripStatusLabel1_Click(object sender, EventArgs e)
{
}
}
}