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 Newtonsoft.Json; using YiDa_WinForm.Business; using YiDa_WinForm.Config; using YiDa_WinForm.Model; using YiDa_WinForm.Service; using YiDa_WinForm.Service.Mqtt; using YiDa_WinForm.Utils; namespace YiDa_WinForm { public partial class MainForm : Form { // 依赖注入 private readonly MqttClientService _mqttService; private readonly YiDaUploadService _uploadService; private readonly ButtonOperationService _buttonService; private readonly FormulaBusinessService _formulaBusinessService; // 通用上传定时器 private System.Timers.Timer _timer; // 日志文件相关路径 private readonly string _logDirectory = "log"; private string _currentLogFile; private DateTime _lastLogDate = DateTime.MinValue; // 工作时间配置(默认 9:00 - 23:30) private TimeSpan _workStartTime = new TimeSpan(9, 0, 0); private TimeSpan _workEndTime = new TimeSpan(23, 30, 0); // 连杆测试定时报警相关 private System.Timers.Timer _rodAlarmTimer; private bool _isImageUploadedInCycle = false; // 报废上传定时报警相关(和连杆完全对齐) private System.Timers.Timer _scrapAlarmTimer; private bool _isScrapImageUploadedInCycle = false; // ======================================== 初始化相关方法 ======================================== public MainForm() { // 初始化设计器方法 InitializeComponent(); // 订阅日志事件 LogHelper.OnLogGenerated += LogHelper_OnLogGenerated; // 初始化服务实例 _buttonService = new ButtonOperationService(); _mqttService = new MqttClientService(); _uploadService = new YiDaUploadService(); _formulaBusinessService = new FormulaBusinessService(_buttonService); // 绑定回调事件(连杆+报废) _mqttService.MessageReceived += OnMqttMessage; _uploadService.RodUploadStatusChanged += OnRodUploadStatusChanged; _uploadService.ScrapUploadStatusChanged += OnScrapUploadStatusChanged; // UI初始化配置 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 LoadMqttDic(); timer1.Enabled = true; } catch (Exception ex) { LogHelper.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); } /// /// 加载配方到UI下拉框 /// private async Task LoadFormula() { await _formulaBusinessService.LoadFormulaAsync(); // 绑定下拉框数据源 bsPF.DataSource = _formulaBusinessService.UniqueFormula; bsPF2.DataSource = _formulaBusinessService.UniqueFormula; 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"]} - {drv["part_name"]}"; } }; comboBox2.Format += (s, e) => { if (e.ListItem is DataRowView drv) { e.Value = string.IsNullOrEmpty(drv["part_number_name"].ToString()) ? "" : $"{drv["part_number"]} - {drv["part_name"]}"; } }; } /// /// 加载MQTT字典到UI /// private async Task LoadMqttDic() { await _formulaBusinessService.LoadMqttDicAsync(); } // ======================================== 按钮相关方法 ======================================== /// /// 网关连接按钮 /// 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 = "连接失败"; } } /// /// 网关断开按钮 /// 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}"); } } /// /// 配方导入按钮 /// private async void button1_Click(object sender, EventArgs e) { try { OpenFileDialog openFile = new OpenFileDialog(); openFile.Filter = "Excel文件|*.xlsx;*.xls"; if (openFile.ShowDialog() == DialogResult.OK) { string filePath = openFile.FileName; DataTable excelDt = ExcelHelper.GetExcelToDataTable(filePath); if (excelDt != null && excelDt.Rows.Count > 0) { await _buttonService.SaveFormulaByExcel(excelDt); await LoadFormula(); MessageBox.Show("配方信息已导入!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("不允许导入空表!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } catch (Exception ex) { LogHelper.AppendLog($"配方导入失败:{ex.Message}"); MessageBox.Show($"配方导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 上传宜搭按钮 /// private async void button2_Click(object sender, EventArgs e) { try { if (toolStripStatusLabel1.Text != "已连接") { MessageBox.Show("请先连接MQTT网关!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (string.IsNullOrEmpty(comboBox1.Text) && string.IsNullOrEmpty(comboBox2.Text)) { MessageBox.Show("请先导入配方信息并选择配方!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } button2.Enabled = false; if (_timer != null) { _timer.Enabled = false; _timer.Dispose(); } RefreshSelectedFormulaToService(); double seconds = 10; if (double.TryParse(txtLUploadPL.Text, out double inputSeconds) && inputSeconds > 0) { seconds = inputSeconds; } await MqttYiDaUpload(); _timer = new System.Timers.Timer(seconds * 1000); _timer.Elapsed += Timer_Elapsed; _timer.AutoReset = true; _timer.Enabled = true; LogHelper.AppendLog($"自动上传已启动,间隔:{seconds}秒"); MessageBox.Show($"自动上传已启动,间隔:{seconds}秒", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { button2.Enabled = true; LogHelper.AppendLog($"上传宜搭失败:{ex.Message}"); MessageBox.Show($"上传宜搭失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 中止上传按钮 /// 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); } } /// /// 报废上传凭证按钮(完整逻辑:定时报警 + 全不选中关闭定时器) /// private async void button4_Click(object sender, EventArgs e) { using (var scrapForm = new ScrapUploadForm()) { DialogResult result = scrapForm.ShowDialog(this); if (result == DialogResult.OK) { bool isScrapUpload = scrapForm.IsScrapUploadSelected; bool isScrapTimedAlarm = scrapForm.IsScrapTimedAlarmSelected; int interval = scrapForm.ScrapTimedInterval; // ========== 核心逻辑:全不选中时销毁报废定时器 ========== if (!isScrapUpload && !isScrapTimedAlarm) { if (_scrapAlarmTimer != null) { _scrapAlarmTimer.Enabled = false; _scrapAlarmTimer.Dispose(); _scrapAlarmTimer = null; LogHelper.AppendLog("未勾选任何选框,已销毁报废上传定时报警定时器"); MessageBox.Show("未勾选任何选框,已关闭报废上传定时报警", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { LogHelper.AppendLog("未勾选任何选框,当前无运行中的报废上传定时报警定时器"); MessageBox.Show("未勾选任何选框,当前无运行中的定时报警任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } return; } bool isImageUploadSuccess = false; if (isScrapUpload) { OpenFileDialog openFile = new OpenFileDialog(); if (openFile.ShowDialog() == DialogResult.OK) { string filePath = openFile.FileName; if (!string.IsNullOrEmpty(filePath)) { isImageUploadSuccess = await _uploadService.UploadScrapImageInternalAsync(filePath); MessageBox.Show(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!"); } else { MessageBox.Show("不允许上传空文件!"); } } } if (isScrapTimedAlarm) { LogHelper.AppendLog($"勾选了报废定时上传报警,准备启动定时任务(间隔{interval}分钟)"); TimeSpan currentTime = DateTime.Now.TimeOfDay; if (currentTime < _workStartTime || currentTime > _workEndTime) { this.Invoke(new Action(() => { MessageBox.Show( $"当前时间不在工作时间({_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm})内,定时任务将在工作时间内自动生效", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); })); } StartScrapAlarmTimer(interval); } LogHelper.AppendLog( $"选择结果:报废凭证上传={isScrapUpload}(上传结果={isImageUploadSuccess}),定时上传报警={isScrapTimedAlarm},时间间隔={interval}分钟"); if (!isScrapUpload && isScrapTimedAlarm) { MessageBox.Show("定时报警任务已启动,将在工作时间内按配置间隔执行判断", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } } /// /// 连杆测试上传按钮(完整逻辑:定时报警 + 全不选中关闭定时器) /// private async void button5_Click(object sender, EventArgs e) { using (var selectForm = new StrengthTestUploadForm()) { DialogResult result = selectForm.ShowDialog(this); if (result == DialogResult.OK) { bool isMoldUpload = selectForm.IsMoldProductionSelected; bool isTimedAlarm = selectForm.IsTimedAlarmSelected; int interval = selectForm.TimedInterval; // ========== 核心逻辑:全不选中时销毁连杆定时器 ========== if (!isMoldUpload && !isTimedAlarm) { if (_rodAlarmTimer != null) { _rodAlarmTimer.Enabled = false; _rodAlarmTimer.Dispose(); _rodAlarmTimer = null; LogHelper.AppendLog("未勾选任何选框,已销毁连杆测试定时报警定时器"); MessageBox.Show("未勾选任何选框,已关闭连杆测试定时报警", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { LogHelper.AppendLog("未勾选任何选框,当前无运行中的连杆测试定时报警定时器"); MessageBox.Show("未勾选任何选框,当前无运行中的定时报警任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } return; } bool isImageUploadSuccess = false; if (isMoldUpload) { OpenFileDialog openFile = new OpenFileDialog(); if (openFile.ShowDialog() == DialogResult.OK) { string filePath = openFile.FileName; if (!string.IsNullOrEmpty(filePath)) { isImageUploadSuccess = await _uploadService.UploadRodTestImageInternalAsync(filePath); MessageBox.Show(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!"); } else { MessageBox.Show("不允许上传空文件!"); } } } if (isTimedAlarm) { LogHelper.AppendLog($"勾选了定时上传报警,准备启动定时任务(间隔{interval}分钟)"); TimeSpan currentTime = DateTime.Now.TimeOfDay; if (currentTime < _workStartTime || currentTime > _workEndTime) { this.Invoke(new Action(() => { MessageBox.Show( $"当前时间不在工作时间({_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm})内,定时任务将在工作时间内自动生效", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); })); } StartRodAlarmTimer(interval); } LogHelper.AppendLog( $"选择结果:模具投产上传={isMoldUpload}(上传结果={isImageUploadSuccess}),定时上传报警={isTimedAlarm},时间间隔={interval}分钟"); if (!isMoldUpload && isTimedAlarm) { MessageBox.Show("定时报警任务已启动,将在工作时间内按配置间隔执行判断", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } } /// /// 配置按钮 /// private void button6_Click(object sender, EventArgs e) { try { using (var configForm = new WorkTimeConfigForm(_workStartTime, _workEndTime)) { DialogResult result = configForm.ShowDialog(this); if (result == DialogResult.OK) { _workStartTime = configForm.WorkStartTime; _workEndTime = configForm.WorkEndTime; LogHelper.AppendLog($"工作时间配置更新:{_workStartTime:hh\\:mm} - {_workEndTime:hh\\:mm}"); MessageBox.Show("工作时间配置成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } catch (Exception ex) { LogHelper.AppendLog($"工作时间配置失败:{ex.Message}"); MessageBox.Show($"配置失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 关闭MainForm按钮 /// private async void MainFormClosing(object sender, FormClosingEventArgs e) { try { if (_timer != null) { _timer.Enabled = false; _timer.Dispose(); } // 销毁连杆+报废定时器 if (_rodAlarmTimer != null) { _rodAlarmTimer.Enabled = false; _rodAlarmTimer.Dispose(); } if (_scrapAlarmTimer != null) { _scrapAlarmTimer.Enabled = false; _scrapAlarmTimer.Dispose(); } await _mqttService.MqttClientStopAsync(); } catch (Exception ex) { LogHelper.AppendLog($"关闭窗口失败:{ex.Message}"); } } /// /// 刷新配方按钮 /// private async void btnRefresh_Click(object sender, EventArgs e) { try { await LoadFormula(); await LoadMqttDic(); MessageBox.Show("配方已刷新!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { LogHelper.AppendLog($"刷新配方失败:{ex.Message}"); MessageBox.Show($"刷新配方失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 上传间隔输入框校验 /// 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; } } /// /// 更新时间显示 /// private void timer1_Tick(object sender, EventArgs e) { label4.Text = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"); } // ======================================== 定时报警核心方法(连杆+报废) ======================================== /// /// 启动连杆测试定时报警任务 /// private void StartRodAlarmTimer(int intervalMinutes) { if (_rodAlarmTimer != null) { _rodAlarmTimer.Enabled = false; _rodAlarmTimer.Dispose(); } _rodAlarmTimer = new System.Timers.Timer(intervalMinutes * 60 * 1000); _rodAlarmTimer.Elapsed += RodAlarmTimer_Elapsed; _rodAlarmTimer.AutoReset = true; _rodAlarmTimer.Enabled = true; _isImageUploadedInCycle = false; LogHelper.AppendLog( $"连杆测试定时报警已启动:间隔{intervalMinutes}分钟,工作时间{_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm}"); } /// /// 启动报废上传定时报警任务 /// private void StartScrapAlarmTimer(int intervalMinutes) { if (_scrapAlarmTimer != null) { _scrapAlarmTimer.Enabled = false; _scrapAlarmTimer.Dispose(); } _scrapAlarmTimer = new System.Timers.Timer(intervalMinutes * 60 * 1000); _scrapAlarmTimer.Elapsed += ScrapAlarmTimer_Elapsed; _scrapAlarmTimer.AutoReset = true; _scrapAlarmTimer.Enabled = true; _isScrapImageUploadedInCycle = false; LogHelper.AppendLog( $"报废上传定时报警已启动:间隔{intervalMinutes}分钟,工作时间{_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm}"); } /// /// 连杆报警定时器触发事件 /// private void RodAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { DateTime now = DateTime.Now; TimeSpan currentTime = now.TimeOfDay; if (currentTime < _workStartTime || currentTime > _workEndTime) { LogHelper.AppendLog($"当前时间{currentTime:hh\\:mm}不在工作时间内,跳过本次连杆报警判断"); _isImageUploadedInCycle = false; return; } if (!_isImageUploadedInCycle) { string alarmContent = $"连杆测试超时未上传图片!当前时间:{now:yyyy-MM-dd HH:mm:ss},配置间隔:{_rodAlarmTimer.Interval / 60000}分钟"; this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true))); } else { LogHelper.AppendLog($"连杆测试计时周期内已上传图片,无需报警,重置标记"); } _isImageUploadedInCycle = false; } /// /// 报废报警定时器触发事件 /// private void ScrapAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { DateTime now = DateTime.Now; TimeSpan currentTime = now.TimeOfDay; if (currentTime < _workStartTime || currentTime > _workEndTime) { LogHelper.AppendLog($"当前时间{currentTime:hh\\:mm}不在工作时间内,跳过本次报废报警判断"); _isScrapImageUploadedInCycle = false; return; } if (!_isScrapImageUploadedInCycle) { string alarmContent = $"报废上传超时未上传凭证!当前时间:{now:yyyy-MM-dd HH:mm:ss},配置间隔:{_scrapAlarmTimer.Interval / 60000}分钟"; this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false))); } else { LogHelper.AppendLog($"报废上传计时周期内已上传凭证,无需报警,重置标记"); } _isScrapImageUploadedInCycle = false; } /// /// 发送钉钉报警(区分连杆/报废) /// private async void SendDingDingAlarm(string alarmContent, bool isRodAlarm) { try { LogHelper.AppendLog($"【钉钉报警】{(isRodAlarm ? "连杆" : "报废")}:{alarmContent}"); await _uploadService.SendDingDingTextAlarmAsync(alarmContent, isRodAlarm); MessageBox.Show($"已发送钉钉{(isRodAlarm ? "连杆" : "报废")}报警:{alarmContent}", "报警提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); } catch (Exception ex) { LogHelper.AppendLog($"{(isRodAlarm ? "连杆" : "报废")}钉钉报警发送失败:{ex.Message}"); } } // ======================================== 上传状态回调方法(连杆+报废) ======================================== /// /// 处理连杆上传状态回调 /// private void OnRodUploadStatusChanged(object sender, RodUploadStatusEventArgs e) { if (this.InvokeRequired) { this.Invoke(new Action(OnRodUploadStatusChanged), sender, e); return; } _isImageUploadedInCycle = e.IsUploadSuccess; LogHelper.AppendLog($"连杆上传状态:{(e.IsUploadSuccess ? "成功" : "失败")},消息:{e.Message}"); } /// /// 处理报废上传状态回调 /// private void OnScrapUploadStatusChanged(object sender, ScrapUploadStatusEventArgs e) { if (this.InvokeRequired) { this.Invoke(new Action(OnScrapUploadStatusChanged), sender, e); return; } _isScrapImageUploadedInCycle = e.IsUploadSuccess; LogHelper.AppendLog($"报废上传状态:{(e.IsUploadSuccess ? "成功" : "失败")},消息:{e.Message}"); } // ======================================== 辅助方法 ======================================== /// /// 定时器触发事件 /// private async void Timer_Elapsed(object sender, ElapsedEventArgs e) { RefreshSelectedFormulaToService(); await MqttYiDaUpload(); } /// /// 转发下拉框选择值到配方业务服务 /// private void RefreshSelectedFormulaToService() { string selected1 = comboBox1.SelectedValue?.ToString() ?? string.Empty; string selected2 = comboBox2.SelectedValue?.ToString() ?? string.Empty; _formulaBusinessService.RefreshSelectedFormula(selected1, selected2); } // ======================================== 核心上传方法 ======================================== /// /// 上传宜搭方法 /// private async Task MqttYiDaUpload() { try { DataTable dtMqtt = await _buttonService.GetLatestMqttDataAsync(); 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}]数据"); var (formulaList, site) = GetDeviceFormulaAndSite(deviceCode); 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(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 mqttLists = new List(); List yidaLists = new List(); foreach (PropertyInfo prop in properties) { string paramCode = prop.Name; object value = prop.GetValue(data.@params); if (value == null || string.IsNullOrWhiteSpace(value.ToString())) { continue; } string paramName = _formulaBusinessService.MapMqttParamName(paramCode); DataRow drMatch = _formulaBusinessService.MatchFormulaByParamName(formulaList, paramName); if (drMatch == null) { LogHelper.AppendLog($"设备[{deviceCode}]配方中未找到参数[{paramName}],跳过"); continue; } MqttModel mqttNew = BuildMqttModel(drMatch, paramName, value, site); YiDaModel yidaNew = BuildYiDaModel(drMatch, paramName, value, site); mqttLists.Add(mqttNew); yidaLists.Add(yidaNew); } int len = Math.Min(mqttLists.Count, yidaLists.Count); if (len > 0) { List mqttLists1 = mqttLists.Take(len).ToList(); List 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, mqttLists, 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 Tuple, string> GetDeviceFormulaAndSite(string deviceCode) { if (string.Equals(deviceCode, "device1", StringComparison.Ordinal)) { return Tuple.Create(_formulaBusinessService.SelectedFormula1, "1号设备"); } else if (string.Equals(deviceCode, "device2", StringComparison.Ordinal)) { return Tuple.Create(_formulaBusinessService.SelectedFormula2, "2号设备"); } else { return Tuple.Create, string>(null, null); } } /// /// 构造MqttModel /// 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); return mqttNew; } /// /// 构造YiDaModel /// 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); if (paramName == "报废图片路径" && _buttonService._formulaValue == "报废图片路径") { yidaNew.imageField_mii5s85z = _uploadService._reformationPicture; } yidaNew.textField_mha98nf5 = JudgeQualification(paramName, value, drMatch); return yidaNew; } /// /// 合格判断 /// private string JudgeQualification(string paramName, object value, DataRow drMatch) { if (paramName == "报警信息") { return string.Equals(value?.ToString(), "无错误", StringComparison.Ordinal) ? "合格" : "不合格"; } else if (paramName == "开模总数实时" || 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 "不合格"; } } } // ======================================== 日志相关方法 ======================================== /// /// 接收MQTT消息并写入日志 /// private void OnMqttMessage(string msg) { if (this.InvokeRequired) { this.Invoke(new Action(OnMqttMessage), msg); return; } LogHelper.AppendLog($"收到消息: {msg}"); } /// /// 处理日志事件,将日志追加到WinForm界面的txtLog控件 /// private void LogHelper_OnLogGenerated(string logContent) { if (textBoxLog.InvokeRequired) { textBoxLog.Invoke(new Action(() => { AppendLogToTextBox(logContent); })); } else { AppendLogToTextBox(logContent); } } /// /// 追加日志到TextBox,并自动滚动到最新日志 /// private void AppendLogToTextBox(string logContent) { textBoxLog.Text += $"{logContent}\r\n"; textBoxLog.SelectionStart = textBoxLog.Text.Length; textBoxLog.ScrollToCaret(); } // ======================================== 未实现方法(保留窗体设计器生成) ======================================== private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { } private void button5_Click_1(object sender, EventArgs e) { } private void txtLUploadPL_TextChanged(object sender, EventArgs e) { } private void panel1_Paint(object sender, PaintEventArgs e) { } private void panel1_Paint_1(object sender, EventArgs e) { } private void label6_Click(object sender, EventArgs e) { } } }