1444 lines
60 KiB
C#
1444 lines
60 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Threading.Tasks;
|
||
using System.Timers;
|
||
using System.Windows.Forms;
|
||
using MySql.Data.MySqlClient;
|
||
using Newtonsoft.Json;
|
||
using YiDa_WinForm.Config;
|
||
using YiDa_WinForm.Model;
|
||
using YiDa_WinForm.Service;
|
||
using YiDa_WinForm.Utils;
|
||
using NPOI.SS.UserModel;
|
||
|
||
namespace YiDa_WinForm
|
||
{
|
||
public partial class MainForm : Form
|
||
{
|
||
// 依赖注入
|
||
private readonly MqttClientService _mqttService;
|
||
private readonly YiDaUploadService _uploadService;
|
||
private readonly ButtonOperationService _buttonService;
|
||
|
||
// 通用上传定时器
|
||
private System.Timers.Timer _timer;
|
||
|
||
// 日志文件相关路径
|
||
private readonly string _logDirectory = "log";
|
||
private string _currentLogFile;
|
||
private DateTime _lastLogDate = DateTime.MinValue;
|
||
|
||
// 工作时间配置
|
||
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;
|
||
|
||
// 配方
|
||
private DataTable _formulas; // 原始配方表(含多行,未去重)
|
||
private DataTable _dtMqttDic; // MQTT 参数字典表
|
||
private List<DataRow> _formulasList1; // 设备1选择的配方(多行)
|
||
private List<DataRow> _formulasList2; // 设备2选择的配方(多行)
|
||
|
||
// 生产相关标记
|
||
private static bool _isProduction = false;
|
||
private static bool _isRodImageUploaded = false; // 连杆图片是否上传
|
||
|
||
private static bool _isScrapImageUploaded = false; // 报废图片是否上传
|
||
|
||
// 报废间隔
|
||
private int _lastScrapInterval = 60; // 关键:这个变量在主窗体生命周期内不会销毁,能保留值
|
||
|
||
// 连杆间隔
|
||
private int _lastStrengthInterval = 60;
|
||
|
||
// 持久化保存两个弹窗的上次勾选状态(核心:再次打开弹窗保留用户选择)
|
||
private bool _lastScrapUploadSelected = true; // 报废:上次是否勾选「上传凭证」
|
||
private bool _lastScrapTimedAlarmSelected = true; // 报废:上次是否勾选「定时报警」
|
||
private bool _lastRodUploadSelected = true; // 连杆:上次是否勾选「模具上传」
|
||
private bool _lastRodTimedAlarmSelected = true; // 连杆:上次是否勾选「定时报警」
|
||
|
||
// 早晚固定时段连杆上传报警相关
|
||
private System.Timers.Timer _morningRodAlarmTimer; // 早上检查点定时器(工作开始+30分钟)
|
||
private System.Timers.Timer _eveningRodAlarmTimer; // 晚上检查点定时器(工作结束-30分钟)
|
||
private bool _isMorningRodUploaded = false; // 早上标记(工作开始后30分钟前是否上传)
|
||
private bool _isEveningRodUploaded = false; // 晚上标记(工作结束前30分钟前是否上传)
|
||
private TimeSpan _morningCheckTime; // 早上检查节点(_workStartTime + 30分钟)
|
||
private TimeSpan _eveningCheckTime; // 晚上检查节点(_workEndTime - 30分钟)
|
||
|
||
// ======================================== 初始化相关方法 ========================================
|
||
public MainForm()
|
||
{
|
||
// 初始化设计器方法
|
||
InitializeComponent();
|
||
|
||
// 订阅日志事件
|
||
LogHelper.OnLogGenerated += LogHelper_OnLogGenerated;
|
||
|
||
// 初始化服务实例
|
||
_buttonService = new ButtonOperationService();
|
||
_mqttService = new MqttClientService();
|
||
_uploadService = new YiDaUploadService();
|
||
|
||
_buttonService.MessageReceived += (msg) => { LogHelper.AppendLog($"服务层消息:{msg}"); };
|
||
|
||
// 绑定回调事件(连杆+报废)
|
||
_mqttService.MessageReceived += OnMqttMessage;
|
||
_uploadService.RodUploadStatusChanged += OnRodUploadStatusChanged;
|
||
_uploadService.ScrapUploadStatusChanged += OnScrapUploadStatusChanged;
|
||
|
||
// UI初始化配置(
|
||
buttonDisconnect.Enabled = false;
|
||
|
||
// 初始化日志目录
|
||
InitializeLogDirectory();
|
||
|
||
// 初始化配方相关变量
|
||
_formulas = new DataTable();
|
||
_dtMqttDic = new DataTable();
|
||
_formulasList1 = new List<DataRow>();
|
||
_formulasList2 = new List<DataRow>();
|
||
|
||
InitDailyRodCheckTimes();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化日志目录
|
||
/// </summary>
|
||
private void InitializeLogDirectory()
|
||
{
|
||
if (!Directory.Exists(_logDirectory))
|
||
{
|
||
Directory.CreateDirectory(_logDirectory);
|
||
}
|
||
|
||
UpdateLogFile();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新当前日志文件(每日一个新文件)
|
||
/// </summary>
|
||
private void UpdateLogFile()
|
||
{
|
||
DateTime today = DateTime.Now.Date;
|
||
if (today != _lastLogDate)
|
||
{
|
||
_lastLogDate = today;
|
||
_currentLogFile = Path.Combine(_logDirectory, $"log_{today:yyyyMMdd}.txt");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载MainForm信息
|
||
/// </summary>
|
||
private async void Form1_Load(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
MakePanelRound(panelLed);
|
||
await InitFormulas(); // 移植:加载配方
|
||
InitMqttDic(); // 移植:加载MQTT字典
|
||
timer1.Enabled = true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"初始化失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将指定 Panel 变成圆形
|
||
/// </summary>
|
||
private static void MakePanelRound(Panel panel)
|
||
{
|
||
int size = Math.Min(panel.Width, panel.Height);
|
||
panel.Size = new Size(size, size);
|
||
GraphicsPath path = new GraphicsPath();
|
||
path.AddEllipse(0, 0, panel.Width, panel.Height);
|
||
panel.Region = new Region(path);
|
||
}
|
||
|
||
// ======================================== 配方核心方法 ========================================
|
||
/// <summary>
|
||
/// 查询配方并去重
|
||
/// </summary>
|
||
private async Task InitFormulas()
|
||
{
|
||
DataTable dt = await _buttonService.QueryFormulaAsync();
|
||
if (dt != null && dt.Rows.Count > 0)
|
||
{
|
||
// 新增列用于拼接 part_number|part_name
|
||
if (!dt.Columns.Contains("part_number_name"))
|
||
dt.Columns.Add("part_number_name", typeof(string));
|
||
|
||
foreach (DataRow row in dt.Rows)
|
||
{
|
||
row["part_number_name"] = row["part_number"].ToString() + "|" + row["part_name"].ToString();
|
||
}
|
||
|
||
// 使用 LINQ 去重,按 part_number + part_name
|
||
var uniqueParts = dt.AsEnumerable()
|
||
.GroupBy(r => r["part_number_name"].ToString())
|
||
.Select(g => g.First())
|
||
.ToList();
|
||
|
||
// 新建 DataTable 用于绑定下拉框
|
||
DataTable dtUnique = dt.Clone(); // 保留原有列
|
||
foreach (var row in uniqueParts)
|
||
{
|
||
dtUnique.ImportRow(row);
|
||
}
|
||
|
||
// 新建空白行
|
||
DataRow drNew = dtUnique.NewRow();
|
||
drNew["id"] = -1;
|
||
drNew["part_number_name"] = ""; // 空行值
|
||
dtUnique.Rows.InsertAt(drNew, 0);
|
||
|
||
_formulas = dt; // 原始配方表(多行)
|
||
bsPF.DataSource = dtUnique;
|
||
bsPF2.DataSource = dtUnique;
|
||
|
||
comboBox1.DisplayMember = "part_number_name"; // 显示“零件号|零件名”
|
||
comboBox1.ValueMember = "part_number_name";
|
||
comboBox2.DisplayMember = "part_number_name";
|
||
comboBox2.ValueMember = "part_number_name";
|
||
|
||
// 自定义显示格式:显示“零件号 - 零件名”
|
||
comboBox1.Format += (s, e) =>
|
||
{
|
||
if (e.ListItem is DataRowView drv)
|
||
{
|
||
e.Value = string.IsNullOrEmpty(drv["part_number_name"].ToString())
|
||
? ""
|
||
: drv["part_number"].ToString() + " - " + drv["part_name"].ToString();
|
||
}
|
||
};
|
||
comboBox2.Format += (s, e) =>
|
||
{
|
||
if (e.ListItem is DataRowView drv)
|
||
{
|
||
e.Value = string.IsNullOrEmpty(drv["part_number_name"].ToString())
|
||
? ""
|
||
: drv["part_number"].ToString() + " - " + drv["part_name"].ToString();
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移植:初始化MQTT字典(第二份 Form1 的 InitMqttDic() 完整迁移)
|
||
/// </summary>
|
||
private async void InitMqttDic()
|
||
{
|
||
DataTable dt = await _buttonService.InitMqttDic(); // 适配第一份的 _buttonService,替换第二份的 _mqttService
|
||
if (dt != null)
|
||
{
|
||
_dtMqttDic = dt;
|
||
}
|
||
}
|
||
|
||
// ======================================== 按钮相关方法 ========================================
|
||
/// <summary>
|
||
/// 网关连接按钮
|
||
/// </summary>
|
||
private async void buttonConnect_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
LogHelper.AppendLog("正在连接MQTT服务器...");
|
||
await _mqttService.MqttClientStartAsync();
|
||
LogHelper.AppendLog("连接成功!");
|
||
panelLed.BackColor = Color.Green;
|
||
toolStripStatusLabel3.Text = "已连接";
|
||
buttonConnect.Enabled = false;
|
||
buttonDisconnect.Enabled = true;
|
||
|
||
// 1. 重置静态上传标记(每次连接都重新开始判断)
|
||
_isRodImageUploaded = false;
|
||
_isScrapImageUploaded = false;
|
||
LogHelper.AppendLog("重置上传标记:连杆未上传、报废未上传");
|
||
|
||
// 2. 启动一次性定时任务(复用用户保存的间隔值)
|
||
StartOneTimeAlarmTimers();
|
||
|
||
StartDailyRodFixedTimers();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
panelLed.BackColor = Color.Red;
|
||
LogHelper.AppendLog($"连接失败:{ex.Message}");
|
||
toolStripStatusLabel3.Text = "连接失败";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 网关断开按钮
|
||
/// </summary>
|
||
private async void buttonDisconnect_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await _mqttService.MqttClientStopAsync();
|
||
LogHelper.AppendLog("已断开连接");
|
||
panelLed.BackColor = Color.Red;
|
||
toolStripStatusLabel3.Text = "未连接";
|
||
buttonConnect.Enabled = true;
|
||
buttonDisconnect.Enabled = false;
|
||
|
||
StopAllAlarmTimers();
|
||
|
||
StopDailyRodFixedTimers();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"无法断开连接:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配方导入按钮
|
||
/// </summary>
|
||
private async void button1_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
OpenFileDialog openFile = new OpenFileDialog();
|
||
openFile.Filter = "Excel文件|*.xlsx;*.xls";
|
||
if (openFile.ShowDialog() != DialogResult.OK)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 显示加载提示
|
||
this.Cursor = Cursors.WaitCursor;
|
||
LogHelper.AppendLog("正在导入配方,请稍候...");
|
||
|
||
string filePath = openFile.FileName;
|
||
// 优化:使用 Task.Run 异步执行,不阻塞UI线程
|
||
DataTable excelDt = await Task.Run(() => ExcelHelper.GetExcel(filePath));
|
||
|
||
if (excelDt == null || excelDt.Rows.Count == 0)
|
||
{
|
||
MessageBox.Show("Excel解析后无有效数据,不允许导入!");
|
||
LogHelper.AppendLog("Excel无有效数据,导入中止");
|
||
return;
|
||
}
|
||
|
||
// excel导入列
|
||
string[] requiredColumns = { "供应商代码", "供应商名称", "车型", "零件号", "零件名", "参数名" };
|
||
var missingColumns = requiredColumns.Where(col => !excelDt.Columns.Contains(col)).ToList();
|
||
if (missingColumns.Count > 0)
|
||
{
|
||
string msg = $"Excel缺少必要列:{string.Join("、", missingColumns)},导入失败";
|
||
MessageBox.Show(msg);
|
||
LogHelper.AppendLog(msg);
|
||
return;
|
||
}
|
||
|
||
// 异步执行批量插入
|
||
await _buttonService.SaveFormulaByExcel(excelDt);
|
||
// 异步刷新配方
|
||
await InitFormulas();
|
||
|
||
MessageBox.Show("配方信息已导入!");
|
||
LogHelper.AppendLog($"配方导入成功:{Path.GetFileName(filePath)},共{excelDt.Rows.Count}行");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"配方导入失败:{ex.Message}");
|
||
MessageBox.Show($"导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
finally
|
||
{
|
||
// 恢复光标
|
||
this.Cursor = Cursors.Default;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传宜搭按钮(第一份原有,仅替换配方筛选逻辑为第二份的多行筛选)
|
||
/// </summary>
|
||
private async void button2_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (toolStripStatusLabel3.Text != "已连接") //如果 MQTT 没连接,就不允许上传。
|
||
{
|
||
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")
|
||
{
|
||
_formulasList1 = null;
|
||
}
|
||
else
|
||
{
|
||
string[] arr1 = selected1.Split('|');
|
||
if (arr1.Length == 2)
|
||
{
|
||
string partNumber1 = arr1[0];
|
||
string partName1 = arr1[1];
|
||
_formulasList1 = _formulas.AsEnumerable()
|
||
.Where(r => r.Field<string>("part_number") == partNumber1
|
||
&& r.Field<string>("part_name") == partName1)
|
||
.ToList();
|
||
}
|
||
else
|
||
{
|
||
_formulasList1 = null;
|
||
}
|
||
}
|
||
|
||
// 设备2选择配方
|
||
string selected2 = this.comboBox2.SelectedValue?.ToString();
|
||
if (string.IsNullOrEmpty(selected2) || selected2 == "-1")
|
||
{
|
||
_formulasList2 = null;
|
||
}
|
||
else
|
||
{
|
||
string[] arr2 = selected2.Split('|');
|
||
if (arr2.Length == 2)
|
||
{
|
||
string partNumber2 = arr2[0];
|
||
string partName2 = arr2[1];
|
||
_formulasList2 = _formulas.AsEnumerable()
|
||
.Where(r => r.Field<string>("part_number") == partNumber2
|
||
&& r.Field<string>("part_name") == partName2)
|
||
.ToList();
|
||
}
|
||
else
|
||
{
|
||
_formulasList2 = 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; // 开始计时
|
||
|
||
LogHelper.AppendLog($"自动上传已启动,间隔:{seconds}秒");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
this.button2.Enabled = true;
|
||
LogHelper.AppendLog($"上传宜搭失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 中止上传按钮
|
||
/// </summary>
|
||
private void button3_Click(object sender, EventArgs e)
|
||
{
|
||
button2.Enabled = true;
|
||
if (_timer != null)
|
||
{
|
||
_timer.Enabled = false;
|
||
_timer.Dispose();
|
||
_timer = null;
|
||
LogHelper.AppendLog("已停止自动上传数据");
|
||
MessageBox.Show("已停止自动上传", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("无运行中的上传定时器");
|
||
MessageBox.Show("当前无自动上传任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 报废上传凭证按钮
|
||
/// </summary>
|
||
private async void button4_Click(object sender, EventArgs e)
|
||
{
|
||
// 传入上次的勾选状态+间隔值(核心:持久化用户选择)
|
||
using (var scrapForm = new ScrapUploadForm(_lastScrapUploadSelected, _lastScrapTimedAlarmSelected,
|
||
_lastScrapInterval))
|
||
{
|
||
DialogResult result = scrapForm.ShowDialog(this);
|
||
|
||
if (result == DialogResult.OK)
|
||
{
|
||
// 1. 提取用户本次选择结果
|
||
bool isScrapUpload = scrapForm.IsScrapUploadSelected;
|
||
bool isScrapTimedAlarm = scrapForm.IsScrapTimedAlarmSelected;
|
||
int interval = scrapForm.ScrapTimedInterval;
|
||
|
||
// 2. 持久化保存用户选择(核心:下次打开弹窗保留本次状态)
|
||
_lastScrapUploadSelected = isScrapUpload;
|
||
_lastScrapTimedAlarmSelected = isScrapTimedAlarm;
|
||
_lastScrapInterval = interval;
|
||
|
||
// 3. 上传逻辑:仅当勾选「上传凭证」时,才弹出文件选择框(分离上传和报警逻辑)
|
||
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);
|
||
LogHelper.AppendLog(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!");
|
||
if (isImageUploadSuccess)
|
||
{
|
||
_buttonService.MergeAndSaveData("1");
|
||
// 上传成功:直接标记为已上传(避免后续定时报警)
|
||
_isScrapImageUploaded = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("不允许上传空文件!");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4. 仅日志提示(报警逻辑已移到网关连接时,此处不启动定时)
|
||
LogHelper.AppendLog(
|
||
$"选择结果:报废凭证上传={isScrapUpload}(上传结果={isImageUploadSuccess}),定时上传报警={isScrapTimedAlarm},时间间隔={interval}分钟");
|
||
|
||
if (isScrapTimedAlarm)
|
||
{
|
||
MessageBox.Show("报废定时报警已启用,连接MQTT网关后将自动启动定时任务", "提示", MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("报废定时报警已禁用,连接MQTT网关后将不启动该定时任务", "提示", MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连杆测试上传按钮
|
||
/// </summary>
|
||
private async void button5_Click(object sender, EventArgs e)
|
||
{
|
||
// 传入上次的勾选状态+间隔值(核心:持久化用户选择)
|
||
using (var selectForm = new StrengthTestUploadForm(_lastRodUploadSelected, _lastRodTimedAlarmSelected,
|
||
_lastStrengthInterval))
|
||
{
|
||
DialogResult result = selectForm.ShowDialog(this);
|
||
|
||
if (result == DialogResult.OK)
|
||
{
|
||
// 1. 提取用户本次选择结果
|
||
bool isMoldUpload = selectForm.IsMoldProductionSelected;
|
||
bool isTimedAlarm = selectForm.IsTimedAlarmSelected;
|
||
int interval = selectForm.TimedInterval;
|
||
|
||
// 2. 持久化保存用户选择(核心:下次打开弹窗保留本次状态)
|
||
_lastRodUploadSelected = isMoldUpload;
|
||
_lastRodTimedAlarmSelected = isTimedAlarm;
|
||
_lastStrengthInterval = interval;
|
||
|
||
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
|
||
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);
|
||
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
|
||
if (isImageUploadSuccess)
|
||
{
|
||
_buttonService.MergeAndSaveData("2");
|
||
// 上传成功:直接标记为已上传(避免后续定时报警)
|
||
_isRodImageUploaded = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("不允许上传空文件!");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4. 仅日志提示(报警逻辑已移到网关连接时,此处不启动定时)
|
||
LogHelper.AppendLog(
|
||
$"选择结果:模具投产上传={isMoldUpload}(上传结果={isImageUploadSuccess}),定时上传报警={isTimedAlarm},时间间隔={interval}分钟");
|
||
|
||
if (isTimedAlarm)
|
||
{
|
||
MessageBox.Show("连杆定时报警已启用,连接MQTT网关后将自动启动定时任务", "提示", MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("连杆定时报警已禁用,连接MQTT网关后将不启动该定时任务", "提示", MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置按钮
|
||
/// </summary>
|
||
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;
|
||
|
||
// ========== 保留:重新初始化早晚连杆检查节点 ==========
|
||
InitDailyRodCheckTimes();
|
||
|
||
// ========== 修改:如果网关已连接,重启早晚固定检查定时器 ==========
|
||
if (toolStripStatusLabel3.Text == "已连接")
|
||
{
|
||
StartDailyRodFixedTimers();
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭MainForm按钮
|
||
/// </summary>
|
||
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();
|
||
}
|
||
StopDailyRodFixedTimers();
|
||
|
||
await _mqttService.MqttClientStopAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"关闭窗口失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新配方按钮
|
||
/// </summary>
|
||
private async void btnRefresh_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await InitFormulas();
|
||
InitMqttDic();
|
||
MessageBox.Show("配方已刷新!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"刷新配方失败:{ex.Message}");
|
||
MessageBox.Show($"刷新配方失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传间隔输入框校验
|
||
/// </summary>
|
||
private void txtLUploadPL_KeyPress(object sender, KeyPressEventArgs e)
|
||
{
|
||
if (e.KeyChar == (char)Keys.Back)
|
||
{
|
||
e.Handled = false;
|
||
return;
|
||
}
|
||
|
||
if (char.IsDigit(e.KeyChar) || (e.KeyChar == '.' && !txtLUploadPL.Text.Contains(".")))
|
||
{
|
||
e.Handled = false;
|
||
}
|
||
else
|
||
{
|
||
e.Handled = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新时间显示
|
||
/// </summary>
|
||
private void timer1_Tick(object sender, EventArgs e)
|
||
{
|
||
label4.Text = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
|
||
}
|
||
|
||
// ======================================== 定时报警核心方法 ========================================
|
||
|
||
/// <summary>
|
||
/// 新增:启动连杆+报废的一次性定时任务(仅当用户勾选了对应报警时才启动)
|
||
/// </summary>
|
||
private void StartOneTimeAlarmTimers()
|
||
{
|
||
// 1. 连杆测试一次性定时(仅当用户勾选了连杆报警时启动)
|
||
if (_lastRodTimedAlarmSelected)
|
||
{
|
||
if (_rodAlarmTimer != null)
|
||
{
|
||
_rodAlarmTimer.Enabled = false;
|
||
_rodAlarmTimer.Dispose();
|
||
}
|
||
|
||
int rodIntervalMs = _lastStrengthInterval * 60 * 1000; // 分钟 → 毫秒
|
||
_rodAlarmTimer = new System.Timers.Timer(rodIntervalMs);
|
||
_rodAlarmTimer.AutoReset = false; // 关键:一次性执行,不循环
|
||
_rodAlarmTimer.Elapsed += RodAlarmTimer_Elapsed;
|
||
_rodAlarmTimer.Enabled = true; // 启动定时
|
||
LogHelper.AppendLog($"连杆测试一次性报警定时已启动:若{_lastStrengthInterval}分钟内未上传图片,将触发钉钉报警");
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("用户未勾选连杆定时报警,跳过连杆定时任务启动");
|
||
}
|
||
|
||
// 2. 报废上传一次性定时(仅当用户勾选了报废报警时启动)
|
||
if (_lastScrapTimedAlarmSelected)
|
||
{
|
||
if (_scrapAlarmTimer != null)
|
||
{
|
||
_scrapAlarmTimer.Enabled = false;
|
||
_scrapAlarmTimer.Dispose();
|
||
}
|
||
|
||
int scrapIntervalMs = _lastScrapInterval * 60 * 1000; // 分钟 → 毫秒
|
||
_scrapAlarmTimer = new System.Timers.Timer(scrapIntervalMs);
|
||
_scrapAlarmTimer.AutoReset = false; // 关键:一次性执行,不循环
|
||
_scrapAlarmTimer.Elapsed += ScrapAlarmTimer_Elapsed;
|
||
_scrapAlarmTimer.Enabled = true; // 启动定时
|
||
LogHelper.AppendLog($"报废上传一次性报警定时已启动:若{_lastScrapInterval}分钟内未上传凭证,将触发钉钉报警");
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("用户未勾选报废定时报警,跳过报废定时任务启动");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 终止所有报警定时任务并重置标记
|
||
/// </summary>
|
||
private void StopAllAlarmTimers()
|
||
{
|
||
// 1. 终止连杆定时
|
||
if (_rodAlarmTimer != null)
|
||
{
|
||
_rodAlarmTimer.Enabled = false;
|
||
_rodAlarmTimer.Dispose();
|
||
_rodAlarmTimer = null;
|
||
LogHelper.AppendLog("连杆测试报警定时已终止(网关断开)");
|
||
}
|
||
|
||
// 2. 终止报废定时
|
||
if (_scrapAlarmTimer != null)
|
||
{
|
||
_scrapAlarmTimer.Enabled = false;
|
||
_scrapAlarmTimer.Dispose();
|
||
_scrapAlarmTimer = null;
|
||
LogHelper.AppendLog("报废上传报警定时已终止(网关断开)");
|
||
}
|
||
|
||
// 3. 重置静态上传标记
|
||
_isRodImageUploaded = false;
|
||
_isScrapImageUploaded = false;
|
||
}
|
||
|
||
private void RodAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
TimeSpan currentTime = now.TimeOfDay;
|
||
|
||
// 1. 工作时间判断
|
||
if (currentTime < _workStartTime || currentTime > _workEndTime)
|
||
{
|
||
LogHelper.AppendLog($"当前时间{currentTime:hh\\:mm}不在工作时间内,跳过本次连杆报警判断");
|
||
_rodAlarmTimer?.Dispose();
|
||
_rodAlarmTimer = null;
|
||
return;
|
||
}
|
||
|
||
// ========== 保留:早晚节点标记判断(已上传则跳过原有报警) ==========
|
||
if (_isMorningRodUploaded || _isEveningRodUploaded)
|
||
{
|
||
LogHelper.AppendLog("连杆图片已在早晚强制时段内上传,跳过本次原有定时报警");
|
||
_rodAlarmTimer?.Dispose();
|
||
_rodAlarmTimer = null;
|
||
_isRodImageUploaded = false;
|
||
return;
|
||
}
|
||
|
||
// 2. 使用静态标记 _isRodImageUploaded 判断是否报警
|
||
if (!_isRodImageUploaded)
|
||
{
|
||
string alarmContent = $"连杆测试超时未上传图片!<br>当前时间:{now:yyyy-MM-dd HH:mm:ss}";
|
||
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true)));
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog($"连杆测试{_lastStrengthInterval}分钟内已上传图片,无需触发报警");
|
||
}
|
||
|
||
// 3. 销毁定时器,重置标记
|
||
_rodAlarmTimer?.Dispose();
|
||
_rodAlarmTimer = null;
|
||
_isRodImageUploaded = false;
|
||
}
|
||
|
||
private void ScrapAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
TimeSpan currentTime = now.TimeOfDay;
|
||
|
||
// 1. 工作时间判断
|
||
if (currentTime < _workStartTime || currentTime > _workEndTime)
|
||
{
|
||
LogHelper.AppendLog($"当前时间{currentTime:hh\\:mm}不在工作时间内,跳过本次报废报警判断");
|
||
// 销毁定时器
|
||
_scrapAlarmTimer?.Dispose();
|
||
_scrapAlarmTimer = null;
|
||
return;
|
||
}
|
||
|
||
// 2. 使用静态标记 _isScrapImageUploaded 判断是否报警(核心:上传成功则不报警)
|
||
if (!_isScrapImageUploaded)
|
||
{
|
||
string alarmContent = $"报废上传超时未上传凭证!<br>当前时间:{now:yyyy-MM-dd HH:mm:ss}";
|
||
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false)));
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog($"报废上传{_lastScrapInterval}分钟内已上传凭证,无需触发报警");
|
||
}
|
||
|
||
// 3. 销毁定时器,重置标记
|
||
_scrapAlarmTimer?.Dispose();
|
||
_scrapAlarmTimer = null;
|
||
_isScrapImageUploaded = 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}");
|
||
}
|
||
}
|
||
|
||
// ======================================== 上传状态回调方法(连杆+报废) ========================================
|
||
/// <summary>
|
||
/// 连杆上传状态回调
|
||
/// </summary>
|
||
private void OnRodUploadStatusChanged(object sender, RodUploadStatusEventArgs e)
|
||
{
|
||
if (this.InvokeRequired)
|
||
{
|
||
this.Invoke(new Action<object, RodUploadStatusEventArgs>(OnRodUploadStatusChanged), sender, e);
|
||
return;
|
||
}
|
||
|
||
_isImageUploadedInCycle = e.IsUploadSuccess;
|
||
// 上传成功时更新静态标记
|
||
if (e.IsUploadSuccess)
|
||
{
|
||
_isRodImageUploaded = true;
|
||
LogHelper.AppendLog("连杆图片上传成功,标记为「已上传」,定时结束后将不触发报警");
|
||
|
||
// ========== 保留:同步更新早晚连杆上传标记(未到节点时) ==========
|
||
TimeSpan currentTime = DateTime.Now.TimeOfDay;
|
||
|
||
// 早上节点前上传,更新早上标记
|
||
if (currentTime < _morningCheckTime && !_isMorningRodUploaded)
|
||
{
|
||
_isMorningRodUploaded = true;
|
||
LogHelper.AppendLog("连杆图片上传成功,同步标记:早上无需触发强制上传报警");
|
||
}
|
||
// 晚上节点前上传,更新晚上标记
|
||
else if (currentTime >= _morningCheckTime && currentTime < _eveningCheckTime && !_isEveningRodUploaded)
|
||
{
|
||
_isEveningRodUploaded = true;
|
||
LogHelper.AppendLog("连杆图片上传成功,同步标记:晚上无需触发强制上传报警");
|
||
}
|
||
}
|
||
|
||
LogHelper.AppendLog($"连杆上传状态:{(e.IsUploadSuccess ? "成功" : "失败")},消息:{e.Message}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 报废上传状态回调
|
||
/// </summary>
|
||
private void OnScrapUploadStatusChanged(object sender, ScrapUploadStatusEventArgs e)
|
||
{
|
||
if (this.InvokeRequired)
|
||
{
|
||
this.Invoke(new Action<object, ScrapUploadStatusEventArgs>(OnScrapUploadStatusChanged), sender, e);
|
||
return;
|
||
}
|
||
|
||
_isScrapImageUploadedInCycle = e.IsUploadSuccess;
|
||
// 上传成功时更新静态标记
|
||
if (e.IsUploadSuccess)
|
||
{
|
||
_isScrapImageUploaded = true;
|
||
LogHelper.AppendLog("报废凭证上传成功,标记为「已上传」,定时结束后将不触发报警");
|
||
}
|
||
|
||
LogHelper.AppendLog($"报废上传状态:{(e.IsUploadSuccess ? "成功" : "失败")},消息:{e.Message}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化早晚连杆检查节点时间(基于配置的工作时间)
|
||
/// </summary>
|
||
private void InitDailyRodCheckTimes()
|
||
{
|
||
// 计算早上检查节点:工作开始时间 + 30分钟
|
||
_morningCheckTime = _workStartTime.Add(TimeSpan.FromMinutes(30));
|
||
// 计算晚上检查节点:工作结束时间 - 30分钟
|
||
_eveningCheckTime = _workEndTime.Subtract(TimeSpan.FromMinutes(30));
|
||
|
||
// 校验:防止晚上检查节点早于早上检查节点(避免用户配置异常)
|
||
if (_eveningCheckTime <= _morningCheckTime)
|
||
{
|
||
LogHelper.AppendLog("警告:工作时间配置异常,结束时间前30分钟早于开始时间后30分钟,早晚连杆检查功能将失效");
|
||
}
|
||
|
||
LogHelper.AppendLog($"早晚连杆检查节点已初始化:早上{_morningCheckTime:hh\\:mm},晚上{_eveningCheckTime:hh\\:mm}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算当前时间到目标检查节点的毫秒数(用于设置定时器延迟)
|
||
/// </summary>
|
||
/// <param name="targetTime">目标检查节点(当天的时间点)</param>
|
||
/// <returns>延迟毫秒数(若目标时间已过,返回-1)</returns>
|
||
private long CalculateDelayToTargetTime(TimeSpan targetTime)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
// 拼接当天的目标日期时间
|
||
DateTime targetDateTime = new DateTime(now.Year, now.Month, now.Day,
|
||
targetTime.Hours, targetTime.Minutes, targetTime.Seconds);
|
||
|
||
// 若目标时间已过当天当前时间,返回-1(无需启动当天定时器)
|
||
if (targetDateTime <= now)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
// 计算延迟毫秒数
|
||
TimeSpan delay = targetDateTime - now;
|
||
return (long)delay.TotalMilliseconds;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动早晚连杆检查定时器(一次性,到点触发)
|
||
/// </summary>
|
||
private void StartDailyRodFixedTimers()
|
||
{
|
||
// 先停止并释放原有定时器(防止重复启动)
|
||
StopDailyRodFixedTimers();
|
||
|
||
// 重置早晚标记
|
||
ResetDailyRodMarks();
|
||
|
||
// 1. 启动早上检查定时器(若目标时间未过)
|
||
long morningDelayMs = CalculateDelayToTargetTime(_morningCheckTime);
|
||
if (morningDelayMs > 0 && _morningCheckTime > _workStartTime)
|
||
{
|
||
_morningRodAlarmTimer = new System.Timers.Timer(morningDelayMs);
|
||
_morningRodAlarmTimer.AutoReset = false; // 一次性执行,不循环
|
||
_morningRodAlarmTimer.Elapsed += MorningRodAlarmTimer_Elapsed;
|
||
_morningRodAlarmTimer.Enabled = true; // 启动定时器
|
||
LogHelper.AppendLog(
|
||
$"早上连杆检查定时器已启动:将在{_morningCheckTime:hh\\:mm}触发");
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("早上检查节点已过当天当前时间,跳过早上连杆检查定时器启动");
|
||
_isMorningRodUploaded = true; // 标记为已检查,避免重复判断
|
||
}
|
||
|
||
// 2. 启动晚上检查定时器(若目标时间未过)
|
||
long eveningDelayMs = CalculateDelayToTargetTime(_eveningCheckTime);
|
||
if (eveningDelayMs > 0 && _eveningCheckTime < _workEndTime)
|
||
{
|
||
_eveningRodAlarmTimer = new System.Timers.Timer(eveningDelayMs);
|
||
_eveningRodAlarmTimer.AutoReset = false; // 一次性执行,不循环
|
||
_eveningRodAlarmTimer.Elapsed += EveningRodAlarmTimer_Elapsed;
|
||
_eveningRodAlarmTimer.Enabled = true; // 启动定时器
|
||
LogHelper.AppendLog(
|
||
$"晚上连杆检查定时器已启动:将在{_eveningCheckTime:hh\\:mm}触发");
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("晚上检查节点已过当天当前时间,跳过晚上连杆检查定时器启动");
|
||
_isEveningRodUploaded = true; // 标记为已检查,避免重复判断
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止早晚连杆检查定时器并重置标记
|
||
/// </summary>
|
||
private void StopDailyRodFixedTimers()
|
||
{
|
||
// 停止早上定时器
|
||
if (_morningRodAlarmTimer != null)
|
||
{
|
||
_morningRodAlarmTimer.Enabled = false;
|
||
_morningRodAlarmTimer.Dispose();
|
||
_morningRodAlarmTimer = null;
|
||
}
|
||
|
||
// 停止晚上定时器
|
||
if (_eveningRodAlarmTimer != null)
|
||
{
|
||
_eveningRodAlarmTimer.Enabled = false;
|
||
_eveningRodAlarmTimer.Dispose();
|
||
_eveningRodAlarmTimer = null;
|
||
}
|
||
|
||
LogHelper.AppendLog("早晚连杆检查定时器已停止");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置早晚连杆上传标记(每日启动定时器或网关断开时调用)
|
||
/// </summary>
|
||
private void ResetDailyRodMarks()
|
||
{
|
||
_isMorningRodUploaded = false;
|
||
_isEveningRodUploaded = false;
|
||
LogHelper.AppendLog("早晚连杆上传标记已重置:早上未上传、晚上未上传");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 早上连杆检查定时器回调(工作开始+30分钟)
|
||
/// </summary>
|
||
private void MorningRodAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
// 1. 判断是否已上传,未上传则触发报警
|
||
if (!_isMorningRodUploaded)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
string morningAlarmContent = $"【早上连杆强制上传报警】<br>当前时间{now:yyyy-MM-dd HH:mm:ss},<br>已超过工作开始后30分钟({_morningCheckTime:hh\\:mm}),<br>未上传连杆测试图片!";
|
||
this.Invoke(new Action(() => SendDingDingAlarm(morningAlarmContent, true)));
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog($"早上{_morningCheckTime:hh\\:mm}:连杆图片已上传,无需触发强制上传报警");
|
||
}
|
||
|
||
// 2. 销毁早上定时器,标记为已检查(避免重复触发)
|
||
_morningRodAlarmTimer?.Dispose();
|
||
_morningRodAlarmTimer = null;
|
||
_isMorningRodUploaded = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 晚上连杆检查定时器回调(工作结束-30分钟)
|
||
/// </summary>
|
||
private void EveningRodAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
// 1. 判断是否已上传,未上传则触发报警
|
||
if (!_isEveningRodUploaded)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
string eveningAlarmContent = $"【晚上连杆强制上传报警】<br>当前时间{now:yyyy-MM-dd HH:mm:ss},<br>已超过工作结束前30分钟({_eveningCheckTime:hh\\:mm}),<br>未上传连杆测试图片!";
|
||
this.Invoke(new Action(() => SendDingDingAlarm(eveningAlarmContent, true)));
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog($"晚上{_eveningCheckTime:hh\\:mm}:连杆图片已上传,无需触发强制上传报警");
|
||
}
|
||
|
||
// 2. 销毁晚上定时器,标记为已检查(避免重复触发)
|
||
_eveningRodAlarmTimer?.Dispose();
|
||
_eveningRodAlarmTimer = null;
|
||
_isEveningRodUploaded = true;
|
||
}
|
||
|
||
// ======================================== 辅助方法 ========================================
|
||
private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
// 适配移植的配方变量,刷新选中配方
|
||
string selected1 = this.comboBox1.SelectedValue?.ToString();
|
||
string selected2 = this.comboBox2.SelectedValue?.ToString();
|
||
RefreshSelectedFormula(selected1, selected2);
|
||
await MqttYiDaUpload();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 适配移植:刷新选中配方(辅助方法)
|
||
/// </summary>
|
||
private void RefreshSelectedFormula(string selected1, string selected2)
|
||
{
|
||
if (string.IsNullOrEmpty(selected1) || selected1 == "-1")
|
||
{
|
||
_formulasList1 = null;
|
||
return;
|
||
}
|
||
|
||
string[] arr1 = selected1.Split('|');
|
||
if (arr1.Length == 2)
|
||
{
|
||
string partNumber1 = arr1[0];
|
||
string partName1 = arr1[1];
|
||
_formulasList1 = _formulas.AsEnumerable()
|
||
.Where(r => r.Field<string>("part_number") == partNumber1
|
||
&& r.Field<string>("part_name") == partName1)
|
||
.ToList();
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(selected2) || selected2 == "-1")
|
||
{
|
||
_formulasList2 = null;
|
||
return;
|
||
}
|
||
|
||
string[] arr2 = selected2.Split('|');
|
||
if (arr2.Length == 2)
|
||
{
|
||
string partNumber2 = arr2[0];
|
||
string partName2 = arr2[1];
|
||
_formulasList2 = _formulas.AsEnumerable()
|
||
.Where(r => r.Field<string>("part_number") == partNumber2
|
||
&& r.Field<string>("part_name") == partName2)
|
||
.ToList();
|
||
}
|
||
}
|
||
|
||
// ======================================== 核心上传方法 ========================================
|
||
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}]数据");
|
||
|
||
// ======================================== 使用移植的配方列表变量 ========================================
|
||
List<DataRow> formulaList = null;
|
||
string site = null;
|
||
if (string.Equals(deviceCode, "device1", StringComparison.Ordinal))
|
||
{
|
||
formulaList = _formulasList1;
|
||
site = "1号设备";
|
||
}
|
||
else if (string.Equals(deviceCode, "device2", StringComparison.Ordinal))
|
||
{
|
||
formulaList = _formulasList2;
|
||
site = "2号设备";
|
||
}
|
||
|
||
if (formulaList == null || formulaList.Count == 0)
|
||
{
|
||
LogHelper.AppendLog($"设备[{deviceCode}]未选择配方,跳过");
|
||
continue;
|
||
}
|
||
|
||
string strMessage = dr["receive_data"].ToString();
|
||
if (string.IsNullOrEmpty(strMessage))
|
||
{
|
||
LogHelper.AppendLog($"设备[{deviceCode}]无有效receive_data,跳过");
|
||
continue;
|
||
}
|
||
|
||
SQLDataModel data = JsonConvert.DeserializeObject<SQLDataModel>(strMessage);
|
||
if (data == null || data.@params == null)
|
||
{
|
||
LogHelper.AppendLog($"设备[{deviceCode}]反序列化后无params数据,跳过");
|
||
continue;
|
||
}
|
||
|
||
Type paramsType = data.@params.GetType();
|
||
PropertyInfo[] properties = paramsType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||
List<MqttModel> mqttLists = new List<MqttModel>();
|
||
List<YiDaModel> yidaLists = new List<YiDaModel>();
|
||
|
||
foreach (PropertyInfo prop in properties)
|
||
{
|
||
string paramCode = prop.Name;
|
||
object value = prop.GetValue(data.@params);
|
||
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// ======================================== 使用移植的MQTT字典进行参数映射 ========================================
|
||
DataRow[] drs = _dtMqttDic.Select($"param_code = '" + paramCode + "'");
|
||
string paramName = paramCode;
|
||
if (drs != null && drs.Length > 0)
|
||
{
|
||
paramName = drs[0]["param_name"].ToString();
|
||
}
|
||
|
||
DataRow drMatch =
|
||
formulaList.FirstOrDefault(r => r.Field<string>("parameter_name") == paramName);
|
||
if (drMatch == null)
|
||
{
|
||
LogHelper.AppendLog($"设备[{deviceCode}]配方中未找到参数[{paramName}],跳过");
|
||
continue;
|
||
}
|
||
|
||
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<MqttModel> mqttLists1 = mqttLists.Take(len).ToList();
|
||
List<YiDaModel> yidaLists1 = yidaLists.Take(len).ToList();
|
||
|
||
string token = _uploadService.GetDingDingToken();
|
||
if (string.IsNullOrEmpty(token))
|
||
{
|
||
LogHelper.AppendLog("获取 token 失败,请检查 AppKey/AppSecret");
|
||
this.Invoke(new Action(() =>
|
||
{
|
||
MessageBox.Show("获取 token 失败,请检查 AppKey/AppSecret", "错误", MessageBoxButtons.OK,
|
||
MessageBoxIcon.Error);
|
||
}));
|
||
return;
|
||
}
|
||
|
||
this.Invoke(new Action(() =>
|
||
{
|
||
_uploadService.UploadDatabaseDataToYiDaWithLogging(token, yidaLists1, 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 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;
|
||
}
|
||
|
||
private YiDaModel BuildYiDaModel(DataRow drMatch, string paramName, object value, string site)
|
||
{
|
||
YiDaModel yidaNew = new YiDaModel();
|
||
yidaNew.textField_mha98neu = drMatch["supplier_code"].ToString();
|
||
yidaNew.textField_mha98nev = drMatch["supplier_name"].ToString();
|
||
yidaNew.textField_mha98new = drMatch["vehicle_model"].ToString();
|
||
yidaNew.textField_mha98nex = drMatch["part_number"].ToString();
|
||
yidaNew.textField_mha98ney = drMatch["part_name"].ToString();
|
||
yidaNew.textField_mha98nf1 = paramName;
|
||
yidaNew.textField_mhx44i2i = value.ToString();
|
||
yidaNew.textField_mhx44i2j = drMatch["tolerance_lower"].ToString();
|
||
yidaNew.textField_mhx44i2k = drMatch["tolerance_upper"].ToString();
|
||
yidaNew.textField_mha98nf7 = drMatch["leader_part"].ToString();
|
||
yidaNew.textField_mha98nf0 = site;
|
||
yidaNew.textField_mha98nfh = drMatch["leader_out_protection"].ToString();
|
||
yidaNew.textField_mhlvt8ht =
|
||
Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))
|
||
.TotalMilliseconds);
|
||
|
||
yidaNew.textField_mha98nf5 = JudgeQualification(paramName, value, drMatch);
|
||
|
||
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 == "报废图片" || paramName == "连杆测试")
|
||
{
|
||
return "合格";
|
||
}
|
||
else
|
||
{
|
||
if (float.TryParse(value?.ToString(), out float paramValue) &&
|
||
float.TryParse(drMatch["tolerance_lower"].ToString(), out float toleranceLower) &&
|
||
float.TryParse(drMatch["tolerance_upper"].ToString(), out float toleranceUpper))
|
||
{
|
||
return (paramValue >= toleranceLower && paramValue <= toleranceUpper) ? "合格" : "不合格";
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog(
|
||
$"数值转换失败:参数名={paramName},参数值={value},下公差={drMatch["tolerance_lower"]},上公差={drMatch["tolerance_upper"]}");
|
||
return "不合格";
|
||
}
|
||
}
|
||
}
|
||
|
||
// ======================================== 日志相关方法 ========================================
|
||
private void OnMqttMessage(string msg)
|
||
{
|
||
if (this.InvokeRequired)
|
||
{
|
||
this.Invoke(new Action<string>(OnMqttMessage), msg);
|
||
return;
|
||
}
|
||
|
||
LogHelper.AppendLog($"收到消息: {msg}");
|
||
}
|
||
|
||
private void LogHelper_OnLogGenerated(string logContent)
|
||
{
|
||
if (textBoxLog.InvokeRequired)
|
||
{
|
||
textBoxLog.Invoke(new Action(() => { AppendLogToTextBox(logContent); }));
|
||
}
|
||
else
|
||
{
|
||
AppendLogToTextBox(logContent);
|
||
}
|
||
}
|
||
|
||
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, PaintEventArgs e)
|
||
{
|
||
}
|
||
|
||
private void label6_Click(object sender, EventArgs e)
|
||
{
|
||
}
|
||
|
||
private void toolStripStatusLabel3_Click(object sender, EventArgs e)
|
||
{
|
||
|
||
}
|
||
}
|
||
} |