fg_yida_2/YiDa_WinForm/MainForm.cs

1015 lines
40 KiB
C#
Raw 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 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.Reflection;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using YiDa_WinForm.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 _formulaDic;
private List<DataRow> _formulaList1; // 设备1选择的配方
private List<DataRow> _formulaList2; // 设备2选择的配方
private System.Timers.Timer _timer;
// 日志文件相关路径
private readonly string _logDirectory = "log";
private string _currentLogFile;
private DateTime _lastLogDate = DateTime.MinValue;
// ======================================== 初始化相关方法 ========================================
/// <summary>
/// 构造器
/// </summary>
public MainForm()
{
// 初始化设计器方法
InitializeComponent();
_mqttService = new MqttClientService();
_buttonService = new ButtonOperationService();
_uploadService = new YiDaUploadService();
_mqttService.MessageReceived += OnMqttMessage;
_formula = new DataTable();
_formulaDic = new DataTable();
buttonDisconnect.Enabled = false;
// 初始化日志目录
InitializeLogDirectory();
}
/// <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>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Form1_Load(object sender, EventArgs e)
{
try
{
MakePanelRound(panelLed);
await LoadFormula();
await LoadMqttDic();
timer1.Enabled = true;
}
catch (Exception ex)
{
AppendLog($"初始化失败:{ex.Message}");
}
}
/// <summary>
/// 将指定 Panel 变成圆形
/// </summary>
/// <param name="panel"></param>
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 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();
}
};
}
}
/// <summary>
/// 加载注塑机MQTT字典
/// </summary>
private async Task LoadMqttDic()
{
DataTable dt = await _buttonService.InitMqttDic();
if (dt != null)
{
_formulaDic = dt;
}
}
// ======================================== 按钮相关方法 ========================================
/// <summary>
/// 网关连接按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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 = "连接失败";
}
}
/// <summary>
/// 网关断开按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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}");
}
}
/// <summary>
/// 关闭MainForm按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void MainFormClosing(object sender, FormClosingEventArgs e)
{
try
{
// 停止定时器
if (_timer != null)
{
_timer.Enabled = false;
_timer.Dispose();
}
await _mqttService.MqttClientStopAsync();
}
catch (Exception ex)
{
AppendLog($"关闭窗口失败:{ex.Message}");
}
}
/// <summary>
/// 配方导入按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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 = GetExcel(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)
{
AppendLog($"配方导入失败:{ex.Message}");
MessageBox.Show($"配方导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 上传宜搭按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button2_Click(object sender, EventArgs e)
{
try
{
// 如果 MQTT 没连接,就不允许上传
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;
}
this.button2.Enabled = false;
// 停止旧定时器(避免重复)
if (_timer != null)
{
_timer.Enabled = false;
_timer.Dispose();
}
// 重新获取配方(每次点击都刷新)
RefreshSelectedFormula();
double seconds;
if (!double.TryParse(txtLUploadPL.Text, out seconds) || seconds <= 0)
{
seconds = 10; //控制上传间隔默认10秒。
}
// 立即上传一次
await MqttYiDaUpload();
// 创建新定时器(.NET 4.8 兼容写法)
_timer = new System.Timers.Timer(seconds * 1000);
_timer.Elapsed += Timer_Elapsed;
_timer.AutoReset = true; // 是否重复
_timer.Enabled = true; // 开始计时
AppendLog($"自动上传已启动,间隔:{seconds}秒");
MessageBox.Show($"自动上传已启动,间隔:{seconds}秒", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
this.button2.Enabled = true;
AppendLog($"上传宜搭失败:{ex.Message}");
MessageBox.Show($"上传宜搭失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 定时器触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// 每次定时器触发都重新获取最新配方
RefreshSelectedFormula();
await MqttYiDaUpload();
}
/// <summary>
/// 刷新选中的配方(每次上传前重新获取)
/// </summary>
private void RefreshSelectedFormula()
{
// 设备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<string>("part_number") == partNumber1
&& r.Field<string>("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<string>("part_number") == partNumber2
&& r.Field<string>("part_name") == partName2)
.ToList();
}
else
{
_formulaList2 = null;
}
}
}
/// <summary>
/// 刷新配方
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnRefresh_Click(object sender, EventArgs e)
{
try
{
await LoadFormula();
await LoadMqttDic();
MessageBox.Show("配方已刷新!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
AppendLog($"刷新配方失败:{ex.Message}");
MessageBox.Show($"刷新配方失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 中止上传按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
this.button2.Enabled = true;
if (this._timer != null)
{
this._timer.Enabled = false;
this._timer.Dispose();
this._timer = null;
AppendLog("已停止自动上传数据");
MessageBox.Show("已停止自动上传", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
AppendLog("无运行中的上传定时器");
MessageBox.Show("当前无自动上传任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
/// <summary>
/// 报废上传凭证按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button4_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (filePath != null)
{
// 获取钉钉 token
string token = _uploadService.GetDingDingToken();
if (string.IsNullOrEmpty(token))
{
MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret");
return;
}
// 上传数据库数据到宜搭
await _uploadService.UploadScrapCertificate(token, filePath);
MessageBox.Show("报废证据上传已上传!");
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
catch (Exception ex)
{
AppendLog($"报废证据上传失败:{ex.Message}");
}
}
private void button5_Click(object sender, EventArgs e)
{
throw new System.NotImplementedException();
}
/// <summary>
/// 刷新秒数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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;
}
}
/// <summary>
/// 更新时间
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
// 每秒更新一次时间显示
this.label4.Text = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
}
// ======================================== 工具方法 ========================================
/// <summary>
/// 获取Excel到Datatable中.NET 4.8 兼容版)
/// </summary>
/// <param name="filePath">Excel路径</param>
/// <returns>返回DataTable数据</returns>
private static DataTable GetExcel(string filePath)
{
IWorkbook iwkX = null;
FileStream fs = null;
try
{
fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
iwkX = WorkbookFactory.Create(fs);
//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() ?? $"列{i + 1}";
dt.Columns.Add(str);
}
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
{
// 处理不同单元格类型
switch (cell.CellType)
{
case CellType.Numeric:
if (DateUtil.IsCellDateFormatted(cell))
{
dr[i] = cell.DateCellValue.ToString();
}
else
{
dr[i] = cell.NumericCellValue.ToString();
}
break;
case CellType.String:
dr[i] = cell.StringCellValue;
break;
case CellType.Boolean:
dr[i] = cell.BooleanCellValue.ToString();
break;
default:
dr[i] = cell.ToString();
break;
}
}
}
dt.Rows.Add(dr);
}
}
}
return dt;
}
finally
{
// .NET 4.8 手动释放资源
if (fs != null)
{
fs.Close();
fs.Dispose();
}
if (iwkX != null)
{
iwkX = null;
}
}
}
/// <summary>
/// 上传宜搭方法
/// </summary>
private async Task MqttYiDaUpload()
{
try
{
//获取最新的2台设备数据各取最新1条。
DataTable dtMqtt = await _buttonService.GetLatestMqttDataAsync();
if (dtMqtt == null || dtMqtt.Rows.Count == 0)//有设备数据才会执行
{
AppendLog("未查询到MQTT设备数据跳过本次上传");
return;
}
AppendLog($"本次读取到{dtMqtt.Rows.Count}条MQTT设备数据");
foreach (DataRow dr in dtMqtt.Rows)//遍历dtMqtt中数据一行代表一个设备的数据一共2行
{
string deviceCode = dr["device_code"].ToString();
AppendLog($"开始处理设备[{deviceCode}]数据");
List<DataRow> drPeiFangList = null;
string site = null;
if (deviceCode == "device1")
{
drPeiFangList = _formulaList1;
site = "1号设备";
}
else if (deviceCode == "device2")
{
drPeiFangList = _formulaList2;
site = "2号设备";
}
if (drPeiFangList == null || drPeiFangList.Count == 0)
{
AppendLog($"设备[{deviceCode}]未选择配方,跳过");
continue;
}
//取出一条字段 MESSAGE
string strMessage = dr["receive_data"].ToString();
if (string.IsNullOrEmpty(strMessage))
{
AppendLog($"设备[{deviceCode}]无有效receive_data跳过");
continue;
}
//反序列化设备收到的数据,取出里面的参数(@params。根据MqttDataModel 的定义。
SQLDataModel data = null;
try
{
data = JsonConvert.DeserializeObject<SQLDataModel>(strMessage);
}
catch (Exception ex)
{
AppendLog($"设备[{deviceCode}]数据反序列化失败:{ex.Message}");
continue;
}
//如果反序列化失败或 params 为空,就跳过。
if (data == null || data.@params == null)
{
AppendLog($"设备[{deviceCode}]反序列化后无params数据跳过");
continue;
}
//获取 @params 的实际类型。
Type paramsType = data.@params.GetType();
//properties—>paramsType 的所有字段信息
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)
{
//parameter_name,参数名,比如 "Value01"
string paramName = prop.Name;
//parameter_value参数值,比如 24.6
object value = prop.GetValue(data.@params);
// 跳过空值null 或 ""
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
continue;
}
//查映射表
//如果找到了映射,就替换 name 为中文显示名,
//dtMqttDic在Form1_Load方法中已经被赋值是字典表中的数据。
DataRow[] drs = _formulaDic.Select("param_code = '" + paramName + "'");
if (drs != null && drs.Length > 0)
{
paramName = drs[0]["param_name"].ToString();
}
DataRow drMatch = drPeiFangList.FirstOrDefault(r => r.Field<string>("parameter_name") == paramName);
if (drMatch == null)
{
AppendLog($"设备[{deviceCode}]配方中未找到参数[{paramName}],跳过");
continue; // 没找到匹配行就跳过
}
//构造 MqttModel用于存数据库
MqttModel mqttNew = new MqttModel();
//把当前参数数据与选中的配方信息drPeiFang融合形成一个完整记录。
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;//parameter_name,参数名
mqttNew.ParameterValue = value == null ? "" : value.ToString();//parameter_value参数值
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();//外保负责人
//构造 YiDaModel用于宜搭上传
//把当前参数数据与选中的配方信息drPeiFang融合形成一个完整记录。
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;//parameter_name,参数名
yidaNew.textField_mhx44i2i = value == null ? "" : value.ToString();//parameter_value参数值
yidaNew.textField_mhx44i2j = drMatch["tolerance_lower"].ToString();//下公差
yidaNew.textField_mhx44i2k = drMatch["tolerance_upper"].ToString();//上公差
yidaNew.textField_mha98nf7 = drMatch["leader_part"].ToString();//零件负责人
if (paramName == "报废图片路径" && _buttonService._formulaValue == "报废图片路径")
{
yidaNew.imageField_mii5s85z = _uploadService._reformationPicture; //整改图片
}
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 == "报警信息")
{
if (string.Equals(value?.ToString(), "无错误", StringComparison.Ordinal))
{
mqttNew.IsQualification = "合格";
yidaNew.textField_mha98nf5 = "合格";
}
else
{
mqttNew.IsQualification = "不合格";
yidaNew.textField_mha98nf5 = "不合格";
}
}
else if (paramName == "开模总数实时" || paramName == "托模次数" || paramName == "报废图片路径")
{
mqttNew.IsQualification = "合格";
yidaNew.textField_mha98nf5 = "合格";
}
else
{
// 用参数值value比较而非参数名name
// 安全转换参数值、下公差、上公差为float
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))
{
// 数值在公差范围内则合格
if (paramValue >= toleranceLower && paramValue <= toleranceUpper)
{
mqttNew.IsQualification = "合格";
yidaNew.textField_mha98nf5 = "合格";
}
else
{
mqttNew.IsQualification = "不合格";
yidaNew.textField_mha98nf5 = "不合格";
}
}
else
{
// 转换失败(如参数值非数值、公差为空),判定为不合格或跳过
mqttNew.IsQualification = "不合格";
yidaNew.textField_mha98nf5 = "不合格";
// 输出日志排查转换失败的字段
this.AppendLog($"数值转换失败:设备[{deviceCode}],参数名={paramName},参数值={value},下公差={drMatch["tolerance_lower"]},上公差={drMatch["tolerance_upper"]}");
}
}
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))
{
AppendLog("获取 token 失败,请检查 AppKey/AppSecret");
// .NET 4.8 跨线程弹窗
this.Invoke(new Action(() =>
{
MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}));
return;
}
// 跨线程调用UI相关方法
this.Invoke(new Action(() =>
{
_uploadService.UploadDatabaseDataToYiDaWithLogging(token, yidaLists1, mqttLists, this);
}));
AppendLog($"设备[{deviceCode}]成功上传{len}条数据到宜搭");
}
else
{
AppendLog($"设备[{deviceCode}]无有效参数数据可上传");
}
}
}
catch (Exception ex)
{
AppendLog($"MqttYiDaUpload执行失败{ex.Message}");
// 跨线程显示错误提示(.NET 4.8 兼容写法)
this.Invoke(new Action(() =>
{
MessageBox.Show($"上传失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}));
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
// ======================================== 日志相关方法 ========================================
/// <summary>
/// 接收 MQTT 消息后,将消息日志显示到 UI 控件上(.NET 4.8 兼容版)
/// </summary>
/// <param name="msg"></param>
private void OnMqttMessage(string msg)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<string>(OnMqttMessage), msg);
return;
}
AppendLog($"收到消息: {msg}");
}
/// <summary>
/// 写入日志到文本框并保存到文件(.NET 4.8 兼容版)
/// </summary>
public void AppendLog(string message)
{
try
{
if (this.InvokeRequired)
{
this.Invoke(new Action<string>(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)
{
// 静默处理日志写入错误,避免影响主流程
Console.WriteLine($"日志处理错误: {ex.Message}");
}
}
/// <summary>
/// 将日志保存到文件(.NET 4.8 兼容版)
/// </summary>
private void SaveToLogFile(string logContent)
{
try
{
UpdateLogFile(); // 确保使用当天的日志文件
// 追加写入日志使用UTF8编码避免中文乱码.NET 4.8 显式指定编码)
File.AppendAllText(_currentLogFile, logContent, System.Text.Encoding.UTF8);
}
catch (Exception ex)
{
// 文件写入错误处理
Console.WriteLine($"日志文件写入错误: {ex.Message}");
}
}
/// <summary>
/// 限制日志文本框的最大行数(.NET 4.8 兼容版)
/// </summary>
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();
}
}
private void button5_Click_1(object sender, EventArgs e)
{
throw new System.NotImplementedException();
}
private void txtLUploadPL_TextChanged(object sender, EventArgs e)
{
throw new System.NotImplementedException();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
throw new System.NotImplementedException();
}
}
}