1045 lines
42 KiB
C#
1045 lines
42 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 Newtonsoft.Json;
|
||
using YiDa_WinForm.Business;
|
||
using YiDa_WinForm.Config;
|
||
using YiDa_WinForm.Model;
|
||
using YiDa_WinForm.Service;
|
||
using YiDa_WinForm.Service.Mqtt;
|
||
using YiDa_WinForm.Utils;
|
||
|
||
namespace YiDa_WinForm
|
||
{
|
||
public partial class MainForm : Form
|
||
{
|
||
// 依赖注入
|
||
private readonly MqttClientService _mqttService;
|
||
private readonly YiDaUploadService _uploadService;
|
||
private readonly ButtonOperationService _buttonService;
|
||
private readonly FormulaBusinessService _formulaBusinessService;
|
||
|
||
// 通用上传定时器
|
||
private System.Timers.Timer _timer;
|
||
|
||
// 日志文件相关路径
|
||
private readonly string _logDirectory = "log";
|
||
private string _currentLogFile;
|
||
private DateTime _lastLogDate = DateTime.MinValue;
|
||
|
||
// 工作时间配置(默认 9:00 - 23:30)
|
||
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;
|
||
|
||
// ======================================== 初始化相关方法 ========================================
|
||
public MainForm()
|
||
{
|
||
// 初始化设计器方法
|
||
InitializeComponent();
|
||
|
||
// 订阅日志事件
|
||
LogHelper.OnLogGenerated += LogHelper_OnLogGenerated;
|
||
|
||
// 初始化服务实例
|
||
_buttonService = new ButtonOperationService();
|
||
_mqttService = new MqttClientService();
|
||
_uploadService = new YiDaUploadService();
|
||
_formulaBusinessService = new FormulaBusinessService(_buttonService);
|
||
|
||
// 绑定回调事件(连杆+报废)
|
||
_mqttService.MessageReceived += OnMqttMessage;
|
||
_uploadService.RodUploadStatusChanged += OnRodUploadStatusChanged;
|
||
_uploadService.ScrapUploadStatusChanged += OnScrapUploadStatusChanged;
|
||
|
||
// UI初始化配置
|
||
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>
|
||
private async void Form1_Load(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
MakePanelRound(panelLed);
|
||
await LoadFormula();
|
||
await LoadMqttDic();
|
||
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>
|
||
/// 加载配方到UI下拉框
|
||
/// </summary>
|
||
private async Task LoadFormula()
|
||
{
|
||
await _formulaBusinessService.LoadFormulaAsync();
|
||
|
||
// 绑定下拉框数据源
|
||
bsPF.DataSource = _formulaBusinessService.UniqueFormula;
|
||
bsPF2.DataSource = _formulaBusinessService.UniqueFormula;
|
||
|
||
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"]} - {drv["part_name"]}";
|
||
}
|
||
};
|
||
comboBox2.Format += (s, e) =>
|
||
{
|
||
if (e.ListItem is DataRowView drv)
|
||
{
|
||
e.Value = string.IsNullOrEmpty(drv["part_number_name"].ToString())
|
||
? ""
|
||
: $"{drv["part_number"]} - {drv["part_name"]}";
|
||
}
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载MQTT字典到UI
|
||
/// </summary>
|
||
private async Task LoadMqttDic()
|
||
{
|
||
await _formulaBusinessService.LoadMqttDicAsync();
|
||
}
|
||
|
||
// ======================================== 按钮相关方法 ========================================
|
||
/// <summary>
|
||
/// 网关连接按钮
|
||
/// </summary>
|
||
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 = "连接失败";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 网关断开按钮
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <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)
|
||
{
|
||
string filePath = openFile.FileName;
|
||
DataTable excelDt = ExcelHelper.GetExcelToDataTable(filePath);
|
||
if (excelDt != null && excelDt.Rows.Count > 0)
|
||
{
|
||
await _buttonService.SaveFormulaByExcel(excelDt);
|
||
await LoadFormula();
|
||
MessageBox.Show("配方信息已导入!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("不允许导入空表!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"配方导入失败:{ex.Message}");
|
||
MessageBox.Show($"配方导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传宜搭按钮
|
||
/// </summary>
|
||
private async void button2_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (toolStripStatusLabel1.Text != "已连接")
|
||
{
|
||
MessageBox.Show("请先连接MQTT网关!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(comboBox1.Text) && string.IsNullOrEmpty(comboBox2.Text))
|
||
{
|
||
MessageBox.Show("请先导入配方信息并选择配方!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
button2.Enabled = false;
|
||
|
||
if (_timer != null)
|
||
{
|
||
_timer.Enabled = false;
|
||
_timer.Dispose();
|
||
}
|
||
|
||
RefreshSelectedFormulaToService();
|
||
|
||
double seconds = 10;
|
||
if (double.TryParse(txtLUploadPL.Text, out double inputSeconds) && inputSeconds > 0)
|
||
{
|
||
seconds = inputSeconds;
|
||
}
|
||
|
||
await MqttYiDaUpload();
|
||
|
||
_timer = new System.Timers.Timer(seconds * 1000);
|
||
_timer.Elapsed += Timer_Elapsed;
|
||
_timer.AutoReset = true;
|
||
_timer.Enabled = true;
|
||
|
||
LogHelper.AppendLog($"自动上传已启动,间隔:{seconds}秒");
|
||
MessageBox.Show($"自动上传已启动,间隔:{seconds}秒", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
button2.Enabled = true;
|
||
LogHelper.AppendLog($"上传宜搭失败:{ex.Message}");
|
||
MessageBox.Show($"上传宜搭失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <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())
|
||
{
|
||
DialogResult result = scrapForm.ShowDialog(this);
|
||
|
||
if (result == DialogResult.OK)
|
||
{
|
||
bool isScrapUpload = scrapForm.IsScrapUploadSelected;
|
||
bool isScrapTimedAlarm = scrapForm.IsScrapTimedAlarmSelected;
|
||
int interval = scrapForm.ScrapTimedInterval;
|
||
|
||
// ========== 核心逻辑:全不选中时销毁报废定时器 ==========
|
||
if (!isScrapUpload && !isScrapTimedAlarm)
|
||
{
|
||
if (_scrapAlarmTimer != null)
|
||
{
|
||
_scrapAlarmTimer.Enabled = false;
|
||
_scrapAlarmTimer.Dispose();
|
||
_scrapAlarmTimer = null;
|
||
LogHelper.AppendLog("未勾选任何选框,已销毁报废上传定时报警定时器");
|
||
MessageBox.Show("未勾选任何选框,已关闭报废上传定时报警", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("未勾选任何选框,当前无运行中的报废上传定时报警定时器");
|
||
MessageBox.Show("未勾选任何选框,当前无运行中的定时报警任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
return;
|
||
}
|
||
|
||
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);
|
||
MessageBox.Show(isImageUploadSuccess ? "报废凭证图片已上传!" : "报废凭证图片上传失败!");
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("不允许上传空文件!");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isScrapTimedAlarm)
|
||
{
|
||
LogHelper.AppendLog($"勾选了报废定时上传报警,准备启动定时任务(间隔{interval}分钟)");
|
||
TimeSpan currentTime = DateTime.Now.TimeOfDay;
|
||
if (currentTime < _workStartTime || currentTime > _workEndTime)
|
||
{
|
||
this.Invoke(new Action(() =>
|
||
{
|
||
MessageBox.Show(
|
||
$"当前时间不在工作时间({_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm})内,定时任务将在工作时间内自动生效",
|
||
"提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}));
|
||
}
|
||
|
||
StartScrapAlarmTimer(interval);
|
||
}
|
||
|
||
LogHelper.AppendLog(
|
||
$"选择结果:报废凭证上传={isScrapUpload}(上传结果={isImageUploadSuccess}),定时上传报警={isScrapTimedAlarm},时间间隔={interval}分钟");
|
||
if (!isScrapUpload && isScrapTimedAlarm)
|
||
{
|
||
MessageBox.Show("定时报警任务已启动,将在工作时间内按配置间隔执行判断", "成功", MessageBoxButtons.OK,
|
||
MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连杆测试上传按钮(完整逻辑:定时报警 + 全不选中关闭定时器)
|
||
/// </summary>
|
||
private async void button5_Click(object sender, EventArgs e)
|
||
{
|
||
using (var selectForm = new StrengthTestUploadForm())
|
||
{
|
||
DialogResult result = selectForm.ShowDialog(this);
|
||
|
||
if (result == DialogResult.OK)
|
||
{
|
||
bool isMoldUpload = selectForm.IsMoldProductionSelected;
|
||
bool isTimedAlarm = selectForm.IsTimedAlarmSelected;
|
||
int interval = selectForm.TimedInterval;
|
||
|
||
// ========== 核心逻辑:全不选中时销毁连杆定时器 ==========
|
||
if (!isMoldUpload && !isTimedAlarm)
|
||
{
|
||
if (_rodAlarmTimer != null)
|
||
{
|
||
_rodAlarmTimer.Enabled = false;
|
||
_rodAlarmTimer.Dispose();
|
||
_rodAlarmTimer = null;
|
||
LogHelper.AppendLog("未勾选任何选框,已销毁连杆测试定时报警定时器");
|
||
MessageBox.Show("未勾选任何选框,已关闭连杆测试定时报警", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog("未勾选任何选框,当前无运行中的连杆测试定时报警定时器");
|
||
MessageBox.Show("未勾选任何选框,当前无运行中的定时报警任务", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
return;
|
||
}
|
||
|
||
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);
|
||
MessageBox.Show(isImageUploadSuccess ? "连杆测试图片已上传!" : "连杆测试图片上传失败!");
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("不允许上传空文件!");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isTimedAlarm)
|
||
{
|
||
LogHelper.AppendLog($"勾选了定时上传报警,准备启动定时任务(间隔{interval}分钟)");
|
||
TimeSpan currentTime = DateTime.Now.TimeOfDay;
|
||
if (currentTime < _workStartTime || currentTime > _workEndTime)
|
||
{
|
||
this.Invoke(new Action(() =>
|
||
{
|
||
MessageBox.Show(
|
||
$"当前时间不在工作时间({_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm})内,定时任务将在工作时间内自动生效",
|
||
"提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}));
|
||
}
|
||
|
||
StartRodAlarmTimer(interval);
|
||
}
|
||
|
||
LogHelper.AppendLog(
|
||
$"选择结果:模具投产上传={isMoldUpload}(上传结果={isImageUploadSuccess}),定时上传报警={isTimedAlarm},时间间隔={interval}分钟");
|
||
if (!isMoldUpload && isTimedAlarm)
|
||
{
|
||
MessageBox.Show("定时报警任务已启动,将在工作时间内按配置间隔执行判断", "成功", 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;
|
||
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();
|
||
}
|
||
|
||
await _mqttService.MqttClientStopAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.AppendLog($"关闭窗口失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新配方按钮
|
||
/// </summary>
|
||
private async void btnRefresh_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await LoadFormula();
|
||
await LoadMqttDic();
|
||
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 StartRodAlarmTimer(int intervalMinutes)
|
||
{
|
||
if (_rodAlarmTimer != null)
|
||
{
|
||
_rodAlarmTimer.Enabled = false;
|
||
_rodAlarmTimer.Dispose();
|
||
}
|
||
|
||
_rodAlarmTimer = new System.Timers.Timer(intervalMinutes * 60 * 1000);
|
||
_rodAlarmTimer.Elapsed += RodAlarmTimer_Elapsed;
|
||
_rodAlarmTimer.AutoReset = true;
|
||
_rodAlarmTimer.Enabled = true;
|
||
|
||
_isImageUploadedInCycle = false;
|
||
LogHelper.AppendLog(
|
||
$"连杆测试定时报警已启动:间隔{intervalMinutes}分钟,工作时间{_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动报废上传定时报警任务
|
||
/// </summary>
|
||
private void StartScrapAlarmTimer(int intervalMinutes)
|
||
{
|
||
if (_scrapAlarmTimer != null)
|
||
{
|
||
_scrapAlarmTimer.Enabled = false;
|
||
_scrapAlarmTimer.Dispose();
|
||
}
|
||
|
||
_scrapAlarmTimer = new System.Timers.Timer(intervalMinutes * 60 * 1000);
|
||
_scrapAlarmTimer.Elapsed += ScrapAlarmTimer_Elapsed;
|
||
_scrapAlarmTimer.AutoReset = true;
|
||
_scrapAlarmTimer.Enabled = true;
|
||
|
||
_isScrapImageUploadedInCycle = false;
|
||
LogHelper.AppendLog(
|
||
$"报废上传定时报警已启动:间隔{intervalMinutes}分钟,工作时间{_workStartTime:hh\\:mm}-{_workEndTime:hh\\:mm}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连杆报警定时器触发事件
|
||
/// </summary>
|
||
private void RodAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
TimeSpan currentTime = now.TimeOfDay;
|
||
if (currentTime < _workStartTime || currentTime > _workEndTime)
|
||
{
|
||
LogHelper.AppendLog($"当前时间{currentTime:hh\\:mm}不在工作时间内,跳过本次连杆报警判断");
|
||
_isImageUploadedInCycle = false;
|
||
return;
|
||
}
|
||
|
||
if (!_isImageUploadedInCycle)
|
||
{
|
||
string alarmContent =
|
||
$"连杆测试超时未上传图片!当前时间:{now:yyyy-MM-dd HH:mm:ss},配置间隔:{_rodAlarmTimer.Interval / 60000}分钟";
|
||
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, true)));
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog($"连杆测试计时周期内已上传图片,无需报警,重置标记");
|
||
}
|
||
|
||
_isImageUploadedInCycle = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 报废报警定时器触发事件
|
||
/// </summary>
|
||
private void ScrapAlarmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
DateTime now = DateTime.Now;
|
||
TimeSpan currentTime = now.TimeOfDay;
|
||
if (currentTime < _workStartTime || currentTime > _workEndTime)
|
||
{
|
||
LogHelper.AppendLog($"当前时间{currentTime:hh\\:mm}不在工作时间内,跳过本次报废报警判断");
|
||
_isScrapImageUploadedInCycle = false;
|
||
return;
|
||
}
|
||
|
||
if (!_isScrapImageUploadedInCycle)
|
||
{
|
||
string alarmContent =
|
||
$"报废上传超时未上传凭证!当前时间:{now:yyyy-MM-dd HH:mm:ss},配置间隔:{_scrapAlarmTimer.Interval / 60000}分钟";
|
||
this.Invoke(new Action(() => SendDingDingAlarm(alarmContent, false)));
|
||
}
|
||
else
|
||
{
|
||
LogHelper.AppendLog($"报废上传计时周期内已上传凭证,无需报警,重置标记");
|
||
}
|
||
|
||
_isScrapImageUploadedInCycle = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送钉钉报警(区分连杆/报废)
|
||
/// </summary>
|
||
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;
|
||
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;
|
||
LogHelper.AppendLog($"报废上传状态:{(e.IsUploadSuccess ? "成功" : "失败")},消息:{e.Message}");
|
||
}
|
||
|
||
// ======================================== 辅助方法 ========================================
|
||
/// <summary>
|
||
/// 定时器触发事件
|
||
/// </summary>
|
||
private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
RefreshSelectedFormulaToService();
|
||
await MqttYiDaUpload();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发下拉框选择值到配方业务服务
|
||
/// </summary>
|
||
private void RefreshSelectedFormulaToService()
|
||
{
|
||
string selected1 = comboBox1.SelectedValue?.ToString() ?? string.Empty;
|
||
string selected2 = comboBox2.SelectedValue?.ToString() ?? string.Empty;
|
||
_formulaBusinessService.RefreshSelectedFormula(selected1, selected2);
|
||
}
|
||
|
||
// ======================================== 核心上传方法 ========================================
|
||
/// <summary>
|
||
/// 上传宜搭方法
|
||
/// </summary>
|
||
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}]数据");
|
||
|
||
var (formulaList, site) = GetDeviceFormulaAndSite(deviceCode);
|
||
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;
|
||
}
|
||
|
||
string paramName = _formulaBusinessService.MapMqttParamName(paramCode);
|
||
DataRow drMatch = _formulaBusinessService.MatchFormulaByParamName(formulaList, 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);
|
||
}));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取设备对应的配方和工位
|
||
/// </summary>
|
||
private Tuple<List<DataRow>, string> GetDeviceFormulaAndSite(string deviceCode)
|
||
{
|
||
if (string.Equals(deviceCode, "device1", StringComparison.Ordinal))
|
||
{
|
||
return Tuple.Create(_formulaBusinessService.SelectedFormula1, "1号设备");
|
||
}
|
||
else if (string.Equals(deviceCode, "device2", StringComparison.Ordinal))
|
||
{
|
||
return Tuple.Create(_formulaBusinessService.SelectedFormula2, "2号设备");
|
||
}
|
||
else
|
||
{
|
||
return Tuple.Create<List<DataRow>, string>(null, null);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造MqttModel
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造YiDaModel
|
||
/// </summary>
|
||
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);
|
||
|
||
if (paramName == "报废图片路径" && _buttonService._formulaValue == "报废图片路径")
|
||
{
|
||
yidaNew.imageField_mii5s85z = _uploadService._reformationPicture;
|
||
}
|
||
|
||
yidaNew.textField_mha98nf5 = JudgeQualification(paramName, value, drMatch);
|
||
|
||
return yidaNew;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合格判断
|
||
/// </summary>
|
||
private string JudgeQualification(string paramName, object value, DataRow drMatch)
|
||
{
|
||
if (paramName == "报警信息")
|
||
{
|
||
return string.Equals(value?.ToString(), "无错误", StringComparison.Ordinal) ? "合格" : "不合格";
|
||
}
|
||
else if (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 "不合格";
|
||
}
|
||
}
|
||
}
|
||
|
||
// ======================================== 日志相关方法 ========================================
|
||
/// <summary>
|
||
/// 接收MQTT消息并写入日志
|
||
/// </summary>
|
||
private void OnMqttMessage(string msg)
|
||
{
|
||
if (this.InvokeRequired)
|
||
{
|
||
this.Invoke(new Action<string>(OnMqttMessage), msg);
|
||
return;
|
||
}
|
||
|
||
LogHelper.AppendLog($"收到消息: {msg}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理日志事件,将日志追加到WinForm界面的txtLog控件
|
||
/// </summary>
|
||
private void LogHelper_OnLogGenerated(string logContent)
|
||
{
|
||
if (textBoxLog.InvokeRequired)
|
||
{
|
||
textBoxLog.Invoke(new Action(() => { AppendLogToTextBox(logContent); }));
|
||
}
|
||
else
|
||
{
|
||
AppendLogToTextBox(logContent);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 追加日志到TextBox,并自动滚动到最新日志
|
||
/// </summary>
|
||
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, EventArgs e)
|
||
{
|
||
}
|
||
|
||
private void label6_Click(object sender, EventArgs e)
|
||
{
|
||
}
|
||
}
|
||
} |