1019 lines
38 KiB
C#
1019 lines
38 KiB
C#
using YiDa_WinForm.Model;
|
||
using Newtonsoft.Json;
|
||
using NPOI.SS.UserModel;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using System.Timers;
|
||
using System.Windows.Forms;
|
||
using MQTT_WinformV1.Service;
|
||
using YiDa_WinForm.Config;
|
||
using YiDa_WinForm.Service.Mqtt;
|
||
|
||
namespace YiDa_WinForm
|
||
{
|
||
public partial class MainForm : Form
|
||
{
|
||
// 依赖注入
|
||
private readonly MqttClientService _mqttService;
|
||
private readonly YiDaUploadService _uploadService;
|
||
private readonly ButtonOperationService _buttonService;
|
||
|
||
// 配方配置
|
||
private DataTable _formula; // 配方缓存
|
||
private DataTable _immFormulaDic;
|
||
private DataTable _tcFormulaDic;
|
||
private List<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();
|
||
_immFormulaDic = new DataTable();
|
||
_tcFormulaDic = 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 LoadInjectionMqttDic();
|
||
await LoadTemperatureMqttDic();
|
||
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 LoadInjectionMqttDic()
|
||
{
|
||
DataTable dt = await _buttonService.InitInjectionMqttDic();
|
||
if (dt != null)
|
||
{
|
||
_immFormulaDic = dt;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载温控器MQTT字典
|
||
/// </summary>
|
||
private async Task LoadTemperatureMqttDic()
|
||
{
|
||
DataTable dt = await _buttonService.InitTemperatureMqttDic();
|
||
if (dt != null)
|
||
{
|
||
_tcFormulaDic = 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
|
||
{
|
||
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();
|
||
if (openFile.ShowDialog() == DialogResult.OK)
|
||
{
|
||
string filePath = openFile.FileName;
|
||
DataTable excelDt = GetExcel(filePath);
|
||
if (excelDt != null && excelDt.Rows.Count > 0)
|
||
{
|
||
await _buttonService.SaveFormulaByExcel(excelDt);
|
||
await LoadFormula();
|
||
MessageBox.Show("配方信息已导入!");
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("不允许导入空表!");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"配方导入失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <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网关!");
|
||
return;
|
||
}
|
||
|
||
if (this.comboBox1.Text.Length == 0 && this.comboBox2.Text.Length == 0)
|
||
{
|
||
//如果两个下拉框都还没数据,说明没导入过配方,直接中止。
|
||
MessageBox.Show("请先导入配方信息,再刷新!");
|
||
return;
|
||
}
|
||
|
||
this.button2.Enabled = false;
|
||
|
||
// 设备1选择配方
|
||
string selected1 = this.comboBox1.SelectedValue?.ToString();
|
||
if (string.IsNullOrEmpty(selected1) || selected1 == "-1")
|
||
{
|
||
_formulaList1 = null;
|
||
}
|
||
else
|
||
{
|
||
string[] arr1 = selected1.Split('|');
|
||
if (arr1.Length == 2)
|
||
{
|
||
string partNumber1 = arr1[0];
|
||
string partName1 = arr1[1];
|
||
_formulaList1 = _formula.AsEnumerable()
|
||
.Where(r => r.Field<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;
|
||
}
|
||
}
|
||
|
||
double seconds;
|
||
if (!double.TryParse(txtLUploadPL.Text, out seconds) || seconds <= 0)
|
||
{
|
||
seconds = 10; //控制上传间隔,默认10秒。
|
||
}
|
||
|
||
// 立即上传一次
|
||
await MqttYiDaUpload();
|
||
|
||
_timer = new System.Timers.Timer(seconds * 1000);
|
||
_timer.Elapsed += async (s, evt) => { await MqttYiDaUpload(); };
|
||
_timer.AutoReset = true; // 是否重复
|
||
_timer.Enabled = true; // 开始计时
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.button2.Enabled = true;
|
||
AppendLog($"上传宜搭失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新配方
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
private void btnRefresh_Click(object sender, EventArgs e)
|
||
{
|
||
LoadFormula();
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
|
||
AppendLog("停止上传数据");
|
||
MessageBox.Show("待程序完成后,将停止自动上传", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
|
||
/// <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中
|
||
/// </summary>
|
||
/// <param name="filePath">Excel路径</param>
|
||
/// <returns>返回DataTable数据</returns>
|
||
private static DataTable GetExcel(string filePath)
|
||
{
|
||
IWorkbook iwkX;
|
||
using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||
{
|
||
iwkX = WorkbookFactory.Create(fs);
|
||
fs.Close();
|
||
}
|
||
|
||
//sheet
|
||
DataTable dt = new DataTable();
|
||
for (int h = 0; h < iwkX.NumberOfSheets; h++)
|
||
{
|
||
ISheet sheet = iwkX.GetSheetAt(h);
|
||
var rows = sheet.GetRowEnumerator();
|
||
bool isMove = rows.MoveNext();
|
||
//循环sheet
|
||
if (isMove)
|
||
{
|
||
var Cols = (IRow)rows.Current;
|
||
dt.TableName = sheet.SheetName;
|
||
for (int i = 0; i < Cols.LastCellNum; i++)
|
||
{
|
||
string str = Cols.GetCell(i).ToString();
|
||
dt.Columns.Add(Cols.GetCell(i).ToString());
|
||
}
|
||
|
||
while (rows.MoveNext())
|
||
{
|
||
var row = (IRow)rows.Current;
|
||
var dr = dt.NewRow();
|
||
for (int i = 0; i < row.LastCellNum; i++)
|
||
{
|
||
var cell = row.GetCell(i);
|
||
if (cell == null)
|
||
{
|
||
dr[i] = "";
|
||
}
|
||
else
|
||
{
|
||
string strdr = cell.ToString();
|
||
dr[i] = cell.ToString();
|
||
}
|
||
}
|
||
|
||
dt.Rows.Add(dr);
|
||
}
|
||
}
|
||
}
|
||
|
||
return dt;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传后续
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
private void _timer_Tick(object sender, ElapsedEventArgs e)
|
||
{
|
||
MqttYiDaUpload();
|
||
AppendLog("宜搭数据已上传!");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传宜搭方法
|
||
/// </summary>
|
||
private async Task MqttYiDaUpload()
|
||
{
|
||
// 1. 查询4个设备的最新MQTT数据
|
||
DataTable mqttData = await _buttonService.GetLatestMqttDataAsync();
|
||
if (mqttData == null || mqttData.Rows.Count == 0)
|
||
{
|
||
AppendLog("未查询到任何设备的MQTT数据,跳过上传");
|
||
return;
|
||
}
|
||
|
||
// 存储所有设备的Model(后续按part_number排序)
|
||
List<MqttModel> allMqttModels = new List<MqttModel>();
|
||
List<YiDaModel> allYiDaModels = new List<YiDaModel>();
|
||
|
||
// 2. 遍历4个设备的每条数据,逐个参数生成Model
|
||
foreach (DataRow row in mqttData.Rows)
|
||
{
|
||
string deviceCode = row["device_code"].ToString();
|
||
if (string.IsNullOrWhiteSpace(deviceCode))
|
||
{
|
||
AppendLog($"发现空设备编码数据,跳过");
|
||
continue;
|
||
}
|
||
|
||
// ========== 设备基础配置 ==========
|
||
List<DataRow> formulaList = null;
|
||
string site = null;
|
||
DeviceType deviceType = DeviceType.InjectionMoldingMachine;
|
||
DataTable currentFormulaDic = null;
|
||
|
||
// 按设备编码分配配置
|
||
switch (deviceCode)
|
||
{
|
||
case "device1":
|
||
formulaList = _formulaList1;
|
||
site = "1号注塑机";
|
||
deviceType = DeviceType.InjectionMoldingMachine;
|
||
currentFormulaDic = _immFormulaDic;
|
||
break;
|
||
case "device2":
|
||
formulaList = _formulaList2;
|
||
site = "2号注塑机";
|
||
deviceType = DeviceType.InjectionMoldingMachine;
|
||
currentFormulaDic = _immFormulaDic;
|
||
break;
|
||
case "device3":
|
||
formulaList = _formulaList1;
|
||
site = "1号温控器";
|
||
deviceType = DeviceType.TemperatureController;
|
||
currentFormulaDic = _tcFormulaDic;
|
||
break;
|
||
case "device4":
|
||
formulaList = _formulaList2;
|
||
site = "2号温控器";
|
||
deviceType = DeviceType.TemperatureController;
|
||
currentFormulaDic = _tcFormulaDic;
|
||
break;
|
||
default:
|
||
AppendLog($"未知设备编码:{deviceCode},跳过该设备数据");
|
||
continue;
|
||
}
|
||
|
||
// 校验配方和字典表
|
||
if (formulaList == null || formulaList.Count == 0)
|
||
{
|
||
AppendLog($"设备{deviceCode}未选择配方,跳过该设备");
|
||
continue;
|
||
}
|
||
|
||
if (currentFormulaDic == null || currentFormulaDic.Rows.Count == 0)
|
||
{
|
||
AppendLog($"设备{deviceCode}字典表为空,跳过该设备");
|
||
continue;
|
||
}
|
||
|
||
// ========== 反序列化MQTT数据 ==========
|
||
string receiveData = row["receive_data"].ToString();
|
||
if (string.IsNullOrWhiteSpace(receiveData))
|
||
{
|
||
AppendLog($"设备{deviceCode}的receive_data为空,跳过该设备");
|
||
continue;
|
||
}
|
||
|
||
object dataObj = null;
|
||
try
|
||
{
|
||
if (deviceType == DeviceType.InjectionMoldingMachine)
|
||
{
|
||
// device1/2:反序列化为注塑机模型
|
||
dataObj = JsonConvert.DeserializeObject<ImmDataModel>(receiveData);
|
||
}
|
||
else
|
||
{
|
||
// device3/4:反序列化为温控器模型
|
||
dataObj = JsonConvert.DeserializeObject<TcDataModel>(receiveData);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"设备{deviceCode}反序列化失败:{ex.Message},原始数据:{receiveData}");
|
||
continue;
|
||
}
|
||
|
||
// ========== 解析参数(注塑机多参数/温控器PV) ==========
|
||
Dictionary<string, string> paramDict = new Dictionary<string, string>();
|
||
if (deviceType == DeviceType.InjectionMoldingMachine)
|
||
{
|
||
ImmDataModel immData = dataObj as ImmDataModel;
|
||
paramDict = ParseImmParams(immData?.@params);
|
||
}
|
||
else
|
||
{
|
||
TcDataModel tcData = dataObj as TcDataModel;
|
||
paramDict = ParseTcParams(tcData?.@params); // 仅解析PV参数
|
||
}
|
||
|
||
if (paramDict.Count == 0)
|
||
{
|
||
AppendLog($"设备{deviceCode}无有效参数(注塑机参数为空/温控器PV为空),跳过该设备");
|
||
continue;
|
||
}
|
||
|
||
// ========== 逐个参数生成Model ==========
|
||
foreach (var kvp in paramDict)
|
||
{
|
||
string paramCode = kvp.Key; // 原始参数编码(PV/Alarm/T1等)
|
||
string paramValue = kvp.Value; // 参数值
|
||
|
||
// 1. 查字典表获取中文参数名
|
||
DataRow[] drs = currentFormulaDic.Select($"param_code = '{paramCode}'");
|
||
string paramName = paramCode;
|
||
if (drs != null && drs.Length > 0)
|
||
{
|
||
paramName = drs[0]["param_name"].ToString();
|
||
}
|
||
else
|
||
{
|
||
AppendLog($"设备{deviceCode}参数{paramCode}无字典映射,使用原编码作为参数名");
|
||
}
|
||
|
||
// 2. 匹配配方数据
|
||
DataRow drMatch = formulaList.FirstOrDefault(r =>
|
||
r.Field<string>("parameter_name").Equals(paramName, StringComparison.OrdinalIgnoreCase) ||
|
||
r.Field<string>("parameter_name").Equals(paramCode, StringComparison.OrdinalIgnoreCase));
|
||
if (drMatch == null)
|
||
{
|
||
AppendLog($"设备{deviceCode}配方中未找到参数[{paramCode}/{paramName}],跳过该参数");
|
||
continue;
|
||
}
|
||
|
||
// 3. 生成MqttModel(单个参数)
|
||
MqttModel mqttModel = new MqttModel
|
||
{
|
||
SupplierCode = drMatch["supplier_code"].ToString(),
|
||
SupplierName = drMatch["supplier_name"].ToString(),
|
||
VehicleModel = drMatch["vehicle_model"].ToString(),
|
||
PartNumber = drMatch["part_number"].ToString(), // 排序依据
|
||
PartName = drMatch["part_name"].ToString(),
|
||
Configuration = drMatch["configuration"]?.ToString() ?? "",
|
||
ParameterName = paramName,
|
||
ParameterValue = paramValue,
|
||
ToleranceLower = drMatch["tolerance_lower"].ToString(),
|
||
ToleranceUpper = drMatch["tolerance_upper"].ToString(),
|
||
IsQualification = GetQualificationResult(deviceType, paramName, paramValue, drMatch),
|
||
LeaderPart = drMatch["leader_part"].ToString(),
|
||
WorkStation = site,
|
||
LeaderOutProtection = drMatch["leader_out_protection"].ToString()
|
||
};
|
||
|
||
// 4. 生成YiDaModel(单个参数)
|
||
YiDaModel yiDaModel = new YiDaModel
|
||
{
|
||
textField_mha98neu = drMatch["supplier_code"].ToString(),
|
||
textField_mha98nev = drMatch["supplier_name"].ToString(),
|
||
textField_mha98new = drMatch["vehicle_model"].ToString(),
|
||
textField_mha98nex = drMatch["part_number"].ToString(), // 排序依据
|
||
textField_mha98ney = drMatch["part_name"].ToString(),
|
||
textField_mha98nez = drMatch["configuration"]?.ToString() ?? "",
|
||
textField_mha98nf0 = site,
|
||
textField_mha98nf1 = paramName,
|
||
textField_mhx44i2i = paramValue,
|
||
textField_mhx44i2j = drMatch["tolerance_lower"].ToString(),
|
||
textField_mhx44i2k = drMatch["tolerance_upper"].ToString(),
|
||
textField_mha98nf5 = mqttModel.IsQualification,
|
||
textField_mhlvt8ht = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
|
||
textField_mha98nf7 = drMatch["leader_part"].ToString(),
|
||
textField_mha98nfh = drMatch["leader_out_protection"].ToString(),
|
||
// 其他字段赋空值
|
||
textField_mha98nf8 = "",
|
||
textField_mha98nf9 = "",
|
||
textField_mha98nfa = "",
|
||
textField_mha98nfb = "",
|
||
textField_mha98nfc = "",
|
||
textField_mha98nfd = "",
|
||
imageField_mii5s85z = "",
|
||
textField_mha98nff = "",
|
||
textField_mha98nfg = ""
|
||
};
|
||
|
||
// 5. 添加到总列表
|
||
allMqttModels.Add(mqttModel);
|
||
allYiDaModels.Add(yiDaModel);
|
||
}
|
||
}
|
||
|
||
// ========== 核心:按part_number排序(升序) ==========
|
||
if (allMqttModels.Count > 0 && allYiDaModels.Count > 0)
|
||
{
|
||
// 按part_number升序排序(保证两个列表排序一致)
|
||
var sortedModels = allMqttModels
|
||
.Select((mqtt, index) => new { Mqtt = mqtt, YiDa = allYiDaModels[index] })
|
||
.OrderBy(item => item.Mqtt.PartNumber) // 按part_number升序
|
||
.ToList();
|
||
|
||
// 重新赋值排序后的列表
|
||
allMqttModels = sortedModels.Select(item => item.Mqtt).ToList();
|
||
allYiDaModels = sortedModels.Select(item => item.YiDa).ToList();
|
||
|
||
AppendLog($"所有参数Model已按part_number排序,共{allMqttModels.Count}条数据");
|
||
}
|
||
|
||
// ========== 统一上传排序后的Model ==========
|
||
if (allMqttModels.Count > 0 && allYiDaModels.Count > 0)
|
||
{
|
||
string token = _uploadService.GetDingDingToken();
|
||
if (string.IsNullOrEmpty(token))
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(new Action(() => MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret")));
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret");
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
// WinForm跨线程安全上传
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(
|
||
new Action<string, List<YiDaModel>, List<MqttModel>, MainForm>(_uploadService
|
||
.UploadDatabaseDataToYiDaWithLogging),
|
||
token, allYiDaModels, allMqttModels, this);
|
||
}
|
||
else
|
||
{
|
||
_uploadService.UploadDatabaseDataToYiDaWithLogging(token, allYiDaModels, allMqttModels, this);
|
||
}
|
||
|
||
AppendLog($"成功上传{allMqttModels.Count}条参数数据(按part_number排序)");
|
||
}
|
||
else
|
||
{
|
||
AppendLog("无有效参数Model可上传");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 辅助方法:获取合格判定结果
|
||
/// </summary>
|
||
private string GetQualificationResult(DeviceType deviceType, string paramName, string paramValue,
|
||
DataRow drMatch)
|
||
{
|
||
try
|
||
{
|
||
if (deviceType == DeviceType.InjectionMoldingMachine)
|
||
{
|
||
// 注塑机特殊参数判断
|
||
if (paramName == "报警信息" || paramName == "Alarm")
|
||
{
|
||
return string.Equals(paramValue, "无错误", StringComparison.Ordinal) ? "合格" : "不合格";
|
||
}
|
||
else if (paramName == "开模总数实时" || paramName == "托模次数" || paramName == "ProductCounts")
|
||
{
|
||
return "合格";
|
||
}
|
||
}
|
||
|
||
// 数值型参数公差判断(注塑机普通参数/温控器PV)
|
||
if (float.TryParse(paramValue, out float val) &&
|
||
float.TryParse(drMatch["tolerance_lower"].ToString(), out float lower) &&
|
||
float.TryParse(drMatch["tolerance_upper"].ToString(), out float upper))
|
||
{
|
||
return (val >= lower && val <= upper) ? "合格" : "不合格";
|
||
}
|
||
|
||
return "不合格";
|
||
}
|
||
catch
|
||
{
|
||
return "不合格";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析注塑机模型参数
|
||
/// </summary>
|
||
/// <param name="paramsModel"></param>
|
||
/// <returns></returns>
|
||
private Dictionary<string, string> ParseImmParams(ImmParamsModel paramsModel)
|
||
{
|
||
Dictionary<string, string> paramDict = new Dictionary<string, string>();
|
||
if (paramsModel == null) return paramDict;
|
||
|
||
AddParamIfNotEmpty(paramDict, "Alarm", paramsModel.Alarm);
|
||
AddParamIfNotEmpty(paramDict, "ProductCounts", paramsModel.ProductCounts);
|
||
AddParamIfNotEmpty(paramDict, "CT", paramsModel.CT);
|
||
AddParamIfNotEmpty(paramDict, "T1", paramsModel.T1);
|
||
AddParamIfNotEmpty(paramDict, "T2", paramsModel.T2);
|
||
AddParamIfNotEmpty(paramDict, "T3", paramsModel.T3);
|
||
AddParamIfNotEmpty(paramDict, "T4", paramsModel.T4);
|
||
AddParamIfNotEmpty(paramDict, "T5", paramsModel.T5);
|
||
AddParamIfNotEmpty(paramDict, "IP1", paramsModel.IP1);
|
||
AddParamIfNotEmpty(paramDict, "IP2", paramsModel.IP2);
|
||
AddParamIfNotEmpty(paramDict, "IP3", paramsModel.IP3);
|
||
AddParamIfNotEmpty(paramDict, "IP4", paramsModel.IP4);
|
||
AddParamIfNotEmpty(paramDict, "IP5", paramsModel.IP5);
|
||
AddParamIfNotEmpty(paramDict, "IV1", paramsModel.IV1);
|
||
AddParamIfNotEmpty(paramDict, "IV2", paramsModel.IV2);
|
||
AddParamIfNotEmpty(paramDict, "IV3", paramsModel.IV3);
|
||
AddParamIfNotEmpty(paramDict, "IV4", paramsModel.IV4);
|
||
AddParamIfNotEmpty(paramDict, "IV5", paramsModel.IV5);
|
||
AddParamIfNotEmpty(paramDict, "ITT", paramsModel.ITT);
|
||
AddParamIfNotEmpty(paramDict, "PP1", paramsModel.PP1);
|
||
AddParamIfNotEmpty(paramDict, "PP2", paramsModel.PP2);
|
||
AddParamIfNotEmpty(paramDict, "PP3", paramsModel.PP3);
|
||
AddParamIfNotEmpty(paramDict, "PV1", paramsModel.PV1);
|
||
AddParamIfNotEmpty(paramDict, "PV2", paramsModel.PV2);
|
||
AddParamIfNotEmpty(paramDict, "PV3", paramsModel.PV3);
|
||
AddParamIfNotEmpty(paramDict, "PT1", paramsModel.PT1);
|
||
AddParamIfNotEmpty(paramDict, "PT2", paramsModel.PT2);
|
||
AddParamIfNotEmpty(paramDict, "PT3", paramsModel.PT3);
|
||
AddParamIfNotEmpty(paramDict, "CC", paramsModel.CC);
|
||
|
||
return paramDict;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析温控器模型参数
|
||
/// </summary>
|
||
/// <param name="paramsModel"></param>
|
||
/// <returns></returns>
|
||
private Dictionary<string, string> ParseTcParams(TcParamsModel paramsModel)
|
||
{
|
||
Dictionary<string, string> paramDict = new Dictionary<string, string>();
|
||
if (paramsModel == null) return paramDict;
|
||
|
||
// 仅解析PV参数(核心需求)
|
||
AddParamIfNotEmpty(paramDict, "PV", paramsModel.PV);
|
||
|
||
return paramDict;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断参数是否为空
|
||
/// </summary>
|
||
/// <param name="dict"></param>
|
||
/// <param name="key"></param>
|
||
/// <param name="value"></param>
|
||
private void AddParamIfNotEmpty(Dictionary<string, string> dict, string key, string value)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(value))
|
||
{
|
||
dict[key] = value.Trim();
|
||
}
|
||
}
|
||
|
||
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
|
||
{
|
||
}
|
||
|
||
// ======================================== 日志相关方法 ========================================
|
||
|
||
/// <summary>
|
||
/// 接收 MQTT 消息后,将消息日志显示到 UI 控件上
|
||
/// </summary>
|
||
/// <param name="msg"></param>
|
||
private void OnMqttMessage(string msg)
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(new Action<string>(OnMqttMessage), msg);
|
||
return;
|
||
}
|
||
|
||
AppendLog($"收到消息: {msg}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 写入日志到文本框并保存到文件
|
||
/// </summary>
|
||
public void AppendLog(string message)
|
||
{
|
||
try
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(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)
|
||
{
|
||
// 可以在这里添加错误处理,例如写入错误日志
|
||
// MessageBox.Show($"日志处理错误: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将日志保存到文件
|
||
/// </summary>
|
||
private void SaveToLogFile(string logContent)
|
||
{
|
||
try
|
||
{
|
||
UpdateLogFile(); // 确保使用当天的日志文件
|
||
// 追加写入日志,使用UTF8编码避免中文乱码
|
||
File.AppendAllText(_currentLogFile, logContent, System.Text.Encoding.UTF8);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 文件写入错误处理
|
||
// MessageBox.Show($"日志文件写入错误: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 限制日志文本框的最大行数
|
||
/// </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();
|
||
}
|
||
}
|
||
}
|
||
} |