2026-01-21 19:31:27 +08:00
|
|
|
|
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;
|
2026-01-22 16:59:41 +08:00
|
|
|
|
using System.Reflection;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using System.Timers;
|
|
|
|
|
|
using System.Windows.Forms;
|
2026-01-22 16:59:41 +08:00
|
|
|
|
using YiDa_WinForm.Service;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
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; // 配方缓存
|
2026-01-22 16:59:41 +08:00
|
|
|
|
private DataTable _formulaDic;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
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;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
// ======================================== 初始化相关方法 ========================================
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构造器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public MainForm()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 初始化设计器方法
|
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
|
|
|
|
|
|
_mqttService = new MqttClientService();
|
|
|
|
|
|
_buttonService = new ButtonOperationService();
|
|
|
|
|
|
_uploadService = new YiDaUploadService();
|
|
|
|
|
|
_mqttService.MessageReceived += OnMqttMessage;
|
|
|
|
|
|
_formula = new DataTable();
|
2026-01-22 16:59:41 +08:00
|
|
|
|
_formulaDic = new DataTable();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
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();
|
2026-01-22 16:59:41 +08:00
|
|
|
|
await LoadMqttDic();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
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>
|
2026-01-22 16:59:41 +08:00
|
|
|
|
private async Task LoadMqttDic()
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
DataTable dt = await _buttonService.InitMqttDic();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
if (dt != null)
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
_formulaDic = dt;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================== 按钮相关方法 ========================================
|
|
|
|
|
|
|
|
|
|
|
|
/// <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
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 停止定时器
|
|
|
|
|
|
if (_timer != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_timer.Enabled = false;
|
|
|
|
|
|
_timer.Dispose();
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
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();
|
2026-01-22 16:59:41 +08:00
|
|
|
|
openFile.Filter = "Excel文件|*.xlsx;*.xls";
|
2026-01-21 19:31:27 +08:00
|
|
|
|
if (openFile.ShowDialog() == DialogResult.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
string filePath = openFile.FileName;
|
|
|
|
|
|
DataTable excelDt = GetExcel(filePath);
|
|
|
|
|
|
if (excelDt != null && excelDt.Rows.Count > 0)
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
await _buttonService.SaveFormulaByExcel(excelDt);
|
|
|
|
|
|
await LoadFormula();
|
2026-01-22 16:59:41 +08:00
|
|
|
|
MessageBox.Show("配方信息已导入!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
MessageBox.Show("不允许导入空表!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
AppendLog($"配方导入失败:{ex.Message}");
|
2026-01-22 16:59:41 +08:00
|
|
|
|
MessageBox.Show($"配方导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-23 16:25:57 +08:00
|
|
|
|
/// 上传宜搭按钮
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
|
private async void button2_Click(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果 MQTT 没连接,就不允许上传
|
|
|
|
|
|
if (toolStripStatusLabel1.Text != "已连接")
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
MessageBox.Show("请先连接MQTT网关!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
if (string.IsNullOrEmpty(comboBox1.Text) && string.IsNullOrEmpty(comboBox2.Text))
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
//如果两个下拉框都还没数据,说明没导入过配方,直接中止。
|
2026-01-22 16:59:41 +08:00
|
|
|
|
MessageBox.Show("请先导入配方信息并选择配方!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.button2.Enabled = false;
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 停止旧定时器(避免重复)
|
|
|
|
|
|
if (_timer != null)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
_timer.Enabled = false;
|
|
|
|
|
|
_timer.Dispose();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 重新获取配方(每次点击都刷新)
|
|
|
|
|
|
RefreshSelectedFormula();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
double seconds;
|
|
|
|
|
|
if (!double.TryParse(txtLUploadPL.Text, out seconds) || seconds <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
seconds = 10; //控制上传间隔,默认10秒。
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 立即上传一次
|
|
|
|
|
|
await MqttYiDaUpload();
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 创建新定时器(.NET 4.8 兼容写法)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
_timer = new System.Timers.Timer(seconds * 1000);
|
2026-01-22 16:59:41 +08:00
|
|
|
|
_timer.Elapsed += Timer_Elapsed;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
_timer.AutoReset = true; // 是否重复
|
|
|
|
|
|
_timer.Enabled = true; // 开始计时
|
2026-01-22 16:59:41 +08:00
|
|
|
|
|
|
|
|
|
|
AppendLog($"自动上传已启动,间隔:{seconds}秒");
|
|
|
|
|
|
MessageBox.Show($"自动上传已启动,间隔:{seconds}秒", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.button2.Enabled = true;
|
|
|
|
|
|
AppendLog($"上传宜搭失败:{ex.Message}");
|
2026-01-22 16:59:41 +08:00
|
|
|
|
MessageBox.Show($"上传宜搭失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-23 16:25:57 +08:00
|
|
|
|
/// 定时器触发事件
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
|
private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 每次定时器触发都重新获取最新配方
|
|
|
|
|
|
RefreshSelectedFormula();
|
|
|
|
|
|
await MqttYiDaUpload();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-23 16:25:57 +08:00
|
|
|
|
/// 刷新选中的配方(每次上传前重新获取)
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// </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;
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新配方
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
2026-01-22 16:59:41 +08:00
|
|
|
|
private async void btnRefresh_Click(object sender, EventArgs e)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// <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;
|
2026-01-22 16:59:41 +08:00
|
|
|
|
this._timer.Dispose();
|
|
|
|
|
|
this._timer = null;
|
|
|
|
|
|
AppendLog("已停止自动上传数据");
|
|
|
|
|
|
MessageBox.Show("已停止自动上传", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
AppendLog("无运行中的上传定时器");
|
|
|
|
|
|
MessageBox.Show("当前无自动上传任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
/// <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();
|
|
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// <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");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================== 工具方法 ========================================
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取Excel到Datatable中(.NET 4.8 兼容版)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="filePath">Excel路径</param>
|
|
|
|
|
|
/// <returns>返回DataTable数据</returns>
|
|
|
|
|
|
private static DataTable GetExcel(string filePath)
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
IWorkbook iwkX = null;
|
|
|
|
|
|
FileStream fs = null;
|
|
|
|
|
|
try
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
iwkX = WorkbookFactory.Create(fs);
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//sheet
|
|
|
|
|
|
DataTable dt = new DataTable();
|
|
|
|
|
|
for (int h = 0; h < iwkX.NumberOfSheets; h++)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
ISheet sheet = iwkX.GetSheetAt(h);
|
|
|
|
|
|
var rows = sheet.GetRowEnumerator();
|
|
|
|
|
|
bool isMove = rows.MoveNext();
|
|
|
|
|
|
//循环sheet
|
|
|
|
|
|
if (isMove)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
while (rows.MoveNext())
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
var row = (IRow)rows.Current;
|
|
|
|
|
|
var dr = dt.NewRow();
|
|
|
|
|
|
for (int i = 0; i < row.LastCellNum; i++)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
dt.Rows.Add(dr);
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
return dt;
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
// .NET 4.8 手动释放资源
|
|
|
|
|
|
if (fs != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
fs.Close();
|
|
|
|
|
|
fs.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (iwkX != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
iwkX = null;
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-23 16:25:57 +08:00
|
|
|
|
/// 上传宜搭方法
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task MqttYiDaUpload()
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
try
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//获取最新的2台设备数据,各取最新1条。
|
|
|
|
|
|
DataTable dtMqtt = await _buttonService.GetLatestMqttDataAsync();
|
|
|
|
|
|
if (dtMqtt == null || dtMqtt.Rows.Count == 0)//有设备数据才会执行
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog("未查询到MQTT设备数据,跳过本次上传");
|
|
|
|
|
|
return;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog($"本次读取到{dtMqtt.Rows.Count}条MQTT设备数据");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
foreach (DataRow dr in dtMqtt.Rows)//遍历dtMqtt中数据,一行代表一个设备的数据,一共2行
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
string deviceCode = dr["device_code"].ToString();
|
|
|
|
|
|
AppendLog($"开始处理设备[{deviceCode}]数据");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
List<DataRow> drPeiFangList = null;
|
|
|
|
|
|
string site = null;
|
|
|
|
|
|
if (deviceCode == "device1")
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
drPeiFangList = _formulaList1;
|
|
|
|
|
|
site = "1号设备";
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
else if (deviceCode == "device2")
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
drPeiFangList = _formulaList2;
|
|
|
|
|
|
site = "2号设备";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (drPeiFangList == null || drPeiFangList.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
AppendLog($"设备[{deviceCode}]未选择配方,跳过");
|
|
|
|
|
|
continue;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//取出一条字段 MESSAGE
|
|
|
|
|
|
string strMessage = dr["receive_data"].ToString();
|
|
|
|
|
|
if (string.IsNullOrEmpty(strMessage))
|
|
|
|
|
|
{
|
|
|
|
|
|
AppendLog($"设备[{deviceCode}]无有效receive_data,跳过");
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//反序列化设备收到的数据,取出里面的参数(@params)。根据MqttDataModel 的定义。
|
|
|
|
|
|
SQLDataModel data = null;
|
|
|
|
|
|
try
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
data = JsonConvert.DeserializeObject<SQLDataModel>(strMessage);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
catch (Exception ex)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog($"设备[{deviceCode}]数据反序列化失败:{ex.Message}");
|
|
|
|
|
|
continue;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//如果反序列化失败或 params 为空,就跳过。
|
|
|
|
|
|
if (data == null || data.@params == null)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog($"设备[{deviceCode}]反序列化后无params数据,跳过");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//获取 @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)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//parameter_name,参数名,比如 "Value01"
|
|
|
|
|
|
string paramName = prop.Name;
|
|
|
|
|
|
//parameter_value,参数值,比如 24.6
|
|
|
|
|
|
object value = prop.GetValue(data.@params);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 跳过空值(null 或 "")
|
|
|
|
|
|
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//查映射表
|
|
|
|
|
|
//如果找到了映射,就替换 name 为中文显示名,
|
|
|
|
|
|
//dtMqttDic在Form1_Load方法中已经被赋值,是字典表中的数据。
|
|
|
|
|
|
DataRow[] drs = _formulaDic.Select("param_code = '" + paramName + "'");
|
|
|
|
|
|
if (drs != null && drs.Length > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
paramName = drs[0]["param_name"].ToString();
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
DataRow drMatch = drPeiFangList.FirstOrDefault(r => r.Field<string>("parameter_name") == paramName);
|
|
|
|
|
|
if (drMatch == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
AppendLog($"设备[{deviceCode}]配方中未找到参数[{paramName}],跳过");
|
|
|
|
|
|
continue; // 没找到匹配行就跳过
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
//构造 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();//零件负责人
|
2026-01-23 16:25:57 +08:00
|
|
|
|
if (paramName == "报废图片路径" && _buttonService._formulaValue == "报废图片路径")
|
|
|
|
|
|
{
|
|
|
|
|
|
yidaNew.imageField_mii5s85z = _uploadService._reformationPicture; //整改图片
|
|
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
yidaNew.textField_mha98nf0 = site; //工位
|
|
|
|
|
|
yidaNew.textField_mha98nfh = drMatch["leader_out_protection"].ToString(); //外保负责人
|
|
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
// 添加时间戳
|
2026-01-22 16:59:41 +08:00
|
|
|
|
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 = "不合格";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-23 16:25:57 +08:00
|
|
|
|
else if (paramName == "开模总数实时" || paramName == "托模次数" || paramName == "报废图片路径")
|
2026-01-22 16:59:41 +08:00
|
|
|
|
{
|
|
|
|
|
|
mqttNew.IsQualification = "合格";
|
|
|
|
|
|
yidaNew.textField_mha98nf5 = "合格";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
// 用参数值(value)比较,而非参数名(name)
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 安全转换参数值、下公差、上公差为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);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
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();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
// 跨线程调用UI相关方法
|
2026-01-22 16:59:41 +08:00
|
|
|
|
this.Invoke(new Action(() =>
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
_uploadService.UploadDatabaseDataToYiDaWithLogging(token, yidaLists1, mqttLists, this);
|
2026-01-22 16:59:41 +08:00
|
|
|
|
}));
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog($"设备[{deviceCode}]成功上传{len}条数据到宜搭");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
else
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog($"设备[{deviceCode}]无有效参数数据可上传");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-22 16:59:41 +08:00
|
|
|
|
catch (Exception ex)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
AppendLog($"MqttYiDaUpload执行失败:{ex.Message}");
|
|
|
|
|
|
// 跨线程显示错误提示(.NET 4.8 兼容写法)
|
|
|
|
|
|
this.Invoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"上传失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
}));
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================== 日志相关方法 ========================================
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// 接收 MQTT 消息后,将消息日志显示到 UI 控件上(.NET 4.8 兼容版)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="msg"></param>
|
|
|
|
|
|
private void OnMqttMessage(string msg)
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
if (this.InvokeRequired)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
this.Invoke(new Action<string>(OnMqttMessage), msg);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AppendLog($"收到消息: {msg}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// 写入日志到文本框并保存到文件(.NET 4.8 兼容版)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void AppendLog(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
if (this.InvokeRequired)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
this.Invoke(new Action<string>(AppendLog), message);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化带时间戳的日志
|
|
|
|
|
|
string timeStamped = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\r\n";
|
|
|
|
|
|
|
|
|
|
|
|
// 保存到日志文件
|
|
|
|
|
|
SaveToLogFile(timeStamped);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到文本框并限制行数
|
|
|
|
|
|
textBoxLog.AppendText(timeStamped);
|
|
|
|
|
|
LimitLogLines(500);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 静默处理日志写入错误,避免影响主流程
|
|
|
|
|
|
Console.WriteLine($"日志处理错误: {ex.Message}");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// 将日志保存到文件(.NET 4.8 兼容版)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void SaveToLogFile(string logContent)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateLogFile(); // 确保使用当天的日志文件
|
2026-01-22 16:59:41 +08:00
|
|
|
|
// 追加写入日志,使用UTF8编码避免中文乱码(.NET 4.8 显式指定编码)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
File.AppendAllText(_currentLogFile, logContent, System.Text.Encoding.UTF8);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 文件写入错误处理
|
2026-01-22 16:59:41 +08:00
|
|
|
|
Console.WriteLine($"日志文件写入错误: {ex.Message}");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-22 16:59:41 +08:00
|
|
|
|
/// 限制日志文本框的最大行数(.NET 4.8 兼容版)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
/// </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();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|