2764 lines
103 KiB
C#
2764 lines
103 KiB
C#
using Google.Protobuf.WellKnownTypes;
|
||||
|
|
using JinYuan.ControlLib;
|
|||
|
|
using JinYuan.Helper;
|
|||
|
|
using JinYuan.MES.Models;
|
|||
|
|
using JinYuan.Models;
|
|||
|
|
using JinYuan.Models.HelperAttribute;
|
|||
|
|
using JinYuan.VirtualDataLibrary;
|
|||
|
|
using Language;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.ComponentModel;
|
|||
|
|
using System.Data;
|
|||
|
|
using System.Diagnostics;
|
|||
|
|
using System.Drawing;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Linq.Expressions;
|
|||
|
|
using System.Reflection;
|
|||
|
|
using System.Runtime.InteropServices;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using System.Windows.Documents;
|
|||
|
|
using System.Windows.Forms;
|
|||
|
|
|
|||
|
|
namespace LargeSquareOne
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
public partial class FrmBatteryStatus : MultiLanguageForm
|
|||
|
|
{
|
|||
|
|
#region 字段定义
|
|||
|
|
// 电池控件字典:Key=stationNo(1/2/3),Value=控件字典(Key=layer-row-col)
|
|||
|
|
private readonly Dictionary<int, Dictionary<string, BatteryControl>> _batteryControlsByStationNo = new Dictionary<int, Dictionary<string, BatteryControl>>();
|
|||
|
|
|
|||
|
|
// 条码到控件映射(加速查找)
|
|||
|
|
private readonly Dictionary<string, (int stationNo, string controlKey)> _barcodeToControlMap = new Dictionary<string, (int, string)>();
|
|||
|
|
|
|||
|
|
// 数据绑定列表
|
|||
|
|
private readonly BindingList<BatteryReplace> _replaceRecords = new BindingList<BatteryReplace>();
|
|||
|
|
private readonly BindingList<BatteryEntity> _ngBatteryRecords = new BindingList<BatteryEntity>();
|
|||
|
|
private readonly BindingList<BatteryEntity> _allBatteryRecords = new BindingList<BatteryEntity>();
|
|||
|
|
|
|||
|
|
// 统计标签字典
|
|||
|
|
private readonly Dictionary<int, List<System.Windows.Forms.Label>> _statisticsLabelsByLoc = new Dictionary<int, List<System.Windows.Forms.Label>>();
|
|||
|
|
|
|||
|
|
// 并发控制
|
|||
|
|
private readonly SemaphoreSlim _operationSemaphore = new SemaphoreSlim(1, 1);
|
|||
|
|
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
|||
|
|
|
|||
|
|
// 防抖控制
|
|||
|
|
private DateTime _lastStatisticsUpdateTime = DateTime.MinValue;
|
|||
|
|
private DateTime _lastBatteryClickTime = DateTime.MinValue;
|
|||
|
|
|
|||
|
|
// 状态标记
|
|||
|
|
private bool _isControlsInitialized = false;
|
|||
|
|
|
|||
|
|
// 统计项显示名称
|
|||
|
|
private readonly string[] _statusDisplayNames = new string[]
|
|||
|
|
{
|
|||
|
|
"电芯替换总数", // GradeNG
|
|||
|
|
"电芯装箱NG总数", // PackNG
|
|||
|
|
"电芯档位&装箱OK总数", // Normal
|
|||
|
|
"满箱还缺总数" // Init
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 性能监控
|
|||
|
|
private PerformanceCounter _cpuUsageCounter;
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 构造函数
|
|||
|
|
public FrmBatteryStatus()
|
|||
|
|
{
|
|||
|
|
InitializeComponent();
|
|||
|
|
// 基础窗体设置
|
|||
|
|
ConfigureFormProperties();
|
|||
|
|
|
|||
|
|
// 性能优化设置
|
|||
|
|
ApplyPerformanceOptimizations();
|
|||
|
|
|
|||
|
|
// 初始化控件
|
|||
|
|
InitializeDataGridViews();
|
|||
|
|
BindScannerEvents();
|
|||
|
|
InitializeDataBindings();
|
|||
|
|
|
|||
|
|
// 事件绑定
|
|||
|
|
Load += FrmBatteryStatus_Load;
|
|||
|
|
|
|||
|
|
// 性能监控初始化
|
|||
|
|
InitializePerformanceMonitoring();
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 初始化方法
|
|||
|
|
/// <summary>
|
|||
|
|
/// 配置窗体基础属性
|
|||
|
|
/// </summary>
|
|||
|
|
private void ConfigureFormProperties()
|
|||
|
|
{
|
|||
|
|
Dock = DockStyle.Fill;
|
|||
|
|
AutoSize = false;
|
|||
|
|
FormBorderStyle = FormBorderStyle.None;
|
|||
|
|
Padding = new Padding(0);
|
|||
|
|
Margin = new Padding(0);
|
|||
|
|
|
|||
|
|
// 双缓冲设置
|
|||
|
|
SetStyle(
|
|||
|
|
ControlStyles.OptimizedDoubleBuffer |
|
|||
|
|
ControlStyles.AllPaintingInWmPaint |
|
|||
|
|
ControlStyles.UserPaint |
|
|||
|
|
ControlStyles.ResizeRedraw, true);
|
|||
|
|
DoubleBuffered = true;
|
|||
|
|
SetStyle(ControlStyles.SupportsTransparentBackColor, false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 应用全局性能优化
|
|||
|
|
/// </summary>
|
|||
|
|
private void ApplyPerformanceOptimizations()
|
|||
|
|
{
|
|||
|
|
// 禁用不必要的动画
|
|||
|
|
Application.EnableVisualStyles();
|
|||
|
|
//Application.SetCompatibleTextRenderingDefault(false);
|
|||
|
|
|
|||
|
|
// 递归设置所有控件双缓冲
|
|||
|
|
SetDoubleBufferingRecursive(this, true);
|
|||
|
|
|
|||
|
|
// 禁用自动滚动减少重绘
|
|||
|
|
AutoScroll = false;
|
|||
|
|
AutoScrollMinSize = new Size(0, 0);
|
|||
|
|
|
|||
|
|
// 优化TabControl
|
|||
|
|
if (tabBattery != null)
|
|||
|
|
{
|
|||
|
|
SetDoubleBuffering(tabBattery, true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (_cpuUsageCounter != null)
|
|||
|
|
{
|
|||
|
|
_cpuUsageCounter.Dispose();
|
|||
|
|
_cpuUsageCounter = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化DataGridView控件
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeDataGridViews()
|
|||
|
|
{
|
|||
|
|
OptimizeDataGridView(dgv_ngRsn);
|
|||
|
|
OptimizeDataGridView(dgvReplaceBattery);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 绑定扫描枪输入事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void BindScannerEvents()
|
|||
|
|
{
|
|||
|
|
// 回车切换焦点
|
|||
|
|
txtBox_Barcode.KeyDown += (s, e) =>
|
|||
|
|
{
|
|||
|
|
if (e.KeyCode == Keys.Enter && !string.IsNullOrEmpty(txtBox_Barcode.Text.Trim()))
|
|||
|
|
{
|
|||
|
|
e.SuppressKeyPress = true;
|
|||
|
|
txtBox_SubBarcode?.Focus();
|
|||
|
|
txtBox_SubBarcode?.SelectAll();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 焦点获取时全选文本
|
|||
|
|
txtBox_Barcode.Enter += (s, e) => ((TextBox)s)?.SelectAll();
|
|||
|
|
txtBox_SubBarcode.Enter += (s, e) => ((TextBox)s)?.SelectAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化数据绑定
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeDataBindings()
|
|||
|
|
{
|
|||
|
|
dgvReplaceBattery.DataSource = _replaceRecords;
|
|||
|
|
dgv_ngRsn.DataSource = _ngBatteryRecords;
|
|||
|
|
|
|||
|
|
// 验证绑定
|
|||
|
|
Debug.WriteLine($"dgv_ngRsn数据源类型:{dgv_ngRsn.DataSource?.GetType().Name}");
|
|||
|
|
Debug.WriteLine($"dgv_ngRsn列数:{dgv_ngRsn.Columns.Count}");
|
|||
|
|
|
|||
|
|
_ngBatteryRecords.ListChanged += (s, e) =>
|
|||
|
|
{
|
|||
|
|
if (dgv_ngRsn.IsHandleCreated)
|
|||
|
|
dgv_ngRsn.BeginInvoke(() => dgv_ngRsn.Invalidate());
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
_replaceRecords.ListChanged += (s, e) =>
|
|||
|
|
{
|
|||
|
|
if (dgvReplaceBattery.IsHandleCreated)
|
|||
|
|
dgvReplaceBattery.BeginInvoke(() => dgvReplaceBattery.Invalidate());
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化性能监控
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializePerformanceMonitoring()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 先释放旧的计数器
|
|||
|
|
if (_cpuUsageCounter != null)
|
|||
|
|
{
|
|||
|
|
_cpuUsageCounter.Dispose();
|
|||
|
|
_cpuUsageCounter = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_cpuUsageCounter = new PerformanceCounter(
|
|||
|
|
"Process",
|
|||
|
|
"% Processor Time",
|
|||
|
|
Process.GetCurrentProcess().ProcessName);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"性能监控初始化失败:{ex}", "BatteryStatusLog");
|
|||
|
|
_cpuUsageCounter = null; // 确保置空
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 性能优化相关方法
|
|||
|
|
/// <summary>
|
|||
|
|
/// 优化DataGridView性能
|
|||
|
|
/// </summary>
|
|||
|
|
private void OptimizeDataGridView(DataGridView dgv)
|
|||
|
|
{
|
|||
|
|
if (dgv == null) return;
|
|||
|
|
|
|||
|
|
// 设置双缓冲
|
|||
|
|
var doubleBufferProp = dgv.GetType().GetProperty(
|
|||
|
|
"DoubleBuffered",
|
|||
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|||
|
|
doubleBufferProp?.SetValue(dgv, true);
|
|||
|
|
|
|||
|
|
dgv.EnableHeadersVisualStyles = false;
|
|||
|
|
dgv.AutoGenerateColumns = false;
|
|||
|
|
dgv.RowHeadersVisible = true;
|
|||
|
|
dgv.AllowUserToAddRows = false;
|
|||
|
|
dgv.AllowUserToDeleteRows = false;
|
|||
|
|
dgv.AllowUserToOrderColumns = false;
|
|||
|
|
dgv.AllowUserToResizeRows = false;
|
|||
|
|
dgv.ReadOnly = true;
|
|||
|
|
dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
|||
|
|
dgv.MultiSelect = false;
|
|||
|
|
|
|||
|
|
// 减少绘制开销
|
|||
|
|
dgv.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
|
|||
|
|
dgv.ColumnHeadersHeight = 25;
|
|||
|
|
dgv.RowTemplate.Height = 22;
|
|||
|
|
dgv.ScrollBars = ScrollBars.Vertical;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置控件双缓冲
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="control">目标控件</param>
|
|||
|
|
/// <param name="enable">是否启用</param>
|
|||
|
|
private void SetDoubleBuffering(Control control, bool enable)
|
|||
|
|
{
|
|||
|
|
if (control == null) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var prop = control.GetType().GetProperty(
|
|||
|
|
"DoubleBuffered",
|
|||
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|||
|
|
prop?.SetValue(control, enable);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteLog($"设置双缓冲失败:{ex.Message}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 递归设置控件双缓冲
|
|||
|
|
/// </summary>
|
|||
|
|
private void SetDoubleBufferingRecursive(Control control, bool enable)
|
|||
|
|
{
|
|||
|
|
if (control == null) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
SetDoubleBuffering(control, enable);
|
|||
|
|
|
|||
|
|
foreach (Control child in control.Controls)
|
|||
|
|
{
|
|||
|
|
SetDoubleBufferingRecursive(child, enable);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch
|
|||
|
|
{
|
|||
|
|
// 忽略设置失败
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 窗体加载与控件初始化
|
|||
|
|
private async void FrmBatteryStatus_Load(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (_isControlsInitialized) return;
|
|||
|
|
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
this.SuspendLayout();
|
|||
|
|
SetPanelsVisibility(false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await Task.Delay(100);
|
|||
|
|
|
|||
|
|
// 所有控件创建必须走 UI()
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
InitializeBatteryControls();
|
|||
|
|
_isControlsInitialized = true;
|
|||
|
|
|
|||
|
|
panelExt_Grade1?.Invalidate();
|
|||
|
|
panelExt_Grade2?.Invalidate();
|
|||
|
|
panelExt_Grade3?.Invalidate();
|
|||
|
|
tabBattery?.Invalidate();
|
|||
|
|
|
|||
|
|
this.ResumeLayout(false);
|
|||
|
|
Invalidate();
|
|||
|
|
SetPanelsVisibility(true);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"加载电池界面异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 异步初始化电池控件(避免UI阻塞)
|
|||
|
|
/// </summary>
|
|||
|
|
private async Task InitializeBatteryControlsAsync()
|
|||
|
|
{
|
|||
|
|
await Task.Run(() =>
|
|||
|
|
{
|
|||
|
|
if (InvokeRequired)
|
|||
|
|
{
|
|||
|
|
Invoke(new Action(InitializeBatteryControls));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
InitializeBatteryControls();
|
|||
|
|
}
|
|||
|
|
}).ConfigureAwait(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化电池控件
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeBatteryControls()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 获取配置参数
|
|||
|
|
var config = CommonMethods.mesConfig;
|
|||
|
|
int layers = config?.packLayers ?? 4;
|
|||
|
|
int rows = config?.rowsPerLayer ?? 5;
|
|||
|
|
int cols = config?.colsPerLayer ?? 12;
|
|||
|
|
|
|||
|
|
Debug.WriteLine($"初始化电池控件:层={layers}, 行={rows}, 列={cols}");
|
|||
|
|
|
|||
|
|
// 暂停面板布局更新
|
|||
|
|
SuspendPanelsLayout();
|
|||
|
|
ClearPanelsControls();
|
|||
|
|
|
|||
|
|
// 获取FD配置
|
|||
|
|
var fdConfig = CommonMethods.curFdConfig;
|
|||
|
|
if (fdConfig == null) return;
|
|||
|
|
|
|||
|
|
// 初始化各档位控件
|
|||
|
|
InitializeGradeControls(1, fdConfig.Grade1, panelExt_Grade1, layers, rows, cols);
|
|||
|
|
InitializeGradeControls(2, fdConfig.Grade2, panelExt_Grade2, layers, rows, cols);
|
|||
|
|
InitializeGradeControls(3, fdConfig.Grade3, panelExt_Grade3, layers, rows, cols);
|
|||
|
|
|
|||
|
|
// 恢复布局
|
|||
|
|
ResumePanelsLayout();
|
|||
|
|
|
|||
|
|
// 统计控件数量
|
|||
|
|
int totalControls = _batteryControlsByStationNo.Values.Sum(d => d.Count);
|
|||
|
|
Debug.WriteLine($"电池控件初始化完成,总控件数:{totalControls}");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
Debug.WriteLine($"控件初始化异常:{ex.Message}");
|
|||
|
|
LogHelper.Instance.WriteError($"初始化电池控件异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 窗体关闭时释放资源
|
|||
|
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnFormClosing(e);
|
|||
|
|
|
|||
|
|
//// 取消异步任务
|
|||
|
|
//_cancellationTokenSource.Cancel();
|
|||
|
|
|
|||
|
|
_cancellationTokenSource.Token.UnregisterAll(); // 新增:移除所有注册的回调
|
|||
|
|
|
|||
|
|
// 安全取消
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_cancellationTokenSource.Cancel(true); // true=允许回调抛异常但不终止程序
|
|||
|
|
}
|
|||
|
|
catch (AggregateException ex)
|
|||
|
|
{
|
|||
|
|
// 记录回调中的具体异常(关键:定位哪个回调出问题)
|
|||
|
|
foreach (var innerEx in ex.InnerExceptions)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"取消任务时回调异常:{innerEx}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"取消CancellationTokenSource异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Task.Delay(100).Wait();
|
|||
|
|
|
|||
|
|
// 释放资源
|
|||
|
|
_operationSemaphore.Dispose();
|
|||
|
|
if (_cpuUsageCounter != null)
|
|||
|
|
{
|
|||
|
|
_cpuUsageCounter.Dispose();
|
|||
|
|
_cpuUsageCounter = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lock (_batteryControlsByStationNo)
|
|||
|
|
{
|
|||
|
|
DisposeBatteryControls();
|
|||
|
|
ClearAllPanels();
|
|||
|
|
_batteryControlsByStationNo.Clear();
|
|||
|
|
_statisticsLabelsByLoc.Clear();
|
|||
|
|
_barcodeToControlMap.Clear();
|
|||
|
|
}
|
|||
|
|
_cancellationTokenSource.Dispose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 重写窗体显示方法,优化渲染
|
|||
|
|
protected override void OnShown(EventArgs e)
|
|||
|
|
{
|
|||
|
|
base.OnShown(e);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 恢复布局
|
|||
|
|
foreach (Control control in this.Controls)
|
|||
|
|
{
|
|||
|
|
control.ResumeLayout(false);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 重写CreateParams减少闪烁
|
|||
|
|
/// </summary>
|
|||
|
|
protected override CreateParams CreateParams
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
CreateParams cp = base.CreateParams;
|
|||
|
|
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
|
|||
|
|
return cp;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 电池控件管理
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化指定档位的电池控件
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="stationNo">机架号(1/2/3)</param>
|
|||
|
|
/// <param name="grade">档位名称</param>
|
|||
|
|
/// <param name="container">容器面板</param>
|
|||
|
|
/// <param name="layers">层数</param>
|
|||
|
|
/// <param name="rows">行数</param>
|
|||
|
|
/// <param name="cols">列数</param>
|
|||
|
|
private void InitializeGradeControls(int stationNo, string grade, PanelExt container, int layers, int rows, int cols)
|
|||
|
|
{
|
|||
|
|
if (stationNo < 1 || stationNo > 3 || container == null || container.HasChildren)
|
|||
|
|
{
|
|||
|
|
Debug.WriteLine($"机架号{stationNo}错误或容器非空,跳过初始化");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (string.IsNullOrEmpty(grade) || grade == "不设置" || grade == "NG" )
|
|||
|
|
{
|
|||
|
|
// 设置标签页文本
|
|||
|
|
SetTabPageText(stationNo, grade);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 设置标签页文本
|
|||
|
|
SetTabPageText(stationNo, grade);
|
|||
|
|
|
|||
|
|
// 初始化容器面板
|
|||
|
|
container.Controls.Clear();
|
|||
|
|
|
|||
|
|
//container.AutoScroll = true;
|
|||
|
|
container.BackColor = Color.FromArgb(11, 20, 36);
|
|||
|
|
container.SuspendLayout();
|
|||
|
|
container.Visible = false;
|
|||
|
|
container.Dock = DockStyle.Fill;
|
|||
|
|
container.Padding = new Padding(0); // 增加容器内边距
|
|||
|
|
|
|||
|
|
// 创建层级面板
|
|||
|
|
var layerPanel = CreateLayerPanel(stationNo, grade, layers, rows, cols);
|
|||
|
|
|
|||
|
|
// 创建统计区域
|
|||
|
|
var statsGroup = CreateStatisticsGroupBox(stationNo, grade, out var statLabels);
|
|||
|
|
_statisticsLabelsByLoc[stationNo] = statLabels;
|
|||
|
|
statsGroup.Size = new Size(layerPanel.Width, 70);
|
|||
|
|
statsGroup.Location = new System.Drawing.Point(0, layerPanel.Bottom + 5);
|
|||
|
|
//statsGroup.Dock = DockStyle.Bottom;
|
|||
|
|
|
|||
|
|
// 添加控件到容器
|
|||
|
|
container.Controls.Add(layerPanel);
|
|||
|
|
container.Controls.Add(statsGroup);
|
|||
|
|
|
|||
|
|
//container.AutoScrollMinSize = new Size(layerPanel.Width, statsGroup.Bottom);
|
|||
|
|
//container.ClientSize = new Size(layerPanel.Width + 10, statsGroup.Bottom + 10);
|
|||
|
|
// 恢复布局
|
|||
|
|
container.ResumeLayout(true);
|
|||
|
|
container.Visible = true;
|
|||
|
|
container.Invalidate();
|
|||
|
|
|
|||
|
|
|
|||
|
|
Debug.WriteLine($"机架号{stationNo}控件创建完成,总数:{layers * rows * cols}");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
Debug.WriteLine($"初始化机架号{stationNo}异常:{ex.Message}");
|
|||
|
|
LogHelper.Instance.WriteError($"初始化机架号{stationNo}电池控件异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建层级面板
|
|||
|
|
/// </summary>
|
|||
|
|
//创建分层的表格
|
|||
|
|
private TableLayoutPanel CreateLayerPanel(int stationNo, string grade, int layers, int rows, int cols)
|
|||
|
|
{
|
|||
|
|
// 布局参数
|
|||
|
|
int batterySize = 24;
|
|||
|
|
int cellPadding = 2;
|
|||
|
|
int cellSize = batterySize + cellPadding * 2;
|
|||
|
|
int rowNumColWidth = 40; // 行号列宽度
|
|||
|
|
int layerColWidth = 40; // 层数列宽度
|
|||
|
|
|
|||
|
|
int totalColWidth = rowNumColWidth + (cellSize * cols); // 电池区域总列宽
|
|||
|
|
int totalPanelWidth = layerColWidth + totalColWidth + 10; // 整体总宽度(+10px余量)
|
|||
|
|
int rowHeight = cellSize + cellPadding; // 每行高度 = 单元格高度
|
|||
|
|
int layerHeight = (rowHeight * rows) + 2; // 每层高度 = 表头 + 电池行
|
|||
|
|
int layerTotalHeight = rowHeight * rows + 6; // 每层总高度(+8px余量,确保最后一行显示)
|
|||
|
|
|
|||
|
|
|
|||
|
|
//外层TableLayoutPanel:分层(包含表头+所有层)
|
|||
|
|
TableLayoutPanel layerPanel = new TableLayoutPanel
|
|||
|
|
{
|
|||
|
|
RowCount = layers + 1,
|
|||
|
|
ColumnCount = 2,
|
|||
|
|
//Dock = DockStyle.Fill, // 顶部对齐,避免Fill压缩宽度
|
|||
|
|
BackColor = Color.Transparent,
|
|||
|
|
CellBorderStyle = TableLayoutPanelCellBorderStyle.Single, //TableLayoutPanelCellBorderStyle.None,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
|||
|
|
Size = new Size(totalPanelWidth, layerTotalHeight * layers + 35),
|
|||
|
|
//Margin = new Padding(0, 0, 0, 10),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
layerPanel.ColumnStyles.Clear();
|
|||
|
|
layerPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, layerColWidth));
|
|||
|
|
layerPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, totalColWidth));
|
|||
|
|
|
|||
|
|
// 设置行样式
|
|||
|
|
layerPanel.RowStyles.Clear();
|
|||
|
|
layerPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 25F)); //第一行 显示列号
|
|||
|
|
for (int i = 1; i <= layers; i++)
|
|||
|
|
{
|
|||
|
|
layerPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, layerTotalHeight));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========== 创建表头行 ==========
|
|||
|
|
// 表头-第一列(文本:层数/列)
|
|||
|
|
System.Windows.Forms.Label lblHeaderLayer = new System.Windows.Forms.Label
|
|||
|
|
{
|
|||
|
|
Text = "层",
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold)
|
|||
|
|
};
|
|||
|
|
layerPanel.Controls.Add(lblHeaderLayer, 0, 0);
|
|||
|
|
|
|||
|
|
// 表头-第二列(列号容器)
|
|||
|
|
TableLayoutPanel tblColHeader = new TableLayoutPanel
|
|||
|
|
{
|
|||
|
|
RowCount = 1,
|
|||
|
|
ColumnCount = cols + 1, // 行号列 + 所有电池列
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Size = new Size(totalColWidth, 25),
|
|||
|
|
//AutoSize = true,
|
|||
|
|
//AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
|||
|
|
//BackColor = Color.Transparent,
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
CellBorderStyle = TableLayoutPanelCellBorderStyle.None
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
tblColHeader.ColumnStyles.Clear();
|
|||
|
|
tblColHeader.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, rowNumColWidth));
|
|||
|
|
for (int col = 1; col <= cols; col++)
|
|||
|
|
{
|
|||
|
|
tblColHeader.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, cellSize));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
System.Windows.Forms.Label lblheaderCol = new System.Windows.Forms.Label
|
|||
|
|
{
|
|||
|
|
Text = "行/列",
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
};
|
|||
|
|
tblColHeader.Controls.Add(lblheaderCol, 0, 0);
|
|||
|
|
|
|||
|
|
for (int col = 1; col <= cols; col++)
|
|||
|
|
{
|
|||
|
|
System.Windows.Forms.Label lblCol = new System.Windows.Forms.Label
|
|||
|
|
{
|
|||
|
|
Text = col.ToString(),
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold)
|
|||
|
|
};
|
|||
|
|
tblColHeader.Controls.Add(lblCol, col, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
layerPanel.Controls.Add(tblColHeader, 1, 0);
|
|||
|
|
//CreateBatteryControls(stationNo, grade, layerPanel, layers, rows, cols);
|
|||
|
|
// 初始化控件字典
|
|||
|
|
if (!_batteryControlsByStationNo.ContainsKey(stationNo))
|
|||
|
|
{
|
|||
|
|
_batteryControlsByStationNo[stationNo] = new Dictionary<string, BatteryControl>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========== 遍历每层,创建电池控件 ==========
|
|||
|
|
for (int layer = 1; layer <= layers; layer++)
|
|||
|
|
{
|
|||
|
|
// 层数标签(第一列)
|
|||
|
|
System.Windows.Forms.Label lblLayer = new System.Windows.Forms.Label
|
|||
|
|
{
|
|||
|
|
Text = $"第{layer}层",
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Padding = new Padding(0)
|
|||
|
|
};
|
|||
|
|
layerPanel.Controls.Add(lblLayer, 0, layer);
|
|||
|
|
|
|||
|
|
System.Windows.Forms.Panel pnlBatteryContainer = new System.Windows.Forms.Panel
|
|||
|
|
{
|
|||
|
|
//Dock = DockStyle.Fill,
|
|||
|
|
//BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
//BorderStyle = BorderStyle.FixedSingle, // 层边框
|
|||
|
|
//Margin = new Padding(2), // 层之间的间距
|
|||
|
|
//Padding = new Padding(3)
|
|||
|
|
Size = new Size(totalColWidth, layerTotalHeight), // 固定尺寸,和层高度一致
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
//BorderStyle = BorderStyle.FixedSingle, // 层边框
|
|||
|
|
Margin = new Padding(3), // 层之间间距(统一)
|
|||
|
|
Padding = new Padding(1)
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 内层TableLayoutPanel:当前层的行和列
|
|||
|
|
TableLayoutPanel tblBattery = new TableLayoutPanel
|
|||
|
|
{
|
|||
|
|
//RowCount = totalRows,
|
|||
|
|
//ColumnCount = totalCols + 1,
|
|||
|
|
//Dock = DockStyle.Fill, // 填充容器
|
|||
|
|
//AutoSize = true,
|
|||
|
|
//AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
|||
|
|
//BackColor = Color.Transparent,
|
|||
|
|
//CellBorderStyle = TableLayoutPanelCellBorderStyle.None,
|
|||
|
|
//Margin = new Padding(0),
|
|||
|
|
//Padding = new Padding(5)
|
|||
|
|
RowCount = rows,
|
|||
|
|
ColumnCount = cols + 1,
|
|||
|
|
Size = new Size(totalColWidth, layerTotalHeight - 2),
|
|||
|
|
//Dock = DockStyle.Fill,
|
|||
|
|
BackColor = Color.Transparent,
|
|||
|
|
CellBorderStyle = TableLayoutPanelCellBorderStyle.None,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Padding = new Padding(0)
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
tblBattery.RowStyles.Clear();
|
|||
|
|
for (int r = 0; r < rows; r++)
|
|||
|
|
{
|
|||
|
|
tblBattery.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tblBattery.ColumnStyles.Clear();
|
|||
|
|
tblBattery.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, rowNumColWidth));
|
|||
|
|
for (int c = 1; c <= cols; c++)
|
|||
|
|
{
|
|||
|
|
tblBattery.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, cellSize));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历当前层的所有行和列,生成电池控件
|
|||
|
|
for (int row = 1; row <= rows; row++)
|
|||
|
|
{
|
|||
|
|
System.Windows.Forms.Label lblrow = new System.Windows.Forms.Label
|
|||
|
|
{
|
|||
|
|
Text = row.ToString(),
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold)
|
|||
|
|
};
|
|||
|
|
tblBattery.Controls.Add(lblrow, 0, row - 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int row = 1; row <= rows; row++)
|
|||
|
|
{
|
|||
|
|
for (int col = 1; col <= cols; col++)
|
|||
|
|
{
|
|||
|
|
BatteryControl battery = new BatteryControl(grade, layer, row, col, "")
|
|||
|
|
{
|
|||
|
|
Name = $"BatteryControl_{stationNo}_{location}",
|
|||
|
|
Size = new Size(batterySize, batterySize),
|
|||
|
|
Margin = new Padding(cellPadding),
|
|||
|
|
Anchor = AnchorStyles.None,
|
|||
|
|
//Dock = DockStyle.None,
|
|||
|
|
Dock = DockStyle.None,
|
|||
|
|
Tag = $"{stationNo}-{location}",
|
|||
|
|
Style = SeeSharpTools.JY.GUI.LED.LedStyle.Circular
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 绑定点击事件
|
|||
|
|
battery.OnBatteryClicked += OnBatteryControlClicked;
|
|||
|
|
|
|||
|
|
// 添加到行列表格
|
|||
|
|
tblBattery.Controls.Add(battery, col, row - 1);
|
|||
|
|
|
|||
|
|
// 加入全局管理字典
|
|||
|
|
string controlKey = $"{layer}-{row}-{col}";
|
|||
|
|
_batteryControlsByStationNo[stationNo][controlKey] = battery;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pnlBatteryContainer.Controls.Add(tblBattery);
|
|||
|
|
layerPanel.Controls.Add(pnlBatteryContainer, 1, layer);
|
|||
|
|
}
|
|||
|
|
return layerPanel;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//创建一个整体的表格
|
|||
|
|
//private TableLayoutPanel CreateLayerPanel(int stationNo, string grade, int layers, int rows, int cols)
|
|||
|
|
//{
|
|||
|
|
// // 布局参数
|
|||
|
|
// int batterySize = 24;
|
|||
|
|
// int cellPadding = 2;
|
|||
|
|
// int cellSize = batterySize + cellPadding * 2;
|
|||
|
|
// int layerNumWidth = 50;
|
|||
|
|
// int totalPanelWidth = layerNumWidth * 2 + (cellSize * cols) + 18;
|
|||
|
|
// int totalPanelHeight = cellSize * rows * layers + 50; // 每层总高度(+8px余量,确保最后一行显示)
|
|||
|
|
|
|||
|
|
// // 创建外层面板
|
|||
|
|
// var layerPanel = new TableLayoutPanel
|
|||
|
|
// {
|
|||
|
|
// RowCount = layers * rows + 1,
|
|||
|
|
// ColumnCount = cols + 2,
|
|||
|
|
// BackColor = Color.Transparent,
|
|||
|
|
// CellBorderStyle = TableLayoutPanelCellBorderStyle.Single,
|
|||
|
|
// Margin = new Padding(0),
|
|||
|
|
// Size = new Size(totalPanelWidth, totalPanelHeight),
|
|||
|
|
// Padding = new Padding(0)
|
|||
|
|
// };
|
|||
|
|
|
|||
|
|
// // 设置列样式
|
|||
|
|
// layerPanel.ColumnStyles.Clear();
|
|||
|
|
// for (int i = 0; i < layerPanel.ColumnCount; i++)
|
|||
|
|
// {
|
|||
|
|
// if (i < 2)
|
|||
|
|
// layerPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, layerNumWidth));
|
|||
|
|
// else
|
|||
|
|
// layerPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, cellSize));
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
|
|||
|
|
// // 设置行样式
|
|||
|
|
// layerPanel.RowStyles.Clear();
|
|||
|
|
// for (int i = 0; i < layerPanel.RowCount; i++)
|
|||
|
|
// {
|
|||
|
|
// layerPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, cellSize));
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// // 添加表头
|
|||
|
|
// //AddPanelHeaders(layerPanel, cols, layerNumWidth, cellSize);
|
|||
|
|
// // 层数标签
|
|||
|
|
// var layerHeader = new Label
|
|||
|
|
// {
|
|||
|
|
// Text = "层",
|
|||
|
|
// TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
// ForeColor = Color.White,
|
|||
|
|
// BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
// Dock = DockStyle.Fill,
|
|||
|
|
// Font = new Font("Microsoft YaHei", 9, FontStyle.Bold)
|
|||
|
|
// };
|
|||
|
|
// layerPanel.Controls.Add(layerHeader, 0, 0);
|
|||
|
|
|
|||
|
|
// // 行/列表头
|
|||
|
|
// var rowColHeader = new Label
|
|||
|
|
// {
|
|||
|
|
// Text = "行/列",
|
|||
|
|
// TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
// ForeColor = Color.White,
|
|||
|
|
// Dock = DockStyle.Fill,
|
|||
|
|
// Margin = new Padding(0),
|
|||
|
|
// Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
// BackColor = Color.FromArgb(18, 30, 50)
|
|||
|
|
// };
|
|||
|
|
// layerPanel.Controls.Add(rowColHeader, 1, 0);
|
|||
|
|
|
|||
|
|
// // 列号标签
|
|||
|
|
// for (int col = 1; col <= cols; col++)
|
|||
|
|
// {
|
|||
|
|
// var colLabel = new Label
|
|||
|
|
// {
|
|||
|
|
// Text = col.ToString(),
|
|||
|
|
// TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
// ForeColor = Color.White,
|
|||
|
|
// Dock = DockStyle.Fill,
|
|||
|
|
// Margin = new Padding(0),
|
|||
|
|
// Padding = new Padding(0),
|
|||
|
|
// Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
// BackColor = Color.FromArgb(18, 30, 50)
|
|||
|
|
// };
|
|||
|
|
// layerPanel.Controls.Add(colLabel, col + 1, 0);
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// // 创建电池控件
|
|||
|
|
// //CreateBatteryControls(stationNo, grade, layerPanel, layers, rows, cols);
|
|||
|
|
// // 初始化控件字典
|
|||
|
|
// if (!_batteryControlsByStationNo.ContainsKey(stationNo))
|
|||
|
|
// {
|
|||
|
|
// _batteryControlsByStationNo[stationNo] = new Dictionary<string, BatteryControl>();
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// for (int layer = 0; layer < layers; layer++)
|
|||
|
|
// {
|
|||
|
|
// // 添加层数标签
|
|||
|
|
// var layerLabel = new Label
|
|||
|
|
// {
|
|||
|
|
// Text = $"第{layer + 1}层",
|
|||
|
|
// TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
// ForeColor = Color.White,
|
|||
|
|
// Dock = DockStyle.Fill,
|
|||
|
|
// BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
// Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
// Margin = new Padding(0)
|
|||
|
|
// };
|
|||
|
|
// layerPanel.Controls.Add(layerLabel, 0, layer * rows + 1);
|
|||
|
|
// layerPanel.SetRowSpan(layerLabel, rows);
|
|||
|
|
|
|||
|
|
// // 添加行号标签
|
|||
|
|
// for (int row = 1; row <= rows; row++)
|
|||
|
|
// {
|
|||
|
|
// var rowLabel = new Label
|
|||
|
|
// {
|
|||
|
|
// Text = row.ToString(),
|
|||
|
|
// TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
// ForeColor = Color.White,
|
|||
|
|
// Dock = DockStyle.Fill,
|
|||
|
|
// Margin = new Padding(0),
|
|||
|
|
// Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
// BackColor = Color.FromArgb(18, 30, 50)
|
|||
|
|
// };
|
|||
|
|
// layerPanel.Controls.Add(rowLabel, 1, layer * rows + row);
|
|||
|
|
|
|||
|
|
// for (int col = 1; col <= cols; col++)
|
|||
|
|
// {
|
|||
|
|
// string location = $"{layer}-{row}-{col}";
|
|||
|
|
// // 创建电池控件
|
|||
|
|
// var batteryControl = new BatteryControl(grade, layer, row, col, "", JinYuan.ControlLib.BatteryStatus.Init)
|
|||
|
|
// {
|
|||
|
|
// Name = $"BatteryControl_{stationNo}_{location}",
|
|||
|
|
// Size = new Size(batterySize, batterySize),
|
|||
|
|
// Margin = new Padding(cellPadding),
|
|||
|
|
// Anchor = AnchorStyles.None,
|
|||
|
|
// //Dock = DockStyle.None,
|
|||
|
|
// Dock = DockStyle.Fill,
|
|||
|
|
// Tag = $"{stationNo}-{location}",
|
|||
|
|
// Style = SeeSharpTools.JY.GUI.LED.LedStyle.Circular
|
|||
|
|
// };
|
|||
|
|
|
|||
|
|
// // 绑定点击事件
|
|||
|
|
// batteryControl.OnBatteryClicked += OnBatteryControlClicked;
|
|||
|
|
|
|||
|
|
// // 添加到面板
|
|||
|
|
// layerPanel.Controls.Add(batteryControl, col + 1, row + layer * rows);
|
|||
|
|
|
|||
|
|
// // 加入管理字典
|
|||
|
|
// string controlKey = $"{layer}-{row}-{col}";
|
|||
|
|
// _batteryControlsByStationNo[stationNo][controlKey] = batteryControl;
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// // 创建具体电池控件
|
|||
|
|
// //AddBatteryControlCells(stationNo, grade, layerPanel, layer, rows, cols, batterySize, cellPadding);
|
|||
|
|
|
|||
|
|
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// return layerPanel;
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建电池控件
|
|||
|
|
/// </summary>
|
|||
|
|
private void CreateBatteryControls(int stationNo, string grade, TableLayoutPanel layerPanel, int layers, int rows, int cols)
|
|||
|
|
{
|
|||
|
|
// 初始化控件字典
|
|||
|
|
if (!_batteryControlsByStationNo.ContainsKey(stationNo))
|
|||
|
|
{
|
|||
|
|
_batteryControlsByStationNo[stationNo] = new Dictionary<string, BatteryControl>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int cellPadding = 1;
|
|||
|
|
int batterySize = 24;
|
|||
|
|
int cellSize = batterySize + cellPadding * 2;
|
|||
|
|
//int rowNumWidth = 40;
|
|||
|
|
|
|||
|
|
for (int layer = 0; layer < layers; layer++)
|
|||
|
|
{
|
|||
|
|
// 添加层数标签
|
|||
|
|
var layerLabel = new Label
|
|||
|
|
{
|
|||
|
|
Text = $"第{layer + 1}层",
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
Margin = new Padding(0)
|
|||
|
|
};
|
|||
|
|
layerPanel.Controls.Add(layerLabel, 0, layer * rows + 1);
|
|||
|
|
layerPanel.SetRowSpan(layerLabel, rows);
|
|||
|
|
|
|||
|
|
// 添加行号标签
|
|||
|
|
AddRowNumberLabels(layerPanel, layer, rows);
|
|||
|
|
|
|||
|
|
// 创建具体电池控件
|
|||
|
|
AddBatteryControlCells(stationNo, grade, layerPanel, layer, rows, cols, batterySize, cellPadding);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 添加电池控件单元格
|
|||
|
|
/// </summary>
|
|||
|
|
private void AddBatteryControlCells(int stationNo, string grade, TableLayoutPanel layerPanel,
|
|||
|
|
int layer, int rows, int cols, int batterySize, int cellPadding)
|
|||
|
|
{
|
|||
|
|
for (int row = 1; row <= rows; row++)
|
|||
|
|
{
|
|||
|
|
for (int col = 1; col <= cols; col++)
|
|||
|
|
{
|
|||
|
|
string location = $"{layer}-{row}-{col}";
|
|||
|
|
// 创建电池控件
|
|||
|
|
var batteryControl = new BatteryControl(grade, layer, row, col, "", JinYuan.ControlLib.BatteryStatus.Init)
|
|||
|
|
{
|
|||
|
|
Name = $"BatteryControl_{stationNo}_{location}",
|
|||
|
|
Size = new Size(batterySize, batterySize),
|
|||
|
|
Margin = new Padding(cellPadding),
|
|||
|
|
Anchor = AnchorStyles.None,
|
|||
|
|
//Dock = DockStyle.None,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Tag = $"{stationNo}-{location}",
|
|||
|
|
Style = SeeSharpTools.JY.GUI.LED.LedStyle.Circular
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 绑定点击事件
|
|||
|
|
batteryControl.OnBatteryClicked += OnBatteryControlClicked;
|
|||
|
|
|
|||
|
|
// 添加到面板
|
|||
|
|
layerPanel.Controls.Add(batteryControl, col+1, row + layer * rows);
|
|||
|
|
|
|||
|
|
// 加入管理字典
|
|||
|
|
string controlKey = $"{layer}-{row}-{col}";
|
|||
|
|
_batteryControlsByStationNo[stationNo][controlKey] = batteryControl;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建统计信息GroupBox
|
|||
|
|
/// </summary>
|
|||
|
|
private GroupBoxEX CreateStatisticsGroupBox(int stationNo, string grade, out List<Label> statLabels)
|
|||
|
|
{
|
|||
|
|
statLabels = new List<Label>();
|
|||
|
|
|
|||
|
|
var groupBox = new GroupBoxEX
|
|||
|
|
{
|
|||
|
|
Height = 70,
|
|||
|
|
Margin = new Padding(0, 5, 0, 0),
|
|||
|
|
Font = new Font("微软雅黑", 9F),
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
BackColor = Color.FromArgb(14, 23, 38),
|
|||
|
|
Text = $"叠盘位{stationNo}-{grade}等级统计",
|
|||
|
|
AutoSize = true, // 自动适配内容
|
|||
|
|
AutoSizeMode = AutoSizeMode.GrowOnly
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 创建表格布局
|
|||
|
|
var tableLayout = new TableLayoutPanel
|
|||
|
|
{
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
ColumnCount = 2,
|
|||
|
|
RowCount = 2,
|
|||
|
|
CellBorderStyle = TableLayoutPanelCellBorderStyle.None,
|
|||
|
|
BackColor = Color.Transparent,
|
|||
|
|
Margin = new Padding(2),
|
|||
|
|
AutoSize = true
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 设置行列样式
|
|||
|
|
tableLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
|||
|
|
tableLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
|||
|
|
tableLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
|
|||
|
|
tableLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
|
|||
|
|
|
|||
|
|
// 添加统计标签
|
|||
|
|
for (int i = 0; i < _statusDisplayNames.Length; i++)
|
|||
|
|
{
|
|||
|
|
var label = new Label
|
|||
|
|
{
|
|||
|
|
Text = $"{_statusDisplayNames[i]}:0",
|
|||
|
|
AutoSize = true,
|
|||
|
|
TextAlign = ContentAlignment.MiddleLeft,
|
|||
|
|
Font = new Font("微软雅黑", 9),
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Margin = new Padding(5, 1, 0, 1)
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
int row = i / 2;
|
|||
|
|
int col = i % 2;
|
|||
|
|
tableLayout.Controls.Add(label, col, row);
|
|||
|
|
statLabels.Add(label);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
groupBox.Controls.Add(tableLayout);
|
|||
|
|
return groupBox;
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 数据绑定与更新相关方法
|
|||
|
|
/// <summary>
|
|||
|
|
/// 批量绑定电池数据
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="stationNo">机架号</param>
|
|||
|
|
/// <param name="batteryList">电池数据列表</param>
|
|||
|
|
public void BindBatteryData(int stationNo, List<BatteryEntity> batteryList)
|
|||
|
|
{
|
|||
|
|
if (batteryList == null || batteryList.Count == 0 || stationNo < 1 || stationNo > 3) return;
|
|||
|
|
|
|||
|
|
BeginInvoke(() =>
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 暂停列表事件触发
|
|||
|
|
_allBatteryRecords.RaiseListChangedEvents = false;
|
|||
|
|
_ngBatteryRecords.RaiseListChangedEvents = false;
|
|||
|
|
|
|||
|
|
foreach (var battery in batteryList)
|
|||
|
|
{
|
|||
|
|
_allBatteryRecords.Add(battery);
|
|||
|
|
|
|||
|
|
var (layer, row, col) = SplitLocation(battery.location);
|
|||
|
|
string controlKey = $"{layer}-{row}-{col}";
|
|||
|
|
|
|||
|
|
// 更新控件状态
|
|||
|
|
if (_batteryControlsByStationNo.TryGetValue(stationNo, out var locControls) &&
|
|||
|
|
locControls.TryGetValue(controlKey, out var batteryControl) &&
|
|||
|
|
IsControlValid(batteryControl))
|
|||
|
|
{
|
|||
|
|
var status = battery.packResult ? JinYuan.ControlLib.BatteryStatus.Normal : JinYuan.ControlLib.BatteryStatus.PackNG;
|
|||
|
|
batteryControl.BindData(battery.identification, status, battery.containerCode);
|
|||
|
|
//RefreshControl(batteryControl);
|
|||
|
|
|
|||
|
|
// 更新条码映射
|
|||
|
|
if (!string.IsNullOrEmpty(battery.identification))
|
|||
|
|
{
|
|||
|
|
_barcodeToControlMap[battery.identification] = (stationNo, controlKey);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加到NG列表(去重)
|
|||
|
|
if (!battery.packResult && !_ngBatteryRecords.Any(b => b.identification == battery.identification))
|
|||
|
|
{
|
|||
|
|
_ngBatteryRecords.Add(battery);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复事件触发并刷新
|
|||
|
|
_allBatteryRecords.RaiseListChangedEvents = true;
|
|||
|
|
_ngBatteryRecords.RaiseListChangedEvents = true;
|
|||
|
|
_allBatteryRecords.ResetBindings();
|
|||
|
|
_ngBatteryRecords.ResetBindings();
|
|||
|
|
|
|||
|
|
// 更新统计信息
|
|||
|
|
UpdateBatteryStatistics(stationNo);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"批量绑定电池数据异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 单个电池数据绑定
|
|||
|
|
/// </summary>
|
|||
|
|
//public void ModifyBindBatteryData(int stationNo, BatteryControl batteryControl,
|
|||
|
|
// string barcode, JinYuan.ControlLib.BatteryStatus status, string containerCode = "")
|
|||
|
|
//{
|
|||
|
|
// if (stationNo < 1 || stationNo > 3) return;
|
|||
|
|
|
|||
|
|
// if (IsControlValid(batteryControl))
|
|||
|
|
// {
|
|||
|
|
// Invoke(() =>
|
|||
|
|
// {
|
|||
|
|
// batteryControl.BindData(barcode, status, containerCode);
|
|||
|
|
// RefreshControlPartial(batteryControl);
|
|||
|
|
|
|||
|
|
// // 更新条码映射
|
|||
|
|
// if (!string.IsNullOrEmpty(barcode))
|
|||
|
|
// {
|
|||
|
|
// string controlKey = $"{batteryControl.Layer}-{batteryControl.Row}-{batteryControl.Col}";
|
|||
|
|
// _barcodeToControlMap[barcode] = (stationNo, controlKey);
|
|||
|
|
// }
|
|||
|
|
// });
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
public void ModifyBindBatteryData(int stationNo, BatteryControl batteryControl,
|
|||
|
|
string barcode, JinYuan.ControlLib.BatteryStatus status, string containerCode = "")
|
|||
|
|
{
|
|||
|
|
if (stationNo < 1 || stationNo > 3 || batteryControl == null) return;
|
|||
|
|
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
if (!batteryControl.IsDisposed)
|
|||
|
|
{
|
|||
|
|
batteryControl.BindData(barcode, status, containerCode);
|
|||
|
|
RefreshControlPartial(batteryControl);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!string.IsNullOrEmpty(barcode))
|
|||
|
|
{
|
|||
|
|
string key = $"{batteryControl.Layer}-{batteryControl.Row}-{batteryControl.Col}";
|
|||
|
|
_barcodeToControlMap[barcode] = (stationNo, key);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新电池统计信息
|
|||
|
|
/// </summary>
|
|||
|
|
public void UpdateBatteryStatistics(int stationNo)
|
|||
|
|
{
|
|||
|
|
if (!Visible || IsDisposed || stationNo < 1 || stationNo > 3) return;
|
|||
|
|
|
|||
|
|
// 防抖:200ms内不重复更新
|
|||
|
|
if (DateTime.Now - _lastStatisticsUpdateTime < TimeSpan.FromMilliseconds(200))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
_lastStatisticsUpdateTime = DateTime.Now;
|
|||
|
|
|
|||
|
|
SafeUI(() => UpdateStatisticsInternal(stationNo));
|
|||
|
|
//if (InvokeRequired)
|
|||
|
|
//{
|
|||
|
|
// BeginInvoke(() => UpdateStatisticsInternal(stationNo));
|
|||
|
|
//}
|
|||
|
|
//else
|
|||
|
|
//{
|
|||
|
|
// UpdateStatisticsInternal(stationNo);
|
|||
|
|
//}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 统计信息更新内部逻辑
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdateStatisticsInternal(int stationNo)
|
|||
|
|
{
|
|||
|
|
if (!_statisticsLabelsByLoc.TryGetValue(stationNo, out var labels) || labels.Count < 4)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 初始化状态计数
|
|||
|
|
var statusCounts = new Dictionary<JinYuan.ControlLib.BatteryStatus, int>
|
|||
|
|
{
|
|||
|
|
{ JinYuan.ControlLib.BatteryStatus.Replacing, 0 },
|
|||
|
|
{ JinYuan.ControlLib.BatteryStatus.PackNG, 0 },
|
|||
|
|
{ JinYuan.ControlLib.BatteryStatus.Normal, 0 },
|
|||
|
|
{ JinYuan.ControlLib.BatteryStatus.Init, 0 }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
lock (_batteryControlsByStationNo)
|
|||
|
|
{
|
|||
|
|
if (_batteryControlsByStationNo.TryGetValue(stationNo, out var controls))
|
|||
|
|
{
|
|||
|
|
foreach (var control in controls.Values)
|
|||
|
|
{
|
|||
|
|
if (IsControlValid(control) && statusCounts.ContainsKey(control.Status))
|
|||
|
|
{
|
|||
|
|
statusCounts[control.Status]++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
labels[0].Text = $"{_statusDisplayNames[0]}:{statusCounts[JinYuan.ControlLib.BatteryStatus.Replacing]}";
|
|||
|
|
labels[1].Text = $"{_statusDisplayNames[1]}:{statusCounts[JinYuan.ControlLib.BatteryStatus.PackNG]}";
|
|||
|
|
labels[2].Text = $"{_statusDisplayNames[2]}:{statusCounts[JinYuan.ControlLib.BatteryStatus.Normal]}";
|
|||
|
|
labels[3].Text = $"{_statusDisplayNames[3]}:{statusCounts[JinYuan.ControlLib.BatteryStatus.Init]}";
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清理电池数据
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="stationNo">档位(0=全部)</param>
|
|||
|
|
/// <param name="containerCode">容器码</param>
|
|||
|
|
//public void ClearBatteryData(int stationNo = 0, string containerCode = null)
|
|||
|
|
//{
|
|||
|
|
// BeginInvoke(() =>
|
|||
|
|
// {
|
|||
|
|
// try
|
|||
|
|
// {
|
|||
|
|
// var controlsToClear = new List<BatteryControl>();
|
|||
|
|
|
|||
|
|
// // 筛选要清理的控件
|
|||
|
|
// if (stationNo == 0 && string.IsNullOrEmpty(containerCode))
|
|||
|
|
// {
|
|||
|
|
// // 清空所有
|
|||
|
|
// foreach (var locControls in _batteryControlsByStationNo.Values)
|
|||
|
|
// {
|
|||
|
|
// controlsToClear.AddRange(locControls.Values);
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
// else if (stationNo > 0 && stationNo <= 3)
|
|||
|
|
// {
|
|||
|
|
// // 清空指定档位
|
|||
|
|
// if (_batteryControlsByStationNo.TryGetValue(stationNo, out var locControls))
|
|||
|
|
// {
|
|||
|
|
// controlsToClear = string.IsNullOrEmpty(containerCode)
|
|||
|
|
// ? locControls.Values.ToList()
|
|||
|
|
// : locControls.Values.Where(b => b.Container == containerCode).ToList();
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// // 暂停DataGridView布局
|
|||
|
|
// dgv_ngRsn?.SuspendLayout();
|
|||
|
|
|
|||
|
|
// // 清理控件和数据
|
|||
|
|
// foreach (var battery in controlsToClear)
|
|||
|
|
// {
|
|||
|
|
// // 从NG列表移除
|
|||
|
|
// RemoveBatteryFromNgList(battery.Barcode);
|
|||
|
|
|
|||
|
|
// // 清空控件
|
|||
|
|
// battery.Clear();
|
|||
|
|
// RefreshControlPartial(battery);
|
|||
|
|
|
|||
|
|
// // 从条码映射移除
|
|||
|
|
// if (!string.IsNullOrEmpty(battery.Barcode) && _barcodeToControlMap.ContainsKey(battery.Barcode))
|
|||
|
|
// {
|
|||
|
|
// _barcodeToControlMap.Remove(battery.Barcode);
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// // 恢复布局
|
|||
|
|
// if (dgv_ngRsn != null && dgv_ngRsn.IsHandleCreated)
|
|||
|
|
// {
|
|||
|
|
// dgv_ngRsn.ResumeLayout(false);
|
|||
|
|
// dgv_ngRsn.Invalidate();
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
// catch (Exception ex)
|
|||
|
|
// {
|
|||
|
|
// LogHelper.Instance.WriteError($"清理电池数据异常:{ex}", "BatteryStatusLog");
|
|||
|
|
// }
|
|||
|
|
// });
|
|||
|
|
//}
|
|||
|
|
public void ClearBatteryData(int stationNo = 0, string containerCode = null)
|
|||
|
|
{
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
List<BatteryControl> clearList = new List<BatteryControl>();
|
|||
|
|
|
|||
|
|
if (stationNo == 0 && string.IsNullOrEmpty(containerCode))
|
|||
|
|
{
|
|||
|
|
foreach (var dic in _batteryControlsByStationNo.Values)
|
|||
|
|
clearList.AddRange(dic.Values);
|
|||
|
|
}
|
|||
|
|
else if (stationNo >= 1 && stationNo <= 3)
|
|||
|
|
{
|
|||
|
|
if (_batteryControlsByStationNo.TryGetValue(stationNo, out var dic))
|
|||
|
|
{
|
|||
|
|
clearList = string.IsNullOrEmpty(containerCode)
|
|||
|
|
? dic.Values.ToList()
|
|||
|
|
: dic.Values.Where(b => b.Container == containerCode).ToList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dgv_ngRsn?.SuspendLayout();
|
|||
|
|
|
|||
|
|
foreach (var b in clearList)
|
|||
|
|
{
|
|||
|
|
if (b == null || b.IsDisposed) continue;
|
|||
|
|
|
|||
|
|
RemoveBatteryFromNgList(b.Barcode);
|
|||
|
|
b.Clear();
|
|||
|
|
RefreshControlPartial(b);
|
|||
|
|
|
|||
|
|
if (!string.IsNullOrEmpty(b.Barcode))
|
|||
|
|
_barcodeToControlMap.Remove(b.Barcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dgv_ngRsn?.ResumeLayout(true);
|
|||
|
|
dgv_ngRsn?.Invalidate();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"清理电池数据异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 事件处理方法
|
|||
|
|
/// <summary>
|
|||
|
|
/// 电池控件点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void OnBatteryControlClicked(BatteryControl battery)
|
|||
|
|
{
|
|||
|
|
// 防抖
|
|||
|
|
if (DateTime.Now - _lastBatteryClickTime < TimeSpan.FromMilliseconds(200))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
_lastBatteryClickTime = DateTime.Now;
|
|||
|
|
|
|||
|
|
// 高亮表格行
|
|||
|
|
if (!string.IsNullOrEmpty(battery.Barcode))
|
|||
|
|
{
|
|||
|
|
HighlightGridViewRow(battery.Barcode);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新UI
|
|||
|
|
UpdateBatteryInfoDisplay(battery);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 条码搜索按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void btn_Search_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
string barcode = FilterBarcodeChars(txtBox_Barcode?.Text?.Trim() ?? "");
|
|||
|
|
if (string.IsNullOrEmpty(barcode))
|
|||
|
|
{
|
|||
|
|
txtBox_Barcode?.Focus();
|
|||
|
|
MessageBox.Show("请输入待查询的电芯条码!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找电池控件
|
|||
|
|
var batteryControl = FindBatteryControlByBarcode(barcode);
|
|||
|
|
if (batteryControl != null)
|
|||
|
|
{
|
|||
|
|
// 更新显示信息
|
|||
|
|
UpdateBatteryInfoDisplay(batteryControl);
|
|||
|
|
|
|||
|
|
// 闪烁提示
|
|||
|
|
batteryControl.BlinkOn = true;
|
|||
|
|
batteryControl.BlinkInterval = 500;
|
|||
|
|
|
|||
|
|
// 2秒后停止闪烁
|
|||
|
|
Task.Delay(2000, _cancellationTokenSource.Token).ContinueWith(t =>
|
|||
|
|
{
|
|||
|
|
if (t.IsCanceled || t.IsFaulted) return;
|
|||
|
|
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 校验控件有效性
|
|||
|
|
if (batteryControl != null && !batteryControl.IsDisposed)
|
|||
|
|
{
|
|||
|
|
batteryControl.BlinkOn = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (ObjectDisposedException)
|
|||
|
|
{
|
|||
|
|
// 预期异常:控件已释放,无需处理
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"停止电池闪烁异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}, true);
|
|||
|
|
}, _cancellationTokenSource.Token);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
MessageBox.Show($"未找到条码为{barcode}的电池!");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 条码替换按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private async void btn_SubBarcode_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!await _operationSemaphore.WaitAsync(0, _cancellationTokenSource.Token).ConfigureAwait(false))
|
|||
|
|
{
|
|||
|
|
MessageBox.Show("当前有操作正在执行,请稍候!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 获取输入
|
|||
|
|
string oldBarcode = FilterBarcodeChars(txtBox_Barcode?.Text?.Trim() ?? "");
|
|||
|
|
string newBarcode = FilterBarcodeChars(txtBox_SubBarcode?.Text?.Trim() ?? "");
|
|||
|
|
string containerCode = FilterBarcodeChars(txtBox_Carton?.Text?.Trim() ?? "");
|
|||
|
|
|
|||
|
|
// 输入验证
|
|||
|
|
if (!ValidateBarcodeInput(oldBarcode, newBarcode))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//// 查找原条码位置
|
|||
|
|
int stationNo = 0;
|
|||
|
|
if (_barcodeToControlMap.TryGetValue(oldBarcode, out var mapValue))
|
|||
|
|
{
|
|||
|
|
stationNo = mapValue.stationNo;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 遍历查找
|
|||
|
|
stationNo = FindBatteryLocByBarcode(oldBarcode);
|
|||
|
|
if (stationNo == 0)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show($"电池【{oldBarcode}】不在当前叠盘位内!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找电池控件
|
|||
|
|
var batteryControl = FindBatteryControlByBarcode(oldBarcode);
|
|||
|
|
if (batteryControl == null)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show($"电池【{oldBarcode}】不在当前叠盘位内!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
//数据库验证
|
|||
|
|
var oldBatteryList = await Task.Run(() =>
|
|||
|
|
CommonMethods.db.QueryWhereList<BatteryEntity>(it => it.identification == oldBarcode)).ConfigureAwait(false);
|
|||
|
|
if (oldBatteryList == null || oldBatteryList.Count == 0)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"本地数据库无该电池条码{oldBarcode}", "SysErrorLog");
|
|||
|
|
MessageBox.Show("电池未绑定过,不能替换");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查新条码是否存在
|
|||
|
|
if (await IsBarcodeExistsAsync(newBarcode).ConfigureAwait(false))
|
|||
|
|
{
|
|||
|
|
MessageBox.Show($"新条码【{newBarcode}】已存在,无法替换!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
BatteryEntity battery = new BatteryEntity()
|
|||
|
|
{
|
|||
|
|
identification = batteryControl.Barcode,
|
|||
|
|
location = $"{batteryControl.Layer}-{batteryControl.Row}-{batteryControl.Col}",
|
|||
|
|
grade = batteryControl.Grade,
|
|||
|
|
containerCode = batteryControl.Container,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 确认替换
|
|||
|
|
if (MessageBox.Show($"确认将原条码【{oldBarcode}】替换为新条码【{newBarcode}】吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行数据库更新
|
|||
|
|
bool replaceSuccess = true; //await UpdateBarcodeInDatabase(oldBatteryList, newBarcode);
|
|||
|
|
|
|||
|
|
// 更新UI
|
|||
|
|
if (replaceSuccess)
|
|||
|
|
{
|
|||
|
|
//var (layer, row, col) = SplitLocation(location);
|
|||
|
|
|
|||
|
|
// 更新控件状态
|
|||
|
|
ModifyBindBatteryData(stationNo, batteryControl, oldBarcode, JinYuan.ControlLib.BatteryStatus.Replacing, containerCode);
|
|||
|
|
ModifyBindBatteryData(stationNo, batteryControl, newBarcode, JinYuan.ControlLib.BatteryStatus.Normal, containerCode);
|
|||
|
|
|
|||
|
|
// 记录替换信息
|
|||
|
|
AddReplaceRecord(oldBarcode, newBarcode, battery, "替换成功");
|
|||
|
|
|
|||
|
|
// 从NG列表移除原条码
|
|||
|
|
RemoveBatteryFromNgList(oldBarcode);
|
|||
|
|
|
|||
|
|
|
|||
|
|
MessageBox.Show("条码替换成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|||
|
|
|
|||
|
|
// 清空输入框
|
|||
|
|
ClearInputTextBoxes();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
AddReplaceRecord(oldBarcode, newBarcode, battery, "数据库更新失败");
|
|||
|
|
MessageBox.Show("条码替换失败,请检查条码是否存在!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"条码替换异常:{ex}", "SysErrorLog");
|
|||
|
|
MessageBox.Show($"替换失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
_operationSemaphore.Release();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清空按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private async void btn_clear_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!await _operationSemaphore.WaitAsync(0, _cancellationTokenSource.Token).ConfigureAwait(false)) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
ClearInputTextBoxes();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"清空输入框异常:{ex}", "SysErrorLog");
|
|||
|
|
MessageBox.Show($"清空失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
_operationSemaphore.Release();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 强制放行按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private async void btn_ForceOut_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!await _operationSemaphore.WaitAsync(0, _cancellationTokenSource.Token).ConfigureAwait(false))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 验证配置
|
|||
|
|
if (!ValidatePlcConfiguration())
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取容器码
|
|||
|
|
string containerCode = GetSelectedContainerCode();
|
|||
|
|
if (string.IsNullOrEmpty(containerCode))
|
|||
|
|
{
|
|||
|
|
MessageBox.Show("未获取到容器码,不能继续执行");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认操作
|
|||
|
|
if (MessageBox.Show("确认执行强制放行吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 执行PLC操作
|
|||
|
|
await Task.Run(() =>
|
|||
|
|
{
|
|||
|
|
if (CommonMethods.plcDevices.Count() > 0)
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < CommonMethods.plcDevices.Count(); i++)
|
|||
|
|
{
|
|||
|
|
if (CommonMethods.plcDevices[i].IsConnected && CommonMethods.plcDevices[i].Name == "PLC_1")
|
|||
|
|
{
|
|||
|
|
CommonMethods.plcDevices[i].WriteInt16("ForceOutStation", 1);
|
|||
|
|
CommonMethods.AddDataMonitorLog(0, $"已触发强制放行信号,容器码:{containerCode}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}, _cancellationTokenSource.Token).ConfigureAwait(false);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"强制放行异常:{ex}", "SysErrorLog");
|
|||
|
|
MessageBox.Show($"强制放行失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
_operationSemaphore.Release();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 重新组盘按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private async void btn_repacking_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!await _operationSemaphore.WaitAsync(0, _cancellationTokenSource.Token).ConfigureAwait(false))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 验证配置
|
|||
|
|
if (!ValidatePlcConfiguration())
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认操作
|
|||
|
|
if (MessageBox.Show("确认执行组盘操作吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 执行操作
|
|||
|
|
await Task.Run(() =>
|
|||
|
|
{
|
|||
|
|
CommonMethods.AddDataMonitorLog(0, "执行重新组盘操作");
|
|||
|
|
}, _cancellationTokenSource.Token).ConfigureAwait(false);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"组盘异常:{ex}", "SysErrorLog");
|
|||
|
|
MessageBox.Show($"组盘失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
_operationSemaphore.Release();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 辅助方法
|
|||
|
|
/// <summary>
|
|||
|
|
/// 拆分位置字符串
|
|||
|
|
/// </summary>
|
|||
|
|
public (int layer, int row, int col) SplitLocation(string location)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(location))
|
|||
|
|
return (0, 0, 0);
|
|||
|
|
|
|||
|
|
var parts = location.Split('-');
|
|||
|
|
int layer = parts.Length > 0 && int.TryParse(parts[0], out var l) ? l : 0;
|
|||
|
|
int row = parts.Length > 1 && int.TryParse(parts[1], out var r) ? r : 0;
|
|||
|
|
int col = parts.Length > 2 && int.TryParse(parts[2], out var c) ? c : 0;
|
|||
|
|
|
|||
|
|
return (layer, row, col);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 解析位置编号
|
|||
|
|
/// </summary>
|
|||
|
|
public string ParseLocation(int layerNum, int number)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (CommonMethods.mesConfig == null)
|
|||
|
|
throw new InvalidOperationException("MES配置未初始化");
|
|||
|
|
|
|||
|
|
int row = number / CommonMethods.mesConfig.colsPerLayer + 1; // 行
|
|||
|
|
int col = (number % CommonMethods.mesConfig.colsPerLayer) + 1; // 列
|
|||
|
|
return $"{layerNum}-{row}-{col}";
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
throw new InvalidOperationException($"解析位置失败: {ex.Message}", ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 过滤条码无效字符
|
|||
|
|
/// </summary>
|
|||
|
|
private string FilterBarcodeChars(string barcode)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(barcode)) return string.Empty;
|
|||
|
|
return barcode.Replace("\r", "").Replace("\n", "").Replace("\t", "").Trim();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 检查条码是否存在
|
|||
|
|
/// </summary>
|
|||
|
|
private async Task<bool> IsBarcodeExistsAsync(string barcode)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(barcode)) return false;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
return await Task.Run(() =>
|
|||
|
|
{
|
|||
|
|
var batteryList = CommonMethods.db.QueryWhereList<BatteryEntity>(it => it.identification == barcode);
|
|||
|
|
return batteryList != null && batteryList.Count > 0;
|
|||
|
|
}).ConfigureAwait(false);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"校验条码是否存在异常:{ex}", "SysErrorLog");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 高亮表格行
|
|||
|
|
/// </summary>
|
|||
|
|
private void HighlightGridViewRow(string barcode)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(barcode) || dgv_ngRsn == null || !dgv_ngRsn.IsHandleCreated) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
BeginInvoke(() =>
|
|||
|
|
{
|
|||
|
|
dgv_ngRsn.ClearSelection();
|
|||
|
|
|
|||
|
|
foreach (DataGridViewRow row in dgv_ngRsn.Rows)
|
|||
|
|
{
|
|||
|
|
if (!row.IsNewRow && row.Cells["identification"]?.Value?.ToString() == barcode)
|
|||
|
|
{
|
|||
|
|
row.Selected = true;
|
|||
|
|
row.DefaultCellStyle.BackColor = Color.Yellow;
|
|||
|
|
dgv_ngRsn.FirstDisplayedScrollingRowIndex = row.Index;
|
|||
|
|
|
|||
|
|
// 3秒后恢复颜色
|
|||
|
|
Task.Delay(3000, _cancellationTokenSource.Token).ContinueWith(t =>
|
|||
|
|
{
|
|||
|
|
if (!t.IsCanceled && IsHandleCreated)
|
|||
|
|
{
|
|||
|
|
Invoke(() => row.DefaultCellStyle.BackColor = Color.Transparent);
|
|||
|
|
}
|
|||
|
|
}, _cancellationTokenSource.Token);
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"高亮表格行异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 验证控件是否有效
|
|||
|
|
/// </summary>
|
|||
|
|
private bool IsControlValid(Control control)
|
|||
|
|
{
|
|||
|
|
return control != null && !control.IsDisposed && control.IsHandleCreated;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 添加替换记录
|
|||
|
|
/// </summary>
|
|||
|
|
private void AddReplaceRecord(string oldBarcode, string newBarcode, BatteryEntity battery, string remark)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
BeginInvoke(() =>
|
|||
|
|
{
|
|||
|
|
var record = new BatteryReplace
|
|||
|
|
{
|
|||
|
|
OldBarCode = oldBarcode,
|
|||
|
|
NewBarCode = newBarcode,
|
|||
|
|
ReplaceTime = DateTime.Now,
|
|||
|
|
GongWei = CommonMethods.sysConfig?.GongWei ?? "",
|
|||
|
|
Location = battery.location,
|
|||
|
|
VassoioID = battery.containerCode,
|
|||
|
|
Grade = battery.grade,
|
|||
|
|
Remark = remark
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
_replaceRecords.Add(record);
|
|||
|
|
|
|||
|
|
// 触发外部委托
|
|||
|
|
CommonMethods.ShowBatteryReplaceDelegate?.Invoke(new List<BatteryReplace> { record });
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"添加替换记录异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 从NG列表移除电池
|
|||
|
|
/// </summary>
|
|||
|
|
private void RemoveBatteryFromNgList(string barcode)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(barcode)) return;
|
|||
|
|
|
|||
|
|
for (int i = _ngBatteryRecords.Count - 1; i >= 0; i--)
|
|||
|
|
{
|
|||
|
|
if (_ngBatteryRecords[i].identification == barcode)
|
|||
|
|
{
|
|||
|
|
_ngBatteryRecords.RemoveAt(i);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清空输入框
|
|||
|
|
/// </summary>
|
|||
|
|
private void ClearInputTextBoxes()
|
|||
|
|
{
|
|||
|
|
txtBox_Row?.Clear();
|
|||
|
|
txtBox_Carton?.Clear();
|
|||
|
|
txtBox_Barcode?.Clear();
|
|||
|
|
txtBox_SubBarcode?.Clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 验证PLC配置
|
|||
|
|
/// </summary>
|
|||
|
|
private bool ValidatePlcConfiguration()
|
|||
|
|
{
|
|||
|
|
if (CommonMethods.mesConfig == null || CommonMethods.plcDevices == null ||
|
|||
|
|
CommonMethods.plcDevices.Count == 0 || !CommonMethods.mesConfig.isConnected)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show("MES配置或PLC设备未初始化/未连接,无法执行操作!",
|
|||
|
|
"提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取选中的容器码
|
|||
|
|
/// </summary>
|
|||
|
|
private string GetSelectedContainerCode()
|
|||
|
|
{
|
|||
|
|
return dgv_ngRsn?.Rows.Count > 0
|
|||
|
|
? dgv_ngRsn.Rows[0].Cells["containerCode"]?.Value?.ToString()?.Trim()
|
|||
|
|
: string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 验证条码输入
|
|||
|
|
/// </summary>
|
|||
|
|
private bool ValidateBarcodeInput(string oldBarcode, string newBarcode)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(oldBarcode) || string.IsNullOrEmpty(newBarcode))
|
|||
|
|
{
|
|||
|
|
MessageBox.Show("原条码和新条码均不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(oldBarcode))
|
|||
|
|
txtBox_Barcode?.Focus();
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
txtBox_SubBarcode?.Focus();
|
|||
|
|
txtBox_SubBarcode?.SelectAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (oldBarcode == newBarcode)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show("原条码和新条码不能一致!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|||
|
|
txtBox_SubBarcode?.Focus();
|
|||
|
|
txtBox_SubBarcode?.SelectAll();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新数据库中的条码
|
|||
|
|
/// </summary>
|
|||
|
|
private async Task<bool> UpdateBarcodeInDatabase(List<BatteryEntity> batteryList, string newBarcode)
|
|||
|
|
{
|
|||
|
|
return await Task.Run(() =>
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
foreach (var entity in batteryList)
|
|||
|
|
{
|
|||
|
|
entity.identification = newBarcode;
|
|||
|
|
CommonMethods.db.Update(entity);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"数据库替换条码异常:{ex}", "SysErrorLog");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}).ConfigureAwait(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据条码查找电池位置
|
|||
|
|
/// </summary>
|
|||
|
|
private int FindBatteryLocByBarcode(string barcode)
|
|||
|
|
{
|
|||
|
|
for (int i = 1; i <= 3; i++)
|
|||
|
|
{
|
|||
|
|
if (_batteryControlsByStationNo.TryGetValue(i, out var controls))
|
|||
|
|
{
|
|||
|
|
if (controls.Values.Any(b => b.Barcode == barcode))
|
|||
|
|
{
|
|||
|
|
return i;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据条码查找电池控件
|
|||
|
|
/// </summary>
|
|||
|
|
private BatteryControl FindBatteryControlByBarcode(string barcode)
|
|||
|
|
{
|
|||
|
|
if (!string.IsNullOrEmpty(barcode) && _barcodeToControlMap.TryGetValue(barcode, out var mapValue))
|
|||
|
|
{
|
|||
|
|
if (_batteryControlsByStationNo.TryGetValue(mapValue.stationNo, out var locControls) &&
|
|||
|
|
locControls.TryGetValue(mapValue.controlKey, out var batteryControl) &&
|
|||
|
|
IsControlValid(batteryControl))
|
|||
|
|
{
|
|||
|
|
return batteryControl;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 映射表未找到时,遍历所有控件(兜底)
|
|||
|
|
foreach (var locControls in _batteryControlsByStationNo.Values)
|
|||
|
|
{
|
|||
|
|
var battery = locControls.Values.FirstOrDefault(b => b.Barcode == barcode);
|
|||
|
|
if (battery != null)
|
|||
|
|
{
|
|||
|
|
return battery;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新电池信息显示
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdateBatteryInfoDisplay(BatteryControl battery)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
Invoke(() =>
|
|||
|
|
{
|
|||
|
|
string position = $"{battery.Layer}-{battery.Row}-{battery.Col}";
|
|||
|
|
|
|||
|
|
if (txtBox_Row != null && !txtBox_Row.IsDisposed)
|
|||
|
|
txtBox_Row.Text = position;
|
|||
|
|
|
|||
|
|
if (txtBox_Barcode != null && !txtBox_Barcode.IsDisposed)
|
|||
|
|
txtBox_Barcode.Text = battery.Barcode ?? "";
|
|||
|
|
|
|||
|
|
if (txtBox_Carton != null && !txtBox_Carton.IsDisposed)
|
|||
|
|
txtBox_Carton.Text = battery.Container ?? "";
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"更新电池信息显示异常: {ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置标签页文本
|
|||
|
|
/// </summary>
|
|||
|
|
private void SetTabPageText(int stationNo, string grade)
|
|||
|
|
{
|
|||
|
|
switch (stationNo)
|
|||
|
|
{
|
|||
|
|
case 1: tabPagePlus1.Text = $"1_{grade}"; break;
|
|||
|
|
case 2: tabPagePlus2.Text = $"2_{grade}"; break;
|
|||
|
|
case 3: tabPagePlus3.Text = $"3_{grade}"; break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 添加面板表头
|
|||
|
|
/// </summary>
|
|||
|
|
private void AddPanelHeaders(TableLayoutPanel panel, int cols, int rowNumWidth, int cellSize)
|
|||
|
|
{
|
|||
|
|
// 层数标签
|
|||
|
|
var layerHeader = new Label
|
|||
|
|
{
|
|||
|
|
Text = "层",
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50),
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold)
|
|||
|
|
};
|
|||
|
|
panel.Controls.Add(layerHeader, 0, 0);
|
|||
|
|
|
|||
|
|
// 行/列表头
|
|||
|
|
var rowColHeader = new Label
|
|||
|
|
{
|
|||
|
|
Text = "行/列",
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50)
|
|||
|
|
};
|
|||
|
|
panel.Controls.Add(rowColHeader, 1, 0);
|
|||
|
|
|
|||
|
|
// 列号标签
|
|||
|
|
for (int col = 0; col < cols; col++)
|
|||
|
|
{
|
|||
|
|
var colLabel = new Label
|
|||
|
|
{
|
|||
|
|
Text = col.ToString(),
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Padding = new Padding(0),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50)
|
|||
|
|
};
|
|||
|
|
panel.Controls.Add(colLabel, col+2, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//panel.Controls.Add(colHeaderPanel, 1, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 配置单元格面板样式
|
|||
|
|
/// </summary>
|
|||
|
|
private void ConfigureCellPanelStyles(TableLayoutPanel panel, int rows, int cols, int cellSize, int rowNumWidth)
|
|||
|
|
{
|
|||
|
|
// 行样式
|
|||
|
|
panel.RowStyles.Clear();
|
|||
|
|
for (int r = 0; r < rows; r++)
|
|||
|
|
{
|
|||
|
|
panel.RowStyles.Add(new RowStyle(SizeType.Absolute, cellSize));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 列样式
|
|||
|
|
panel.ColumnStyles.Clear();
|
|||
|
|
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, rowNumWidth));
|
|||
|
|
for (int c = 1; c <= cols; c++)
|
|||
|
|
{
|
|||
|
|
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, cellSize));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
panel.CellBorderStyle = TableLayoutPanelCellBorderStyle.None;
|
|||
|
|
panel.Margin = new Padding(0);
|
|||
|
|
panel.Padding = new Padding(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 添加行号标签
|
|||
|
|
/// </summary>
|
|||
|
|
private void AddRowNumberLabels(TableLayoutPanel panel, int layer, int rows)
|
|||
|
|
{
|
|||
|
|
for (int row = 0; row < rows; row++)
|
|||
|
|
{
|
|||
|
|
var rowLabel = new Label
|
|||
|
|
{
|
|||
|
|
Text = (row + 1).ToString(),
|
|||
|
|
TextAlign = ContentAlignment.MiddleCenter,
|
|||
|
|
ForeColor = Color.White,
|
|||
|
|
Dock = DockStyle.Fill,
|
|||
|
|
Margin = new Padding(0),
|
|||
|
|
Font = new Font("Microsoft YaHei", 9, FontStyle.Bold),
|
|||
|
|
BackColor = Color.FromArgb(18, 30, 50)
|
|||
|
|
};
|
|||
|
|
panel.Controls.Add(rowLabel, 1, layer * rows + row);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置面板可见性
|
|||
|
|
/// </summary>
|
|||
|
|
private void SetPanelsVisibility(bool visible)
|
|||
|
|
{
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
if (panelExt_Grade1 != null && !panelExt_Grade1.IsDisposed)
|
|||
|
|
panelExt_Grade1.Visible = visible;
|
|||
|
|
if (panelExt_Grade2 != null && !panelExt_Grade2.IsDisposed)
|
|||
|
|
panelExt_Grade2.Visible = visible;
|
|||
|
|
if (panelExt_Grade3 != null && !panelExt_Grade3.IsDisposed)
|
|||
|
|
panelExt_Grade3.Visible = visible;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 暂停面板布局
|
|||
|
|
/// </summary>
|
|||
|
|
private void SuspendPanelsLayout()
|
|||
|
|
{
|
|||
|
|
panelExt_Grade1?.SuspendLayout();
|
|||
|
|
panelExt_Grade2?.SuspendLayout();
|
|||
|
|
panelExt_Grade3?.SuspendLayout();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 恢复面板布局
|
|||
|
|
/// </summary>
|
|||
|
|
private void ResumePanelsLayout()
|
|||
|
|
{
|
|||
|
|
panelExt_Grade1?.ResumeLayout(false);
|
|||
|
|
panelExt_Grade2?.ResumeLayout(false);
|
|||
|
|
panelExt_Grade3?.ResumeLayout(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清空面板控件
|
|||
|
|
/// </summary>
|
|||
|
|
private void ClearPanelsControls()
|
|||
|
|
{
|
|||
|
|
panelExt_Grade1?.Controls.Clear();
|
|||
|
|
panelExt_Grade2?.Controls.Clear();
|
|||
|
|
panelExt_Grade3?.Controls.Clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 释放电池控件
|
|||
|
|
/// </summary>
|
|||
|
|
private void DisposeBatteryControls()
|
|||
|
|
{
|
|||
|
|
foreach (var locControls in _batteryControlsByStationNo.Values)
|
|||
|
|
{
|
|||
|
|
foreach (var battery in locControls.Values)
|
|||
|
|
{
|
|||
|
|
battery.Dispose();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清空所有面板
|
|||
|
|
/// </summary>
|
|||
|
|
private void ClearAllPanels()
|
|||
|
|
{
|
|||
|
|
panelExt_Grade1?.Controls.Clear();
|
|||
|
|
panelExt_Grade2?.Controls.Clear();
|
|||
|
|
panelExt_Grade3?.Controls.Clear();
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 公共显示方法(对外接口)
|
|||
|
|
/// <summary>
|
|||
|
|
/// 显示NG电池列表
|
|||
|
|
/// </summary>
|
|||
|
|
public void ShowBatteryNGRsn(List<BatteryEntity> list)
|
|||
|
|
{
|
|||
|
|
list ??= new List<BatteryEntity>();
|
|||
|
|
|
|||
|
|
|
|||
|
|
//// 确保在UI线程执行
|
|||
|
|
//if (InvokeRequired)
|
|||
|
|
//{
|
|||
|
|
// Invoke(new Action(() => ShowBatteryNGRsn(list)));
|
|||
|
|
// return;
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//// 强制关闭自动生成列(避免列名混乱)
|
|||
|
|
//dgv_ngRsn.AutoGenerateColumns = false;
|
|||
|
|
//dgv_ngRsn?.SuspendLayout();
|
|||
|
|
|
|||
|
|
//try
|
|||
|
|
//{
|
|||
|
|
// // 筛选NG电池
|
|||
|
|
// var ngItems = list.Where(b => !b.packResult).ToList();
|
|||
|
|
|
|||
|
|
// if (ngItems.Count == 0)
|
|||
|
|
// {
|
|||
|
|
// Debug.WriteLine("无NG数据");
|
|||
|
|
// return;
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// // 暂停列表事件
|
|||
|
|
// _ngBatteryRecords.RaiseListChangedEvents = false;
|
|||
|
|
// foreach (var item in ngItems)
|
|||
|
|
// {
|
|||
|
|
// var existingItem = _ngBatteryRecords.FirstOrDefault(x => x.identification == item.identification);
|
|||
|
|
// if (existingItem != null)
|
|||
|
|
// {
|
|||
|
|
// // 更新原有记录
|
|||
|
|
// existingItem.location = item.location;
|
|||
|
|
// existingItem.grade = item.grade;
|
|||
|
|
// existingItem.packResult = item.packResult;
|
|||
|
|
// existingItem.outStationType = item.outStationType;
|
|||
|
|
// existingItem.packFalseReason = item.packFalseReason;
|
|||
|
|
// existingItem.containerCode = item.containerCode;
|
|||
|
|
// existingItem.packTime = item.packTime;
|
|||
|
|
// }
|
|||
|
|
// else
|
|||
|
|
// {
|
|||
|
|
// _ngBatteryRecords.Add(item);
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
// _ngBatteryRecords.RaiseListChangedEvents = true;
|
|||
|
|
// _ngBatteryRecords.ResetBindings();
|
|||
|
|
|
|||
|
|
// if (_ngBatteryRecords.Count > 0)
|
|||
|
|
// {
|
|||
|
|
// dgv_ngRsn.ClearSelection();
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// dgv_ngRsn?.ResumeLayout(false);
|
|||
|
|
// // 强制刷新表格单元格
|
|||
|
|
// dgv_ngRsn.Invalidate();
|
|||
|
|
// //dgv_ngRsn.Invalidate(true); // 强制重绘整个控件
|
|||
|
|
//}
|
|||
|
|
//catch (Exception ex)
|
|||
|
|
//{
|
|||
|
|
// LogHelper.Instance.WriteError($"显示NG电池异常:{ex}", "BatteryStatusLog");
|
|||
|
|
//}
|
|||
|
|
//finally
|
|||
|
|
//{
|
|||
|
|
// dgv_ngRsn?.ResumeLayout(true);
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
SafeUI(() =>
|
|||
|
|
{
|
|||
|
|
dgv_ngRsn.AutoGenerateColumns = false;
|
|||
|
|
dgv_ngRsn.SuspendLayout();
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var ngItems = list.Where(b => !b.packResult).ToList();
|
|||
|
|
_ngBatteryRecords.RaiseListChangedEvents = false;
|
|||
|
|
|
|||
|
|
foreach (var item in ngItems)
|
|||
|
|
{
|
|||
|
|
var exist = _ngBatteryRecords.FirstOrDefault(x => x.identification == item.identification);
|
|||
|
|
if (exist != null)
|
|||
|
|
{
|
|||
|
|
exist.location = item.location;
|
|||
|
|
exist.grade = item.grade;
|
|||
|
|
exist.packResult = item.packResult;
|
|||
|
|
exist.packFalseReason = item.packFalseReason;
|
|||
|
|
exist.containerCode = item.containerCode;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
_ngBatteryRecords.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_ngBatteryRecords.RaiseListChangedEvents = true;
|
|||
|
|
_ngBatteryRecords.ResetBindings();
|
|||
|
|
dgv_ngRsn.ResumeLayout(true);
|
|||
|
|
dgv_ngRsn.Invalidate();
|
|||
|
|
}
|
|||
|
|
catch
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void dgv_ngRsn_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
|
|||
|
|
{
|
|||
|
|
//显示序列号
|
|||
|
|
DataGridViewHelper.DgvRowPostPaint(this.dgv_ngRsn, e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void dgv_ngRsn_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
|
|||
|
|
{
|
|||
|
|
//指定列字体颜色
|
|||
|
|
DataGridViewHelper.DgvRowPrePaint(this.dgv_ngRsn, "packResult", sender, e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 显示替换记录
|
|||
|
|
/// </summary>
|
|||
|
|
public void ShowBatteryReplace(List<BatteryReplace> list)
|
|||
|
|
{
|
|||
|
|
list ??= new List<BatteryReplace>();
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
BeginInvoke(() =>
|
|||
|
|
{
|
|||
|
|
dgvReplaceBattery?.SuspendLayout();
|
|||
|
|
|
|||
|
|
// 限制记录数量(最多500条)
|
|||
|
|
if (_replaceRecords.Count > 500)
|
|||
|
|
{
|
|||
|
|
var tempList = _replaceRecords.Skip(_replaceRecords.Count - 500).ToList();
|
|||
|
|
_replaceRecords.Clear();
|
|||
|
|
foreach (var item in tempList)
|
|||
|
|
{
|
|||
|
|
_replaceRecords.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加新记录
|
|||
|
|
foreach (var item in list)
|
|||
|
|
{
|
|||
|
|
if (!_replaceRecords.Any(x => x.OldBarCode == item.OldBarCode && x.NewBarCode == item.NewBarCode))
|
|||
|
|
{
|
|||
|
|
_replaceRecords.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dgvReplaceBattery?.ResumeLayout(false);
|
|||
|
|
dgvReplaceBattery?.Invalidate();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"显示替换记录异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void dgvReplaceBattery_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
|
|||
|
|
{
|
|||
|
|
//显示序列号
|
|||
|
|
DataGridViewHelper.DgvRowPostPaint(this.dgvReplaceBattery, e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 显示电池状态
|
|||
|
|
/// </summary>
|
|||
|
|
public void ShowBatteryStatus(int stationNo, List<BatteryEntity> list)
|
|||
|
|
{
|
|||
|
|
if (list == null || list.Count == 0) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
BindBatteryData(stationNo, list);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"显示电池状态失败:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 局部刷新控件(仅重绘变化区域,避免全局卡顿)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="control">目标控件</param>
|
|||
|
|
/// <param name="rect">需要重绘的区域(null则重绘控件本身)</param>
|
|||
|
|
private void RefreshControlPartial(Control control, Rectangle? rect = null)
|
|||
|
|
{
|
|||
|
|
if (!IsControlValid(control)) return;
|
|||
|
|
|
|||
|
|
if (control.InvokeRequired)
|
|||
|
|
{
|
|||
|
|
control.Invoke(new Action(() => RefreshControlPartial(control, rect)));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只重绘指定区域,而非整个控件
|
|||
|
|
if (rect.HasValue)
|
|||
|
|
{
|
|||
|
|
control.Invalidate(rect.Value);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
control.Invalidate(); // 仅重绘控件边界内区域
|
|||
|
|
}
|
|||
|
|
control.Update(); // 立即更新,不等待刷新队列
|
|||
|
|
|
|||
|
|
// 仅刷新直接父容器,而非递归刷新所有父级(减少卡顿)
|
|||
|
|
var parent = control.Parent;
|
|||
|
|
if (parent != null && !parent.IsDisposed)
|
|||
|
|
{
|
|||
|
|
parent.Invalidate(control.Bounds);
|
|||
|
|
parent.Update();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 测试方法
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 测试按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void button1_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
//TestTrayStation();
|
|||
|
|
TestStatusNG();
|
|||
|
|
//TestBatteryStation();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void TestStatusNG()
|
|||
|
|
{
|
|||
|
|
int stationNo = 1;
|
|||
|
|
int layerNum = 1;
|
|||
|
|
string grade = "A";
|
|||
|
|
string GongWei = CommonMethods.sysConfig.GongWei;
|
|||
|
|
string containerCode = "XM1111";
|
|||
|
|
List<BatteryInfo> batteryInfos = null;
|
|||
|
|
List<BGearEntity> bGearlist = new List<BGearEntity>();
|
|||
|
|
int MAX_BATTERY_NUM = 60;
|
|||
|
|
batteryInfos = Enumerable.Repeat(new BatteryInfo(), MAX_BATTERY_NUM).ToList();
|
|||
|
|
|
|||
|
|
BGearEntity m = new BGearEntity
|
|||
|
|
{
|
|||
|
|
TestTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
|||
|
|
VassoioID = containerCode,
|
|||
|
|
GongWei = GongWei,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
List<MaterialPackInfo> packInfos = new List<MaterialPackInfo>(batteryInfos.Count);
|
|||
|
|
for (int i = 0; i < batteryInfos.Count; i++)
|
|||
|
|
{
|
|||
|
|
bool packResult = true;
|
|||
|
|
string packFalseReason = null;
|
|||
|
|
|
|||
|
|
if (i % 3 == 1)
|
|||
|
|
{
|
|||
|
|
packResult = false;
|
|||
|
|
packFalseReason = "错误NG1";
|
|||
|
|
}
|
|||
|
|
else if (i % 3 == 2)
|
|||
|
|
{
|
|||
|
|
packResult = false;
|
|||
|
|
packFalseReason = "错误NG2";
|
|||
|
|
}
|
|||
|
|
MaterialPackInfo materialPackInfo = new MaterialPackInfo
|
|||
|
|
{
|
|||
|
|
identification = "DC_" + i.ToString(),
|
|||
|
|
locationRow = ParseLocation(layerNum, i),
|
|||
|
|
packResult = packResult,
|
|||
|
|
packFalseReason = packFalseReason
|
|||
|
|
};
|
|||
|
|
packInfos.Add(materialPackInfo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<BatteryEntity> batteryEntities = new List<BatteryEntity>(packInfos.Count);
|
|||
|
|
|
|||
|
|
for (int i = 0; i < packInfos.Count; i++)
|
|||
|
|
{
|
|||
|
|
var info = packInfos[i];
|
|||
|
|
batteryEntities.Add(new BatteryEntity
|
|||
|
|
{
|
|||
|
|
identification = info.identification,
|
|||
|
|
location = info.locationRow,
|
|||
|
|
packResult = info.packResult,
|
|||
|
|
packFalseReason = info.packFalseReason,
|
|||
|
|
containerCode = containerCode,
|
|||
|
|
grade = grade,
|
|||
|
|
outStationType = 1,
|
|||
|
|
packTime = m.TestTime
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//if (batteryEntities.Count > 0)
|
|||
|
|
//{
|
|||
|
|
// CommonMethods.ShowBatteryNGRsnDelegate?.Invoke(batteryEntities);
|
|||
|
|
// CommonMethods.ShowBatteryStatusDelegate?.Invoke(stationNo, batteryEntities);
|
|||
|
|
// //CommonMethods.ShowBatteryQRCoderDelegate?.Invoke(batteryEntities);
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
m.Result = "NG";
|
|||
|
|
m.Remark = "电芯绑定失败";
|
|||
|
|
m.OutSuccTotal = batteryEntities.Where(x => x.packResult == true).Count();
|
|||
|
|
m.Qty = grade;
|
|||
|
|
m.Total = batteryEntities.Count;
|
|||
|
|
bGearlist.Add(m);
|
|||
|
|
CommonMethods.ShowBlankingDelegate.Invoke(bGearlist);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task TestTrayStation()
|
|||
|
|
{
|
|||
|
|
// 初始化业务层TrayInfo
|
|||
|
|
TrayInfo mesTrayInfo = new TrayInfo();
|
|||
|
|
int stationNo = 1;
|
|||
|
|
string trayID = "1234567890";
|
|||
|
|
mesTrayInfo.cellList = new List<Cell>
|
|||
|
|
{
|
|||
|
|
new Cell { cellNo = "11111111111", channel = 1 },
|
|||
|
|
new Cell { cellNo = "22222222222", channel = 2 },
|
|||
|
|
};
|
|||
|
|
mesTrayInfo.trayNo = trayID;
|
|||
|
|
mesTrayInfo.grade = MesConfig.GetLocGrade(stationNo, CommonMethods.curFdConfig);
|
|||
|
|
mesTrayInfo.productCode = CommonMethods.mesConfig.ModelName;
|
|||
|
|
//mesTrayInfo.lineNo = "Line01";
|
|||
|
|
mesTrayInfo.moreGrade = "A1";
|
|||
|
|
|
|||
|
|
// 业务逻辑判断
|
|||
|
|
List<string> grades = CommonMethods.GetValidGrades();
|
|||
|
|
bool bModelSame = mesTrayInfo.productCode == CommonMethods.mesConfig.ModelName;
|
|||
|
|
bool bGradeIn = grades.Contains(mesTrayInfo.grade);
|
|||
|
|
int ret = (bModelSame && bGradeIn) ? 1 : 2;
|
|||
|
|
|
|||
|
|
// 完整赋值TrayEntity(数据库层)
|
|||
|
|
TrayEntity mesTrayEntity = new TrayEntity();
|
|||
|
|
// 映射TrayInfo的核心字段到TrayEntity
|
|||
|
|
mesTrayEntity.trayID = mesTrayInfo.trayNo; // 关键:料框码必须赋值,否则更新/新增无标识
|
|||
|
|
mesTrayEntity.model = mesTrayInfo.productCode;
|
|||
|
|
mesTrayEntity.grade = mesTrayInfo.grade;
|
|||
|
|
mesTrayEntity.moreGrade = mesTrayInfo.moreGrade;
|
|||
|
|
//mesTrayEntity.lineNo = mesTrayInfo.lineNo;
|
|||
|
|
|
|||
|
|
// 补充数据库必需字段
|
|||
|
|
mesTrayEntity.Count = mesTrayInfo.cellList?.Count ?? 0; // 电池个数=电芯条码数量
|
|||
|
|
mesTrayEntity.GongWei = stationNo.ToString(); // 机架号=工位号
|
|||
|
|
mesTrayEntity.Time = DateTime.Now; // 进站时间=当前时间
|
|||
|
|
mesTrayEntity.Result = ret == 1 ? "本机架处理" : "非本机架处理";
|
|||
|
|
mesTrayEntity.Remark = $"{mesTrayEntity.Result}:型号{bModelSame},等级{bGradeIn}";
|
|||
|
|
|
|||
|
|
// 封装为列表并保存
|
|||
|
|
List<TrayEntity> mesTrayInfos = new List<TrayEntity>() { mesTrayEntity };
|
|||
|
|
await SaveTrayDataAsync(mesTrayInfos).ConfigureAwait(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task SaveTrayDataAsync(List<TrayEntity> list)
|
|||
|
|
{
|
|||
|
|
if (list == null || list.Count == 0)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
string strRes = "";
|
|||
|
|
foreach (TrayEntity m in list)
|
|||
|
|
{
|
|||
|
|
int updateCount = await CommonMethods.db.UpdateSingleAsync<TrayEntity>(
|
|||
|
|
m,
|
|||
|
|
null,
|
|||
|
|
it => it.trayID == m.trayID).ConfigureAwait(false);
|
|||
|
|
|
|||
|
|
if (updateCount > 0)
|
|||
|
|
{
|
|||
|
|
strRes = "更新Yes";
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// SQLSugar会自动忽略[IsIgnore]字段,无需额外处理
|
|||
|
|
bool addSuccess = await CommonMethods.db.AddReturnBoolAsync<TrayEntity>(m).ConfigureAwait(false);
|
|||
|
|
strRes = addSuccess ? "新增Yes" : "新增No";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新备注
|
|||
|
|
m.Remark = $"{m.Remark} | {strRes}";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保留原有委托调用(兼容其他显示逻辑)
|
|||
|
|
CommonMethods.ShowTrayDataDelegate?.Invoke(list);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"物流线进站保存处理异常:{ex}", "SysErrorLog");
|
|||
|
|
throw; // 重新抛出异常,让上层知道保存失败
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task TestBatteryStation()
|
|||
|
|
{
|
|||
|
|
TrayInfo mesTrayInfo = new TrayInfo();
|
|||
|
|
List<AGearEntity> list = new List<AGearEntity>();//实体
|
|||
|
|
string recordDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|||
|
|
var GongWei = CommonMethods.sysConfig.GongWei;
|
|||
|
|
|
|||
|
|
string trayID = "1234567890";
|
|||
|
|
List<BatteryControl> batteryControls = new List<BatteryControl>();
|
|||
|
|
|
|||
|
|
mesTrayInfo.cellList = new List<Cell>
|
|||
|
|
{
|
|||
|
|
new Cell { cellNo = "11111111111", channel = 1 },
|
|||
|
|
new Cell { cellNo = "22222222222", channel = 2 },
|
|||
|
|
};
|
|||
|
|
mesTrayInfo.trayNo = trayID;
|
|||
|
|
mesTrayInfo.grade = "A";
|
|||
|
|
mesTrayInfo.productCode = CommonMethods.mesConfig.ModelName;
|
|||
|
|
|
|||
|
|
//创建AGearEntity列表
|
|||
|
|
for (int i = 0; i < mesTrayInfo.cellList.Count; i++)
|
|||
|
|
{
|
|||
|
|
var entity = new AGearEntity
|
|||
|
|
{
|
|||
|
|
Location = mesTrayInfo.cellList[i].channel.ToString(),
|
|||
|
|
Time = DateTime.Now, //DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
|||
|
|
BarCode = mesTrayInfo.cellList[i].cellNo,
|
|||
|
|
TrayID = mesTrayInfo.trayNo,
|
|||
|
|
Result = "OK",
|
|||
|
|
Remark = "",
|
|||
|
|
Grade = mesTrayInfo.grade ?? "",
|
|||
|
|
Model = mesTrayInfo.productCode ?? "",
|
|||
|
|
GongWei = GongWei,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
list.Add(entity);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (list.Count > 0)
|
|||
|
|
{
|
|||
|
|
await SaveFeedingDataAsync(list).ConfigureAwait(false);
|
|||
|
|
CommonMethods.AddDataMonitorLog(0, $"{GongWei} 保存{list.Count}条数据到数据库");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task SaveFeedingDataAsync(List<AGearEntity> list)
|
|||
|
|
{
|
|||
|
|
if (list == null || list.Count == 0)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 明确指定为异步 lambda
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 数据库操作
|
|||
|
|
string strRes = "";
|
|||
|
|
foreach (AGearEntity m in list)
|
|||
|
|
{
|
|||
|
|
//更新
|
|||
|
|
int b = await CommonMethods.db.UpdateSingleAsync<AGearEntity>(m, null, it => it.BarCode == m.BarCode).ConfigureAwait(false);
|
|||
|
|
if (b > 0)
|
|||
|
|
{
|
|||
|
|
strRes = "更新Yes";
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
//新增
|
|||
|
|
bool b2 = await CommonMethods.db.AddReturnBoolAsync<AGearEntity>(m).ConfigureAwait(false);
|
|||
|
|
strRes = b2 ? "新增Yes" : "新增No";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
m.Remark = m.Remark + strRes;
|
|||
|
|
}
|
|||
|
|
//数据显示 - 在主线程执行
|
|||
|
|
CommonMethods.ShowFeedingDelegate?.Invoke(list);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"组盘工位保存处理异常:{ex}", "SysErrorLog");
|
|||
|
|
throw; // 重新抛出异常,让上层知道保存失败
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
private void SafeUI(Action action, bool forceAsync = false)
|
|||
|
|
{
|
|||
|
|
if (action == null || IsDisposed || !IsHandleCreated) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (InvokeRequired)
|
|||
|
|
{
|
|||
|
|
if (forceAsync)
|
|||
|
|
{
|
|||
|
|
// 异步调用不等待,避免阻塞回调
|
|||
|
|
BeginInvoke(new Action(() =>
|
|||
|
|
{
|
|||
|
|
try { action(); }
|
|||
|
|
catch (Exception ex) { LogHelper.Instance.WriteError($"异步UI操作异常:{ex}", "BatteryStatusLog"); }
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 同步调用捕获异常
|
|||
|
|
Invoke(new Action(() =>
|
|||
|
|
{
|
|||
|
|
try { action(); }
|
|||
|
|
catch (Exception ex) { LogHelper.Instance.WriteError($"同步UI操作异常:{ex}", "BatteryStatusLog"); }
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
action();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (ObjectDisposedException)
|
|||
|
|
{
|
|||
|
|
// 窗体已释放,忽略
|
|||
|
|
}
|
|||
|
|
catch (InvalidAsynchronousStateException)
|
|||
|
|
{
|
|||
|
|
// 控件句柄无效,忽略
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"SafeUI执行异常:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static class CancellationTokenExtensions
|
|||
|
|
{
|
|||
|
|
public static void UnregisterAll(this CancellationToken token)
|
|||
|
|
{
|
|||
|
|
// 通过反射清空回调
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var registrationsField = token.GetType().GetField(
|
|||
|
|
"_registrations",
|
|||
|
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|||
|
|
if (registrationsField != null)
|
|||
|
|
{
|
|||
|
|
var registrations = registrationsField.GetValue(token);
|
|||
|
|
if (registrations != null)
|
|||
|
|
{
|
|||
|
|
var clearMethod = registrations.GetType().GetMethod(
|
|||
|
|
"Clear",
|
|||
|
|
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
|||
|
|
clearMethod?.Invoke(registrations, null);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
LogHelper.Instance.WriteError($"移除CancellationToken回调失败:{ex}", "BatteryStatusLog");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|