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 readonly string _logDirectory = "log";
private string _currentLogFile;
private DateTime _lastLogDate = DateTime.MinValue;
///
/// 配方配置
///
private DataTable _formulas; // 原始配方表(含多行,未去重)
private DataTable _dtMqttDic; // MQTT 参数字典表
private List _formulasListOne; // 设备1选择的配方(多行)
private List _formulasListTwo; // 设备2选择的配方(多行)
///
/// 定时任务
///
// 通用上传定时器
private System.Timers.Timer _timer;
// 设备1
private System.Timers.Timer _scrapAlarmTimerOne; // 报废上传定时任务
private int _lastScrapIntervalOne = 60; // 报废间隔
private static bool _isScrapImageUploadedOne = false; // 报废图片是否上传
private static bool _isScrapImageUploadedInCycleOne = false;
private System.Timers.Timer _rodAlarmTimerOne; // 连杆测试定时任务
private int _lastRodIntervalOne = 60; // 连杆间隔
private static bool _isRodImageUploadedOne = false; // 连杆图片是否上传
private static bool _isRodUploadedInCycleOne = false;
private static bool _isScrapImageWorkNoticeOne = false; // 报废图片工作通知标记
private static bool _isRodImageWorkNoticeOne = false; // 连杆图片工作通知标记
// 设备2
private System.Timers.Timer _scrapAlarmTimerTwo; // 报废上传定时任务
private int _lastScrapIntervalTwo = 60; // 报废间隔
private static bool _isScrapImageUploadedTwo = false; // 报废图片是否上传
private static bool _isScrapImageUploadedInCycleTwo = false;
private System.Timers.Timer _rodAlarmTimerTwo; // 连杆测试定时任务
private int _lastRodIntervalTwo = 60; // 连杆间隔
private static bool _isRodImageUploadedTwo = false; // 连杆图片是否上传
private static bool _isRodUploadedInCycleTwo = false;
private static bool _isScrapImageWorkNoticeTwo = false; // 报废图片工作通知标记
private static bool _isRodImageWorkNoticeTwo = false; // 连杆图片工作通知标记
// 连杆工作时间配置
private System.Timers.Timer _morningRodAlarmTimerOne; // 早上检查点定时器(工作开始+30分钟)
private System.Timers.Timer _eveningRodAlarmTimerOne; // 晚上检查点定时器(工作结束-30分钟)
private System.Timers.Timer _morningRodAlarmTimerTwo; // 早上检查点定时器(工作开始+30分钟)
private System.Timers.Timer _eveningRodAlarmTimerTwo; // 晚上检查点定时器(工作结束-30分钟)
private bool _isMorningRodUploadedOne = false; // 早上标记(工作开始后30分钟前是否上传)
private bool _isEveningRodUploadedOne = false; // 晚上标记(工作结束前30分钟前是否上传)
private bool _isMorningRodUploadedTwo = false; // 早上标记(工作开始后30分钟前是否上传)
private bool _isEveningRodUploadedTwo = false; // 晚上标记(工作结束前30分钟前是否上传)
public static TimeSpan _workStartTimeOne = new TimeSpan(9, 0, 0);
public static TimeSpan _workEndTimeOne = new TimeSpan(17, 30, 0);
private TimeSpan _morningCheckTimeOne; // 早上检查节点(_workStartTime + 30分钟)
private TimeSpan _eveningCheckTimeOne; // 晚上检查节点(_workEndTime + 30分钟)
public static TimeSpan _workStartTimeTwo = new TimeSpan(9, 0, 0);
public static TimeSpan _workEndTimeTwo = new TimeSpan(17, 30, 0);
private TimeSpan _morningCheckTimeTwo; // 早上检查节点(_workStartTime + 30分钟)
private TimeSpan _eveningCheckTimeTwo; // 晚上检查节点(_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.ScrapUploadStatusChangedOne += OnScrapUploadStatusChangedOne;
_uploadService.ScrapUploadStatusChangedTwo += OnScrapUploadStatusChangedTwo;
_uploadService.RodUploadStatusChangedOne += OnRodUploadStatusChangedOne;
_uploadService.RodUploadStatusChangedTwo += OnRodUploadStatusChangedTwo;
// UI初始化配置
buttonDisconnect.Enabled = false;
// 初始化日志目录
InitializeLogDirectory();
// 初始化配方相关变量
_formulas = new DataTable();
_dtMqttDic = new DataTable();
_formulasListOne = new List();
_formulasListTwo = new List();
InitDailyRodDeviceOneCheckTimes();
InitDailyRodDeviceTwoCheckTimes();
}
///
/// 初始化日志目录
///
private void InitializeLogDirectory()
{
if (!Directory.Exists(_logDirectory))
{
Directory.CreateDirectory(_logDirectory);
}
UpdateLogFile();
}
///
/// 更新当前日志文件(每日一个新文件)
///
private void UpdateLogFile()
{
DateTime today = DateTime.Now.Date;
if (today != _lastLogDate)
{
_lastLogDate = today;
_currentLogFile = Path.Combine(_logDirectory, $"log_{today:yyyyMMdd}.txt");
}
}
///
/// 加载MainForm信息
///
private async void Form1_Load(object sender, EventArgs e)
{
try
{
MakePanelRound(panelLed);
await InitFormulas(); // 移植:加载配方
InitMqttDic(); // 移植:加载MQTT字典
timer1.Enabled = true;
// 初始化时,若下拉框未选择,禁用所有按钮
UpdateDevice1ButtonStates();
UpdateDevice2ButtonStates();
// 绑定下拉框的选择变化事件
comboBox1.SelectedIndexChanged += comboBox1_SelectedIndexChanged;
comboBox2.SelectedIndexChanged += comboBox2_SelectedIndexChanged;
}
catch (Exception ex)
{
LogHelper.AppendLog($"初始化失败:{ex.Message}");
}
}
///
/// 将指定 Panel 变成圆形
///
private static void MakePanelRound(Panel panel)
{
int size = Math.Min(panel.Width, panel.Height);
panel.Size = new Size(size, size);
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, panel.Width, panel.Height);
panel.Region = new Region(path);
}
// ======================================== 配方核心方法 ========================================
///
/// 查询配方并去重
///
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();
}
};
}
}
///
/// 初始化MQTT字典
///
private async void InitMqttDic()
{
DataTable dt = await _buttonService.InitMqttDic();
if (dt != null)
{
_dtMqttDic = dt;
}
}
// ======================================== 按钮相关方法 ========================================
///
/// 网关连接按钮
///
private async void buttonConnect_Click(object sender, EventArgs e)
{
try
{
LogHelper.AppendLog("正在连接MQTT服务器...");
await _mqttService.MqttClientStartAsync();
LogHelper.AppendLog("连接成功!");
panelLed.BackColor = Color.Green;
toolStripStatusLabel1.Text = "已连接";
buttonConnect.Enabled = false;
buttonDisconnect.Enabled = true;
}
catch (Exception ex)
{
panelLed.BackColor = Color.Red;
LogHelper.AppendLog($"连接失败:{ex.Message}");
toolStripStatusLabel1.Text = "连接失败";
}
}
///
/// 网关断开按钮
///
private async void buttonDisconnect_Click(object sender, EventArgs e)
{
try
{
await _mqttService.MqttClientStopAsync();
LogHelper.AppendLog("已断开连接");
panelLed.BackColor = Color.Red;
toolStripStatusLabel1.Text = "未连接";
buttonConnect.Enabled = true;
buttonDisconnect.Enabled = false;
}
catch (Exception ex)
{
LogHelper.AppendLog($"无法断开连接:{ex.Message}");
}
}
///
/// 配方导入按钮
///
private async void button1_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "Excel文件|*.xlsx;*.xls";
if (openFile.ShowDialog() != DialogResult.OK)
{
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;
}
}
///
/// 上传宜搭按钮
///
private async void button2_Click(object sender, EventArgs e)
{
try
{
if (toolStripStatusLabel1.Text != "已连接") //如果 MQTT 没连接,就不允许上传。
{
MessageBox.Show("请先连接MQTT网关!");
return;
}
if (this.comboBox1.Text.Length == 0 && this.comboBox2.Text.Length == 0)
{
//如果两个下拉框都还没数据,说明没导入过配方,直接中止。
MessageBox.Show("请先导入配方信息,再刷新!");
return;
}
string selection = null;
// 判断是否选择有效下拉框
bool hasSelection1 = comboBox1.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox1.SelectedValue?.ToString());
bool hasSelection2 = comboBox2.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox2.SelectedValue?.ToString());
// 1. 重置静态上传标记(每次连接都重新开始判断)
_isScrapImageUploadedOne = false;
_isRodImageUploadedOne = false;
_isScrapImageUploadedTwo = false;
_isRodImageUploadedTwo = false;
_isRodImageWorkNoticeOne = false;
_isRodImageWorkNoticeTwo = false;
_isScrapImageWorkNoticeOne = false;
_isScrapImageWorkNoticeTwo = false;
LogHelper.AppendLog("重置上传标记:连杆未上传、报废未上传");
string selectedContent1 = string.Empty;
string selectedContent2 = string.Empty;
// 获取选中项的DataRowView(增加判空)
DataRowView drv1 = comboBox1.SelectedItem as DataRowView;
// 先判断drv1是否为null,再访问字段
selectedContent1 = drv1 != null ? (drv1["part_name"]?.ToString()?.Trim() ?? string.Empty) : string.Empty;
// 获取选中项的DataRowView(增加判空)
DataRowView drv2 = comboBox2.SelectedItem as DataRowView;
// 先判断drv2是否为null,再访问字段
selectedContent2 = drv2 != null ? (drv2["part_name"]?.ToString()?.Trim() ?? string.Empty) : string.Empty;
if (hasSelection1 && hasSelection2)
{
selection = "3";
StartScrapOneAlarmTimers();
StartScrapTwoAlarmTimers();
if (selectedContent1.Contains("连杆2") && selectedContent2.Contains("连杆2"))
{
StartRodOneAlarmTimers();
StartDailyRodDeviceOneFixedTimers();
StartRodTwoAlarmTimers();
StartDailyRodDeviceTwoFixedTimers();
}
}
else if (hasSelection1)
{
selection = "1";
StartScrapOneAlarmTimers();
if (selectedContent1.Contains("连杆2"))
{
StartRodOneAlarmTimers();
StartDailyRodDeviceOneFixedTimers();
}
}
else if (hasSelection2)
{
selection = "2";
StartScrapTwoAlarmTimers();
if (selectedContent2.Contains("连杆2"))
{
StartRodTwoAlarmTimers();
StartDailyRodDeviceTwoFixedTimers();
}
}
else
{
MessageBox.Show("未选择数据!");
}
this.button2.Enabled = false;
// 设备1选择配方
string selected1 = this.comboBox1.SelectedValue?.ToString();
if (string.IsNullOrEmpty(selected1) || selected1 == "-1")
{
_formulasListOne = null;
}
else
{
string[] arr1 = selected1.Split('|');
if (arr1.Length == 2)
{
string partNumber1 = arr1[0];
string partName1 = arr1[1];
_formulasListOne = _formulas.AsEnumerable()
.Where(r => r.Field("part_number") == partNumber1
&& r.Field("part_name") == partName1)
.ToList();
}
else
{
_formulasListOne = null;
}
}
// 设备2选择配方
string selected2 = this.comboBox2.SelectedValue?.ToString();
if (string.IsNullOrEmpty(selected2) || selected2 == "-1")
{
_formulasListTwo = null;
}
else
{
string[] arr2 = selected2.Split('|');
if (arr2.Length == 2)
{
string partNumber2 = arr2[0];
string partName2 = arr2[1];
_formulasListTwo = _formulas.AsEnumerable()
.Where(r => r.Field("part_number") == partNumber2
&& r.Field("part_name") == partName2)
.ToList();
}
else
{
_formulasListTwo = null;
}
}
double seconds;
if (!double.TryParse(txtLUploadPL.Text, out seconds) || seconds <= 0)
{
seconds = 10; //控制上传间隔,默认10秒。
}
// 立即上传一次
await MqttYiDaUpload(selection, null);
_timer = new System.Timers.Timer(seconds * 1000);
_timer.Elapsed += async (s, evt) => { await MqttYiDaUpload(selection, null); };
_timer.AutoReset = true; // 是否重复
_timer.Enabled = true; // 开始计时
LogHelper.AppendLog($"自动上传已启动,间隔:{seconds}秒");
}
catch (Exception ex)
{
this.button2.Enabled = true;
LogHelper.AppendLog($"上传宜搭失败:{ex.Message}");
}
}
///
/// 中止上传按钮
///
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);
}
StopDeviceOneAlarmTimers();
StopDeviceTwoAlarmTimers();
StopDailyRodDeviceOneFixedTimers();
StopDailyRodDeviceTwoFixedTimers();
}
///
/// 报废上传凭证按钮(设备1)
///
private async void button4_Click(object sender, EventArgs e)
{
// 传入间隔值
using (var scrapForm = new ScrapUploadForm(_lastScrapIntervalOne, "1"))
{
DialogResult result = scrapForm.ShowDialog(this);
if (result == DialogResult.OK)
{
// 1. 提取用户本次选择结果
int interval = scrapForm.ScrapTimedIntervalOne;
_lastScrapIntervalOne = interval;
// 2. 上传逻辑
bool isImageUploadSuccess = false;
using (OpenFileDialog openFile = new OpenFileDialog())
{
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadScrapDeviceOneAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("1", "device1");
MqttYiDaUpload("1", "device1");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isScrapImageUploadedOne = true;
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
}
///
/// 报废上传凭证按钮(设备2)
///
///
///
private async void button7_Click(object sender, EventArgs e)
{
// 传入间隔值
using (var scrapForm = new ScrapUploadForm(_lastScrapIntervalTwo, "2"))
{
DialogResult result = scrapForm.ShowDialog(this);
if (result == DialogResult.OK)
{
// 1. 提取用户本次选择结果
int interval = scrapForm.ScrapTimedIntervalTwo;
_lastScrapIntervalTwo = interval;
// 2. 上传逻辑
bool isImageUploadSuccess = false;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadScrapDeviceTwoAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("1", "device2");
MqttYiDaUpload("2", "device2");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isScrapImageUploadedTwo = true;
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
///
/// 连杆测试上传按钮(设备1)
///
private async void button5_Click(object sender, EventArgs e)
{
if (!button5.Enabled)
{
StopDeviceOneAlarmTimers();
return;
}
using (var selectForm = new RodTestUploadForm(_lastRodIntervalOne, "1"))
{
DialogResult result = selectForm.ShowDialog(this);
if (result == DialogResult.OK)
{
int interval = selectForm.RodTimedIntervalOne;
_lastRodIntervalOne = interval;
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
bool isImageUploadSuccess = false;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadRodTestDeviceOneAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device1");
MqttYiDaUpload("2", "device1");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedOne = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeOne && timeOfDay < _morningCheckTimeOne)
{
_isMorningRodUploadedOne = true;
}
if (timeOfDay > _workEndTimeOne || timeOfDay < _eveningCheckTimeOne)
{
_isEveningRodUploadedOne = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
///
/// 配置按钮(设备1)
///
///
///
private async void button6_Click(object sender, EventArgs e)
{
try
{
if (!button6.Enabled)
{
StopDailyRodDeviceOneFixedTimers();
return;
}
using (var configForm = new WorkTimeConfigForm(_workStartTimeOne, _workEndTimeOne, "1"))
{
DialogResult result = configForm.ShowDialog(this);
if (result == DialogResult.OK)
{
_workStartTimeOne = configForm.WorkStartTimeOne;
_workEndTimeOne = configForm.WorkEndTimeOne;
// ========== 重新初始化早晚连杆检查节点 ==========
InitDailyRodDeviceOneCheckTimes();
// ========== 如果网关已连接,重启早晚固定检查定时器 ==========
if (toolStripStatusLabel1.Text == "已连接")
{
StartDailyRodDeviceOneFixedTimers();
}
LogHelper.AppendLog($"工作时间配置更新:{_workStartTimeOne:hh\\:mm} - {_workEndTimeOne:hh\\:mm}");
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
bool isImageUploadSuccess = true;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess =
await _uploadService.UploadRodTestDeviceOneAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device1");
MqttYiDaUpload("2", "device1");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedOne = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeOne && timeOfDay < _morningCheckTimeOne)
{
_isMorningRodUploadedOne = true;
}
if (timeOfDay > _workEndTimeOne || timeOfDay < _eveningCheckTimeOne)
{
_isEveningRodUploadedOne = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
catch (Exception ex)
{
LogHelper.AppendLog($"工作时间配置失败:{ex.Message}");
MessageBox.Show($"配置失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// 时间配置(设备2)
///
///
///
private async void button9_Click(object sender, EventArgs e)
{
try
{
if (!button9.Enabled)
{
StopDailyRodDeviceTwoFixedTimers();
return;
}
using (var configForm = new WorkTimeConfigForm(_workStartTimeTwo, _workEndTimeTwo, "2"))
{
DialogResult result = configForm.ShowDialog(this);
if (result == DialogResult.OK)
{
_workStartTimeTwo = configForm.WorkStartTimeTwo;
_workEndTimeTwo = configForm.WorkEndTimeTwo;
// ========== 重新初始化早晚连杆检查节点 ==========
InitDailyRodDeviceTwoCheckTimes();
// ========== 如果网关已连接,重启早晚固定检查定时器 ==========
if (toolStripStatusLabel1.Text == "已连接")
{
StartDailyRodDeviceTwoFixedTimers();
}
LogHelper.AppendLog($"工作时间配置更新:{_workStartTimeTwo:hh\\:mm} - {_workEndTimeTwo:hh\\:mm}");
// 3. 上传逻辑:仅当勾选「模具上传」时,才弹出文件选择框(分离上传和报警逻辑)
bool isImageUploadSuccess = true;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess =
await _uploadService.UploadRodTestDeviceTwoAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device2");
MqttYiDaUpload("2", "device2");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedTwo = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeTwo && timeOfDay < _morningCheckTimeTwo)
{
_isMorningRodUploadedTwo = true;
}
if (timeOfDay > _workEndTimeTwo || timeOfDay < _eveningCheckTimeTwo)
{
_isEveningRodUploadedTwo = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
catch (Exception ex)
{
LogHelper.AppendLog($"工作时间配置失败:{ex.Message}");
MessageBox.Show($"配置失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// 连杆测试上传按钮(设备2)
///
///
///
private async void button8_Click(object sender, EventArgs e)
{
if (!button8.Enabled)
{
StopDeviceTwoAlarmTimers();
return;
}
using (var selectForm = new RodTestUploadForm(_lastRodIntervalTwo, "2"))
{
DialogResult result = selectForm.ShowDialog(this);
if (result == DialogResult.OK)
{
int interval = selectForm.RodTimedIntervalTwo;
_lastRodIntervalTwo = interval;
// 3. 上传逻辑
bool isImageUploadSuccess = false;
OpenFileDialog openFile = new OpenFileDialog();
if (openFile.ShowDialog() == DialogResult.OK)
{
string filePath = openFile.FileName;
if (!string.IsNullOrEmpty(filePath))
{
isImageUploadSuccess = await _uploadService.UploadRodTestDeviceTwoAsync(filePath);
LogHelper.AppendLog(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
if (isImageUploadSuccess)
{
_buttonService.MergeAndSaveData("2", "device2");
MqttYiDaUpload("2", "device2");
// 上传成功:直接标记为已上传(避免后续定时报警)
_isRodImageUploadedTwo = true;
DateTime now = DateTime.Now;
TimeSpan timeOfDay = now.TimeOfDay;
if (timeOfDay > _workStartTimeTwo && timeOfDay < _morningCheckTimeTwo)
{
_isMorningRodUploadedTwo = true;
}
if (timeOfDay > _workEndTimeTwo || timeOfDay < _eveningCheckTimeTwo)
{
_isEveningRodUploadedTwo = true;
}
}
}
else
{
MessageBox.Show("不允许上传空文件!");
}
}
}
}
}
///
/// 关闭MainForm按钮
///
private async void MainFormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (_timer != null)
{
_timer.Enabled = false;
_timer.Dispose();
}
StopDeviceOneAlarmTimers();
StopDeviceTwoAlarmTimers();
StopDailyRodDeviceOneFixedTimers();
StopDailyRodDeviceTwoFixedTimers();
await _mqttService.MqttClientStopAsync();
}
catch (Exception ex)
{
LogHelper.AppendLog($"关闭窗口失败:{ex.Message}");
}
}
///
/// 刷新配方按钮
///
private async void btnRefresh_Click(object sender, EventArgs e)
{
try
{
await InitFormulas();
InitMqttDic();
MessageBox.Show("配方已刷新!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
UpdateDevice1ButtonStates();
UpdateDevice2ButtonStates();
}
catch (Exception ex)
{
LogHelper.AppendLog($"刷新配方失败:{ex.Message}");
MessageBox.Show($"刷新配方失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// 上传间隔输入框校验
///
private void txtLUploadPL_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Back)
{
e.Handled = false;
return;
}
if (char.IsDigit(e.KeyChar) || (e.KeyChar == '.' && !txtLUploadPL.Text.Contains(".")))
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}
///
/// 更新时间显示
///
private void timer1_Tick(object sender, EventArgs e)
{
label4.Text = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
}
// ======================================== 事件绑定 ========================================
// 设备1下拉框选择变化事件
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateDevice1ButtonStates();
}
// 设备2下拉框选择变化事件
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateDevice2ButtonStates();
}
// 更新设备1对应按钮的状态
private void UpdateDevice1ButtonStates()
{
// 判断下拉框是否有有效选择
bool hasSelection = comboBox1.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox1.SelectedValue?.ToString());
// 判断是否选中连杆2
string selectedContent = string.Empty;
if (hasSelection)
{
// 获取选中项的DataRowView
DataRowView drv = comboBox1.SelectedItem as DataRowView;
// 取零件名(part_name)字段,这是你的配方表中实际存储"连杆2"的字段
selectedContent = drv["part_name"]?.ToString()?.Trim() ?? string.Empty;
}
// 判断是否包含"连杆2"
button6.Enabled = hasSelection && selectedContent.Contains("连杆2");
button5.Enabled = hasSelection && selectedContent.Contains("连杆2");
button4.Enabled = hasSelection; // 报废上传凭证1
}
// 更新设备2对应按钮的状态
private void UpdateDevice2ButtonStates()
{
bool hasSelection = comboBox2.SelectedIndex > 0 &&
!string.IsNullOrEmpty(comboBox2.SelectedValue?.ToString());
// 判断是否选中连杆2
string selectedContent = string.Empty;
if (hasSelection)
{
// 获取选中项的DataRowView
DataRowView drv = comboBox2.SelectedItem as DataRowView;
// 取零件名(part_name)字段,这是你的配方表中实际存储"连杆2"的字段
selectedContent = drv["part_name"]?.ToString()?.Trim() ?? string.Empty;
}
// 判断是否包含"连杆2"
button9.Enabled = hasSelection && selectedContent.Contains("连杆2");
button8.Enabled = hasSelection && selectedContent.Contains("连杆2");
button7.Enabled = hasSelection; // 报废上传凭证2
}
// ======================================== 定时报警核心方法 ========================================
///
/// 启动报废定时任务(前30分钟上传)
///
private void StartScrapOneAlarmTimers()
{
// 设备1
if (_scrapAlarmTimerOne != null)
{
_scrapAlarmTimerOne.Enabled = false;
_scrapAlarmTimerOne.Dispose();
}
int scrapIntervalMsOne = _lastScrapIntervalOne * 60 * 1000; // 分钟 → 毫秒
_scrapAlarmTimerOne = new System.Timers.Timer(scrapIntervalMsOne);
_scrapAlarmTimerOne.AutoReset = false; // 关键:一次性执行,不循环
_scrapAlarmTimerOne.Elapsed += ScrapAlarmTimerOne_Elapsed;
_scrapAlarmTimerOne.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备1报废上传定时已启动:若{_lastScrapIntervalOne}分钟内未上传凭证,将触发钉钉报警");
}
private void StartScrapTwoAlarmTimers()
{
// 设备2
if (_scrapAlarmTimerTwo != null)
{
_scrapAlarmTimerTwo.Enabled = false;
_scrapAlarmTimerTwo.Dispose();
}
int scrapIntervalMsTwo = _lastScrapIntervalTwo * 60 * 1000; // 分钟 → 毫秒
_scrapAlarmTimerTwo = new System.Timers.Timer(scrapIntervalMsTwo);
_scrapAlarmTimerTwo.AutoReset = false; // 关键:一次性执行,不循环
_scrapAlarmTimerTwo.Elapsed += ScrapAlarmTimerTwo_Elapsed;
_scrapAlarmTimerTwo.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备1报废上传定时已启动:若{_lastScrapIntervalTwo}分钟内未上传凭证,将触发钉钉报警");
}
///
/// 启动连杆定时任务(前30分钟上传)
///
private void StartRodOneAlarmTimers()
{
// 设备1
if (_rodAlarmTimerOne != null)
{
_rodAlarmTimerOne.Enabled = false;
_rodAlarmTimerOne.Dispose();
}
int rodIntervalMsOne = _lastRodIntervalOne * 60 * 1000; // 分钟 → 毫秒
_rodAlarmTimerOne = new System.Timers.Timer(rodIntervalMsOne);
_rodAlarmTimerOne.AutoReset = false; // 关键:一次性执行,不循环
_rodAlarmTimerOne.Elapsed += RodAlarmTimerOne_Elapsed;
_rodAlarmTimerOne.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备1连杆定时已启动:若{_lastRodIntervalOne}分钟内未上传凭证,将触发钉钉报警");
}
private void StartRodTwoAlarmTimers()
{
// 设备2
if (_rodAlarmTimerTwo != null)
{
_rodAlarmTimerTwo.Enabled = false;
_rodAlarmTimerTwo.Dispose();
}
int scrapIntervalMsTwo = _lastRodIntervalTwo * 60 * 1000; // 分钟 → 毫秒
_rodAlarmTimerTwo = new System.Timers.Timer(scrapIntervalMsTwo);
_rodAlarmTimerTwo.AutoReset = false; // 关键:一次性执行,不循环
_rodAlarmTimerTwo.Elapsed += RodAlarmTimerTwo_Elapsed;
_rodAlarmTimerTwo.Enabled = true; // 启动定时
LogHelper.AppendLog($"设备2连杆上传定时已启动:若{_lastRodIntervalTwo}分钟内未上传凭证,将触发钉钉报警");
}
///
/// 启动连杆定时任务(设备1早晚检查)
///
private void StartDailyRodDeviceOneFixedTimers()
{
// 先停止并释放原有定时器(防止重复启动)
StopDailyRodDeviceOneFixedTimers();
// 重置早晚标记
ResetDailyRodMarks();
DateTime now = DateTime.Now;
DayOfWeek weekEnum = now.DayOfWeek;
if (weekEnum.Equals(DayOfWeek.Monday))
{
_morningCheckTimeOne = new TimeSpan(10, 30, 0);
_workStartTimeOne = new TimeSpan(10, 0, 0);
}
// 1. 启动早上检查定时器(若目标时间未过)
long morningDelayMs = CalculateDelayToTargetTime(_morningCheckTimeOne, "morning");
if (morningDelayMs > 0 && _morningCheckTimeOne > _workStartTimeOne)
{
_morningRodAlarmTimerOne = new System.Timers.Timer(morningDelayMs);
_morningRodAlarmTimerOne.AutoReset = false; // 一次性执行,不循环
_morningRodAlarmTimerOne.Elapsed += MorningRodDeviceOneAlarmTimer_Elapsed;
_morningRodAlarmTimerOne.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"早上连杆检查定时器已启动:将在{_morningCheckTimeOne:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("早上检查节点已过当天当前时间,跳过早上连杆检查定时器启动");
_isMorningRodUploadedOne = true; // 标记为已检查,避免重复判断
}
// 2. 启动晚上检查定时器(若目标时间未过)
long eveningDelayMs = CalculateDelayToTargetTime(_eveningCheckTimeOne, "evening");
if (eveningDelayMs > 0 && _eveningCheckTimeOne > _workEndTimeOne)
{
_eveningRodAlarmTimerOne = new System.Timers.Timer(eveningDelayMs);
_eveningRodAlarmTimerOne.AutoReset = false; // 一次性执行,不循环
_eveningRodAlarmTimerOne.Elapsed += EveningRodDeviceOneAlarmTimer_Elapsed;
_eveningRodAlarmTimerOne.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"晚上连杆检查定时器已启动:将在{_eveningCheckTimeOne:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("晚上检查节点已过当天当前时间,跳过晚上连杆检查定时器启动");
_isEveningRodUploadedOne = true; // 标记为已检查,避免重复判断
}
}
///
/// 启动连杆定时任务(设备2早晚检查)
///
private void StartDailyRodDeviceTwoFixedTimers()
{
// 先停止并释放原有定时器(防止重复启动)
StopDailyRodDeviceTwoFixedTimers();
// 重置早晚标记
ResetDailyRodMarks();
DateTime now = DateTime.Now;
DayOfWeek weekEnum = now.DayOfWeek;
if (weekEnum.Equals(DayOfWeek.Monday))
{
_morningCheckTimeTwo = new TimeSpan(10, 30, 0);
_workStartTimeTwo = new TimeSpan(10, 0, 0);
}
// 1. 启动早上检查定时器(若目标时间未过)
long morningDelayMs = CalculateDelayToTargetTime(_morningCheckTimeTwo, "morning");
if (morningDelayMs > 0 && _morningCheckTimeTwo > _workStartTimeTwo)
{
_morningRodAlarmTimerTwo = new System.Timers.Timer(morningDelayMs);
_morningRodAlarmTimerTwo.AutoReset = false; // 一次性执行,不循环
_morningRodAlarmTimerTwo.Elapsed += MorningRodDeviceTwoAlarmTimer_Elapsed;
_morningRodAlarmTimerTwo.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"早上连杆检查定时器已启动:将在{_morningCheckTimeTwo:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("早上检查节点已过当天当前时间,跳过早上连杆检查定时器启动");
_isMorningRodUploadedTwo = true; // 标记为已检查,避免重复判断
}
// 2. 启动晚上检查定时器(若目标时间未过)
long eveningDelayMs = CalculateDelayToTargetTime(_eveningCheckTimeTwo, "evening");
if (eveningDelayMs > 0 && _eveningCheckTimeTwo > _workEndTimeTwo)
{
_eveningRodAlarmTimerTwo = new System.Timers.Timer(eveningDelayMs);
_eveningRodAlarmTimerTwo.AutoReset = false; // 一次性执行,不循环
_eveningRodAlarmTimerTwo.Elapsed += EveningRodDeviceTwoAlarmTimer_Elapsed;
_eveningRodAlarmTimerTwo.Enabled = true; // 启动定时器
LogHelper.AppendLog(
$"晚上连杆检查定时器已启动:将在{_eveningCheckTimeTwo:hh\\:mm}触发");
}
else
{
LogHelper.AppendLog("晚上检查节点已过当天当前时间,跳过晚上连杆检查定时器启动");
_isEveningRodUploadedTwo = true; // 标记为已检查,避免重复判断
}
}
///
/// 终止所有报警定时任务并重置标记
///
private void StopDeviceOneAlarmTimers()
{
// 终止报废定时
if (_scrapAlarmTimerOne != null)
{
_scrapAlarmTimerOne.Enabled = false;
_scrapAlarmTimerOne.Dispose();
_scrapAlarmTimerOne = null;
}
if (_scrapAlarmTimerTwo != null)
{
_scrapAlarmTimerTwo.Enabled = false;
_scrapAlarmTimerTwo.Dispose();
_scrapAlarmTimerTwo = null;
}
LogHelper.AppendLog("报废上传报警定时已终止");
// 重置静态上传标记
_isScrapImageUploadedOne = false;
_isRodImageUploadedOne = false;
}
///
/// 终止所有报警定时任务并重置标记
///
private void StopDeviceTwoAlarmTimers()
{
// 终止报废定时
if (_rodAlarmTimerOne != null)
{
_rodAlarmTimerOne.Enabled = false;
_rodAlarmTimerOne.Dispose();
_rodAlarmTimerOne = null;
}
if (_rodAlarmTimerTwo != null)
{
_rodAlarmTimerTwo.Enabled = false;
_rodAlarmTimerTwo.Dispose();
_rodAlarmTimerTwo = null;
}
LogHelper.AppendLog("连杆上传报警定时已终止");
// 重置静态上传标记
_isScrapImageUploadedTwo = false;
_isRodImageUploadedTwo = false;
}
///
/// 跳过报废报警(设备1)
///
///
///
private void ScrapAlarmTimerOne_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 使用静态标记 _isScrapImageUploaded 判断是否报警(核心:上传成功则不报警)
if (!_isScrapImageUploadedOne)
{
string alarmContent = $"报废上传超时未上传凭证!
当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false, "1")));
_isScrapImageWorkNoticeOne = true;
}
else
{
LogHelper.AppendLog($"报废上传{_lastScrapIntervalOne}分钟内已上传凭证,无需触发报警");
}
// 3. 销毁定时器,重置标记
_scrapAlarmTimerOne?.Dispose();
_scrapAlarmTimerOne = null;
_isScrapImageUploadedOne = false;
}
///
/// 跳过报废报警(设备2)
///
///
///
private void ScrapAlarmTimerTwo_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 使用静态标记 _isScrapImageUploaded 判断是否报警(核心:上传成功则不报警)
if (!_isScrapImageUploadedTwo)
{
string alarmContent = $"报废上传超时未上传凭证!
当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false, "2")));
_isScrapImageWorkNoticeTwo = true;
}
else
{
LogHelper.AppendLog($"报废上传{_lastScrapIntervalTwo}分钟内已上传凭证,无需触发报警");
}
// 3. 销毁定时器,重置标记
_scrapAlarmTimerTwo?.Dispose();
_scrapAlarmTimerTwo = null;
_isScrapImageUploadedTwo = false;
}
///
/// 跳过连杆报警(设备1)
///
///
///
private void RodAlarmTimerOne_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 2. 使用静态标记 _isRodImageUploaded 判断是否报警
if (!_isRodImageUploadedOne)
{
string alarmContent = $"连杆测试超时未上传图片!
当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true, "1")));
_isRodImageWorkNoticeOne = true;
}
else
{
LogHelper.AppendLog($"连杆测试{_lastRodIntervalOne}分钟内已上传图片,无需触发报警");
}
// 3. 销毁定时器,重置标记
_rodAlarmTimerOne?.Dispose();
_rodAlarmTimerOne = null;
_isRodImageUploadedOne = false;
}
///
/// 跳过连杆报警(设备2)
///
///
///
private void RodAlarmTimerTwo_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime now = DateTime.Now;
TimeSpan currentTime = now.TimeOfDay;
// 2. 使用静态标记 _isRodImageUploaded 判断是否报警
if (!_isRodImageUploadedTwo)
{
string alarmContent = $"连杆测试超时未上传图片!
当前时间:{now:yyyy-MM-dd HH:mm:ss}";
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true, "2")));
_isRodImageWorkNoticeTwo = true;
}
else
{
LogHelper.AppendLog($"连杆测试{_lastRodIntervalTwo}分钟内已上传图片,无需触发报警");
}
// 3. 销毁定时器,重置标记
_rodAlarmTimerTwo?.Dispose();
_rodAlarmTimerTwo = null;
_isRodImageUploadedTwo = false;
}
///
/// 发送钉钉报警
///
///
///
private async void SendDingDingAlarm(string alarmContent, bool isRodAlarm, string deviceCode)
{
try
{
LogHelper.AppendLog($"【钉钉报警】{(isRodAlarm ? "连杆" : "报废")}:{alarmContent}");
await _uploadService.SendDingDingTextAlarmAsync(alarmContent, deviceCode, isRodAlarm);
// if (deviceCode.Contains("1"))
// {
// _isScrapImageWorkNoticeOne = true;
// }
// else
// {
// _isScrapImageWorkNoticeTwo = true;
// }
// MessageBox.Show($"已发送钉钉{(isRodAlarm ? "连杆" : "报废")}报警:{alarmContent}", "报警提示", MessageBoxButtons.OK,
// MessageBoxIcon.Warning);
}
catch (Exception ex)
{
LogHelper.AppendLog($"{(isRodAlarm ? "连杆" : "报废")}钉钉报警发送失败:{ex.Message}");
}
}
// ======================================== 上传状态回调方法(连杆+报废) ========================================
///
/// 报废上传状态回调(设备1)
///
private void OnScrapUploadStatusChangedOne(object sender, ScrapUploadStatusEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action