964 lines
40 KiB
C#
964 lines
40 KiB
C#
using System;
|
||
using System.ComponentModel;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Windows.Forms;
|
||
using DC_A95;
|
||
using MesWork;
|
||
|
||
namespace MesWork.Pages
|
||
{
|
||
/// <summary>
|
||
/// 称重作业页面 — 双工位实时称重监控
|
||
/// 信号灯12个(4×3),500ms轮询PLC数据
|
||
/// </summary>
|
||
public partial class UC_Weighing : UserControl
|
||
{
|
||
// ── 信号灯名称(12个 = 4×3网格)──
|
||
private static readonly string[] SignalNames =
|
||
{
|
||
"工件到位", "读取完成", "开始称重", "称重完成",
|
||
"请求工作", "允许工作", "请求保存", "保存完成",
|
||
"等待放行", "PLC心跳", "PC心跳", "合格标志"
|
||
};
|
||
|
||
/// <summary>信号灯对应的 tagTypeCodeID</summary>
|
||
private static readonly int[] SignalTagCodes =
|
||
{
|
||
2, // 工件到位
|
||
100001, // 读取完成
|
||
100002, // 开始称重
|
||
100003, // 称重完成
|
||
44, // 请求工作
|
||
66, // 允许工作
|
||
19, // 请求保存
|
||
20, // 保存完成
|
||
100004, // 等待放行
|
||
14, // PLC心跳
|
||
15, // PC心跳
|
||
21 // 合格标志 (Int: 0=无, 1=合格, 2=不合格)
|
||
};
|
||
|
||
private static readonly Color C_SignalOn = Color.FromArgb(16, 185, 129); // 绿色
|
||
private static readonly Color C_SignalOff = Color.FromArgb(209, 213, 219); // 灰色
|
||
private static readonly Color C_SignalBad = Color.FromArgb(239, 68, 68); // 红色(不合格)
|
||
|
||
/// <summary>报警代码文本映射</summary>
|
||
private static readonly string[] AlarmCodeTexts =
|
||
{
|
||
"无报警", // 0
|
||
"获取工厂数据接口调用失败", // 1
|
||
"上传工厂数据接口调用失败", // 2
|
||
"发动机号错误" // 3
|
||
};
|
||
|
||
/// <summary>工位1 信号灯面板 [0-11]</summary>
|
||
private Panel[] _s1Signals;
|
||
/// <summary>工位2 信号灯面板 [0-11]</summary>
|
||
private Panel[] _s2Signals;
|
||
|
||
/// <summary>PLC数据轮询定时器 500ms</summary>
|
||
private Timer _tmrPLC;
|
||
|
||
/// <summary>工位1的opName (如OP10)</summary>
|
||
private string _op1;
|
||
/// <summary>工位2的opName (如OP20)</summary>
|
||
private string _op2;
|
||
|
||
private bool _isApplyingMetricLayout;
|
||
|
||
public UC_Weighing()
|
||
{
|
||
InitializeComponent();
|
||
if (IsInDesignMode())
|
||
{
|
||
ApplyDesignerPreviewLayout();
|
||
ApplyWeighingType();
|
||
return;
|
||
}
|
||
Resize += (s, e) => ApplyMetricCardLayout();
|
||
pnl_Stations.Resize += (s, e) => ApplyMetricCardLayout();
|
||
ApplyMetricCardLayout();
|
||
InitSignalLights();
|
||
ApplyWeighingType();
|
||
InitPLCTimer();
|
||
}
|
||
|
||
private void ApplyMetricCardLayout()
|
||
{
|
||
if (IsInDesignMode() || _isApplyingMetricLayout || pnl_Stations.ClientSize.Width <= 0)
|
||
return;
|
||
|
||
_isApplyingMetricLayout = true;
|
||
try
|
||
{
|
||
int logHeight = Math.Max(210, Math.Min(280, Height / 4));
|
||
if (pnl_Log.Height != logHeight)
|
||
pnl_Log.Height = logHeight;
|
||
|
||
pnl_Stations.Padding = new Padding(12, 12, 12, 10);
|
||
|
||
int availableWidth = pnl_Stations.ClientSize.Width - pnl_Stations.Padding.Left - pnl_Stations.Padding.Right;
|
||
int stationGap = 14;
|
||
int stationWidth = Math.Max(620, (availableWidth - stationGap) / 2);
|
||
pnl_Station1.Width = stationWidth;
|
||
|
||
LayoutStationHeaderSection(
|
||
pnl_Station1,
|
||
pnl_S1_Header,
|
||
lbl_S1_Title,
|
||
lbl_S1_EngineLabel,
|
||
lbl_S1_EngineVal,
|
||
lbl_S1_ModelLabel,
|
||
lbl_S1_ModelVal,
|
||
lbl_S1_OrderLabel,
|
||
lbl_S1_OrderVal,
|
||
lbl_S1_PalletLabel,
|
||
lbl_S1_PalletVal);
|
||
LayoutStationHeaderSection(
|
||
pnl_Station2,
|
||
pnl_S2_Header,
|
||
lbl_S2_Title,
|
||
lbl_S2_EngineLabel,
|
||
lbl_S2_EngineVal,
|
||
lbl_S2_ModelLabel,
|
||
lbl_S2_ModelVal,
|
||
lbl_S2_OrderLabel,
|
||
lbl_S2_OrderVal,
|
||
lbl_S2_PalletLabel,
|
||
lbl_S2_PalletVal);
|
||
|
||
LayoutStationMetricCards(
|
||
pnl_Station1,
|
||
pnl_S1_WeightBox,
|
||
label1,
|
||
lbl_S1_Weight,
|
||
lbl_S1_WeightUnit,
|
||
pnl_S1_WaterBox,
|
||
lbl_S1_WaterTitle,
|
||
lbl_S1_Info1,
|
||
lbl_S1_WaterUnit,
|
||
pnl_S1_MetricBox,
|
||
pnl_S1_DensityBox,
|
||
lbl_S1_DensityTitle,
|
||
lbl_S1_DensityVal,
|
||
lbl_S1_DensityUnit,
|
||
pnl_S1_AddOilBox,
|
||
lbl_S1_AddOilTitle,
|
||
lbl_S1_Info2,
|
||
lbl_S1_AddOilUnit,
|
||
pnl_S1_ExtractOilBox,
|
||
lbl_S1_ExtractOilTitle,
|
||
lbl_S1_ExtractOilVal,
|
||
lbl_S1_ExtractOilUnit,
|
||
pnl_S1_OilReleaseBox,
|
||
lbl_S1_OilReleaseTitle,
|
||
lbl_S1_OilReleaseVal,
|
||
lbl_S1_OilReleaseUnit,
|
||
pnl_S1_ResidualOilBox,
|
||
lbl_S1_ResidualOilTitle,
|
||
lbl_S1_ResidualOilVal,
|
||
lbl_S1_ResidualOilUnit,
|
||
pnl_S1_QualityBox,
|
||
lbl_S1_QualityTitle,
|
||
lbl_S1_QualityVal);
|
||
LayoutStationMetricCards(
|
||
pnl_Station2,
|
||
pnl_S2_WeightBox,
|
||
lbl_S2_WeightTitle,
|
||
lbl_S2_Weight,
|
||
lbl_S2_WeightUnit,
|
||
pnl_S2_WaterBox,
|
||
lbl_S2_WaterTitle,
|
||
lbl_S2_Info1,
|
||
lbl_S2_WaterUnit,
|
||
pnl_S2_MetricBox,
|
||
pnl_S2_DensityBox,
|
||
lbl_S2_DensityTitle,
|
||
lbl_S2_DensityVal,
|
||
lbl_S2_DensityUnit,
|
||
pnl_S2_AddOilBox,
|
||
lbl_S2_AddOilTitle,
|
||
lbl_S2_Info2,
|
||
lbl_S2_AddOilUnit,
|
||
pnl_S2_ExtractOilBox,
|
||
lbl_S2_ExtractOilTitle,
|
||
lbl_S2_ExtractOilVal,
|
||
lbl_S2_ExtractOilUnit,
|
||
pnl_S2_OilReleaseBox,
|
||
lbl_S2_OilReleaseTitle,
|
||
lbl_S2_OilReleaseVal,
|
||
lbl_S2_OilReleaseUnit,
|
||
pnl_S2_ResidualOilBox,
|
||
lbl_S2_ResidualOilTitle,
|
||
lbl_S2_ResidualOilVal,
|
||
lbl_S2_ResidualOilUnit,
|
||
pnl_S2_QualityBox,
|
||
lbl_S2_QualityTitle,
|
||
lbl_S2_QualityVal);
|
||
|
||
ConfigureAlarmLine(pnl_Station1, lbl_S1_AlarmLabel, lbl_S1_AlarmVal);
|
||
ConfigureAlarmLine(pnl_Station2, lbl_S2_AlarmLabel, lbl_S2_AlarmVal);
|
||
}
|
||
finally
|
||
{
|
||
_isApplyingMetricLayout = false;
|
||
}
|
||
}
|
||
|
||
private void LayoutStationMetricCards(
|
||
Panel station,
|
||
Panel weightBox,
|
||
Label weightTitle,
|
||
Label weightValue,
|
||
Label weightUnit,
|
||
Panel waterBox,
|
||
Label waterTitle,
|
||
Label waterValue,
|
||
Label waterUnit,
|
||
Panel metricBox,
|
||
Panel densityBox,
|
||
Label densityTitle,
|
||
Label densityValue,
|
||
Label densityUnit,
|
||
Panel addOilBox,
|
||
Label addOilTitle,
|
||
Label addOilValue,
|
||
Label addOilUnit,
|
||
Panel extractOilBox,
|
||
Label extractOilTitle,
|
||
Label extractOilValue,
|
||
Label extractOilUnit,
|
||
Panel oilReleaseBox,
|
||
Label oilReleaseTitle,
|
||
Label oilReleaseValue,
|
||
Label oilReleaseUnit,
|
||
Panel residualOilBox,
|
||
Label residualOilTitle,
|
||
Label residualOilValue,
|
||
Label residualOilUnit,
|
||
Panel qualityBox,
|
||
Label qualityTitle,
|
||
Label qualityValue)
|
||
{
|
||
int margin = 20;
|
||
int gap = 16;
|
||
int stationWidth = Math.Max(620, station.ClientSize.Width);
|
||
int contentWidth = Math.Max(560, stationWidth - margin * 2);
|
||
int topCardWidth = Math.Max(250, (contentWidth - gap) / 2);
|
||
int metricCardWidth = Math.Max(165, (contentWidth - gap * 2) / 3);
|
||
int weightY = 262;
|
||
int metricY = 408;
|
||
int metricRowGap = 12;
|
||
|
||
ConfigureWeightCard(weightBox, weightTitle, weightValue, weightUnit, topCardWidth, 128, 41F);
|
||
weightBox.Location = new Point(margin, weightY);
|
||
ConfigureMetricCard(station, waterBox, waterTitle, waterValue, waterUnit, "最终重量", "KG", margin + topCardWidth + gap, weightY, topCardWidth, 128, 28F);
|
||
if (string.IsNullOrWhiteSpace(waterValue.Text) || waterValue.Text == "0.00")
|
||
waterValue.Text = "0.000";
|
||
|
||
metricBox.BackColor = Color.White;
|
||
metricBox.Location = new Point(margin, metricY);
|
||
metricBox.Size = new Size(contentWidth, 160);
|
||
if (metricBox.Parent != station)
|
||
station.Controls.Add(metricBox);
|
||
|
||
ConfigureMetricCard(metricBox, densityBox, densityTitle, densityValue, densityUnit, "油密度", "KG/L", 0, 0, metricCardWidth, 74, 18F);
|
||
ConfigureMetricCard(metricBox, addOilBox, addOilTitle, addOilValue, addOilUnit, "加油量", "L", metricCardWidth + gap, 0, metricCardWidth, 74, 18F);
|
||
ConfigureMetricCard(metricBox, extractOilBox, extractOilTitle, extractOilValue, extractOilUnit, "抽油量", "L", (metricCardWidth + gap) * 2, 0, metricCardWidth, 74, 18F);
|
||
ConfigureMetricCard(metricBox, oilReleaseBox, oilReleaseTitle, oilReleaseValue, oilReleaseUnit, "放油量", "L", 0, 74 + metricRowGap, metricCardWidth, 74, 18F);
|
||
ConfigureMetricCard(metricBox, residualOilBox, residualOilTitle, residualOilValue, residualOilUnit, "残油量", "L", metricCardWidth + gap, 74 + metricRowGap, metricCardWidth, 74, 18F);
|
||
ConfigureMetricCard(metricBox, qualityBox, qualityTitle, qualityValue, null, "水含量", "", (metricCardWidth + gap) * 2, 74 + metricRowGap, metricCardWidth, 74, 18F);
|
||
if (string.IsNullOrWhiteSpace(qualityValue.Text) || qualityValue.Text == "--" || qualityValue.Text == "0.00")
|
||
qualityValue.Text = "0.00 ppm";
|
||
}
|
||
|
||
private void LayoutStationHeaderSection(
|
||
Panel station,
|
||
Panel headerPanel,
|
||
Label titleLabel,
|
||
Label engineLabel,
|
||
Label engineValue,
|
||
Label modelLabel,
|
||
Label modelValue,
|
||
Label orderLabel,
|
||
Label orderValue,
|
||
Label palletLabel,
|
||
Label palletValue)
|
||
{
|
||
int margin = 20;
|
||
int columnGap = 26;
|
||
int labelWidth = 94;
|
||
int valueGap = 6;
|
||
int headerHeight = 44;
|
||
int rowHeight = 38;
|
||
int topY = headerHeight + 12;
|
||
int contentWidth = Math.Max(560, station.ClientSize.Width - margin * 2);
|
||
int columnWidth = (contentWidth - columnGap) / 2;
|
||
int valueWidth = Math.Max(120, columnWidth - labelWidth - valueGap);
|
||
|
||
headerPanel.Height = headerHeight;
|
||
titleLabel.Font = new Font("微软雅黑", 12.5F, FontStyle.Bold);
|
||
titleLabel.Padding = new Padding(18, 0, 0, 0);
|
||
|
||
ConfigureInfoPair(engineLabel, engineValue, margin, topY, labelWidth, valueWidth, rowHeight);
|
||
ConfigureInfoPair(modelLabel, modelValue, margin + columnWidth + columnGap, topY, labelWidth, valueWidth, rowHeight);
|
||
ConfigureInfoPair(orderLabel, orderValue, margin, topY + rowHeight, labelWidth, valueWidth, rowHeight);
|
||
ConfigureInfoPair(palletLabel, palletValue, margin + columnWidth + columnGap, topY + rowHeight, labelWidth, valueWidth, rowHeight);
|
||
}
|
||
|
||
private void ConfigureAlarmLine(Panel station, Label alarmLabel, Label alarmValue)
|
||
{
|
||
int y = 590;
|
||
alarmLabel.Font = new Font("微软雅黑", 11.5F, FontStyle.Bold);
|
||
alarmLabel.ForeColor = Color.FromArgb(107, 114, 128);
|
||
alarmLabel.Location = new Point(20, y);
|
||
alarmLabel.Size = new Size(96, 34);
|
||
alarmLabel.BringToFront();
|
||
|
||
alarmValue.Font = new Font("微软雅黑", 11.5F, FontStyle.Bold);
|
||
alarmValue.Location = new Point(116, y);
|
||
alarmValue.Size = new Size(Math.Max(240, station.ClientSize.Width - 136), 34);
|
||
alarmValue.BringToFront();
|
||
}
|
||
|
||
private void ConfigureWeightCard(System.Windows.Forms.Panel panel, System.Windows.Forms.Label titleLabel, System.Windows.Forms.Label valueLabel, System.Windows.Forms.Label unitLabel, int width, int height, float valueFontSize)
|
||
{
|
||
panel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
|
||
panel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||
panel.Size = new System.Drawing.Size(width, height);
|
||
|
||
titleLabel.AutoSize = false;
|
||
titleLabel.Font = new System.Drawing.Font("宋体", 14F, System.Drawing.FontStyle.Bold);
|
||
titleLabel.ForeColor = System.Drawing.Color.Black;
|
||
titleLabel.Location = new System.Drawing.Point(14, 8);
|
||
titleLabel.Size = new System.Drawing.Size(178, 30);
|
||
titleLabel.Text = "实时重量";
|
||
titleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||
if (titleLabel.Parent != panel)
|
||
panel.Controls.Add(titleLabel);
|
||
|
||
valueLabel.AutoSize = false;
|
||
valueLabel.Font = new System.Drawing.Font("Consolas", valueFontSize, System.Drawing.FontStyle.Bold);
|
||
valueLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(58)))), ((int)(((byte)(95)))));
|
||
valueLabel.Location = new System.Drawing.Point(34, 20);
|
||
valueLabel.Size = new System.Drawing.Size(Math.Max(150, width - 122), height - 40);
|
||
valueLabel.Text = "0.000";
|
||
valueLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||
if (valueLabel.Parent != panel)
|
||
panel.Controls.Add(valueLabel);
|
||
|
||
unitLabel.AutoSize = false;
|
||
unitLabel.Font = new System.Drawing.Font("微软雅黑", 18F, System.Drawing.FontStyle.Bold);
|
||
unitLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(114)))), ((int)(((byte)(128)))));
|
||
unitLabel.Location = new System.Drawing.Point(width - 82, height - 44);
|
||
unitLabel.Size = new System.Drawing.Size(66, 34);
|
||
unitLabel.Text = "KG";
|
||
unitLabel.TextAlign = System.Drawing.ContentAlignment.BottomRight;
|
||
if (unitLabel.Parent != panel)
|
||
panel.Controls.Add(unitLabel);
|
||
unitLabel.BringToFront();
|
||
titleLabel.BringToFront();
|
||
}
|
||
|
||
private void ConfigureMetricCard(System.Windows.Forms.Control parent, System.Windows.Forms.Panel panel, System.Windows.Forms.Label titleLabel, System.Windows.Forms.Label valueLabel, System.Windows.Forms.Label unitLabel, string title, string unit, int x, int y, int width, int height, float valueFontSize)
|
||
{
|
||
panel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
|
||
panel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||
panel.Location = new System.Drawing.Point(x, y);
|
||
panel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||
panel.Size = new System.Drawing.Size(width, height);
|
||
if (panel.Parent != parent)
|
||
parent.Controls.Add(panel);
|
||
|
||
titleLabel.AutoSize = false;
|
||
titleLabel.Font = new System.Drawing.Font("宋体", height >= 100 ? 14F : 10.5F, System.Drawing.FontStyle.Bold);
|
||
titleLabel.ForeColor = System.Drawing.Color.Black;
|
||
titleLabel.Location = new System.Drawing.Point(12, 7);
|
||
titleLabel.Size = new System.Drawing.Size(width - 24, height >= 100 ? 30 : 22);
|
||
titleLabel.Text = title;
|
||
titleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||
if (titleLabel.Parent != panel)
|
||
panel.Controls.Add(titleLabel);
|
||
|
||
valueLabel.AutoSize = false;
|
||
valueLabel.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||
valueLabel.BackColor = panel.BackColor;
|
||
valueLabel.Font = new System.Drawing.Font("微软雅黑", valueFontSize, System.Drawing.FontStyle.Bold);
|
||
valueLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(58)))), ((int)(((byte)(95)))));
|
||
int unitWidth = unit == "KG/L" ? 66 : (unit == "ppm" ? 58 : 38);
|
||
int valueRightPadding = unitLabel == null ? 16 : unitWidth + 24;
|
||
valueLabel.Location = new System.Drawing.Point(20, height >= 100 ? 24 : 20);
|
||
valueLabel.Size = new System.Drawing.Size(Math.Max(72, width - 20 - valueRightPadding), height >= 100 ? height - 40 : height - 24);
|
||
valueLabel.Text = NormalizeMetricValue(valueLabel.Text);
|
||
valueLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||
if (valueLabel.Parent != panel)
|
||
panel.Controls.Add(valueLabel);
|
||
|
||
if (unitLabel != null)
|
||
{
|
||
unitLabel.AutoSize = false;
|
||
unitLabel.Font = new System.Drawing.Font("微软雅黑", height >= 100 ? 18F : 11F, System.Drawing.FontStyle.Bold);
|
||
unitLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(114)))), ((int)(((byte)(128)))));
|
||
unitLabel.Location = new System.Drawing.Point(width - unitWidth - 12, height - (height >= 100 ? 42 : 28));
|
||
unitLabel.Size = new System.Drawing.Size(unitWidth, height >= 100 ? 30 : 20);
|
||
unitLabel.Text = unit;
|
||
unitLabel.TextAlign = System.Drawing.ContentAlignment.BottomRight;
|
||
if (unitLabel.Parent != panel)
|
||
panel.Controls.Add(unitLabel);
|
||
unitLabel.BringToFront();
|
||
}
|
||
|
||
titleLabel.BringToFront();
|
||
}
|
||
|
||
private void ConfigureInfoPair(Label titleLabel, Label valueLabel, int x, int y, int titleWidth, int valueWidth, int height)
|
||
{
|
||
titleLabel.Font = new Font("微软雅黑", 12F, FontStyle.Bold);
|
||
titleLabel.ForeColor = Color.FromArgb(107, 114, 128);
|
||
titleLabel.Location = new Point(x, y);
|
||
titleLabel.Size = new Size(titleWidth, height);
|
||
titleLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||
|
||
valueLabel.Font = new Font("微软雅黑", 13F, FontStyle.Bold);
|
||
valueLabel.ForeColor = Color.FromArgb(30, 58, 95);
|
||
valueLabel.Location = new Point(x + titleWidth + 6, y);
|
||
valueLabel.Size = new Size(valueWidth, height);
|
||
valueLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||
}
|
||
|
||
private string NormalizeMetricValue(string text)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(text))
|
||
return "0.00";
|
||
|
||
int colonIndex = text.IndexOf(':');
|
||
if (colonIndex >= 0 && colonIndex < text.Length - 1)
|
||
text = text.Substring(colonIndex + 1).Trim();
|
||
|
||
text = text.Trim();
|
||
return text == "—" ? "0.00" : text;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据 App.config 中的 WeighingType 配置切换工位标题
|
||
/// </summary>
|
||
private void ApplyWeighingType()
|
||
{
|
||
// 优先从Excel配置读取工位显示名(OpName → "OP10 — 称重位1")
|
||
var names = MesWorkForm.StationDisplayNames;
|
||
var opNames = MesWorkForm.StationOpNames;
|
||
string wType = System.Configuration.ConfigurationManager.AppSettings["WeighingType"] ?? "Hanging";
|
||
bool isGround = wType.Equals("Ground", StringComparison.OrdinalIgnoreCase);
|
||
|
||
string dn1 = null, dn2 = null;
|
||
string title1 = opNames.Count >= 1 && names.TryGetValue(opNames[0], out dn1)
|
||
? dn1
|
||
: (isGround ? "工位1 — 地面称重" : "工位1 — 放油前称重");
|
||
|
||
string title2 = opNames.Count >= 2 && names.TryGetValue(opNames[1], out dn2)
|
||
? dn2
|
||
: (isGround ? "工位2 — 地面称重" : "工位2 — 放油后称重");
|
||
|
||
lbl_S1_Title.Text = title1;
|
||
lbl_S2_Title.Text = title2;
|
||
}
|
||
|
||
// ====================================================================
|
||
// 信号灯创建(12个 = 4列×3行)
|
||
// ====================================================================
|
||
|
||
private void InitSignalLights()
|
||
{
|
||
_s1Signals = CreateSignalGrid(pnl_Station1, 20, 136, 198, 36, 22, 12.5F);
|
||
_s2Signals = CreateSignalGrid(pnl_Station2, 20, 136, 198, 36, 22, 12.5F);
|
||
}
|
||
|
||
private Panel[] CreateSignalGrid(Panel parent, int startX, int startY)
|
||
{
|
||
return CreateSignalGrid(parent, startX, startY, 200, 32, 20, 10F);
|
||
}
|
||
|
||
private Panel[] CreateSignalGrid(Panel parent, int startX, int startY, int colSpacing, int rowSpacing, int dotSize, float fontSize)
|
||
{
|
||
var signals = new Panel[12];
|
||
for (int i = 0; i < 12; i++)
|
||
{
|
||
int col = i % 4;
|
||
int row = i / 4;
|
||
int x = startX + col * colSpacing;
|
||
int y = startY + row * rowSpacing;
|
||
|
||
var dot = new Panel
|
||
{
|
||
BackColor = C_SignalOff,
|
||
Location = new Point(x, y + 2),
|
||
Size = new Size(dotSize, dotSize),
|
||
Name = $"sig_{parent.Name}_{i}"
|
||
};
|
||
dot.Paint += (s, e) =>
|
||
{
|
||
var p = (Panel)s;
|
||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||
using (var brush = new SolidBrush(p.BackColor))
|
||
{
|
||
e.Graphics.Clear(p.Parent.BackColor);
|
||
e.Graphics.FillEllipse(brush, 0, 0, p.Width - 1, p.Height - 1);
|
||
}
|
||
};
|
||
|
||
var lbl = new Label
|
||
{
|
||
Text = SignalNames[i],
|
||
Font = new Font("微软雅黑", fontSize, FontStyle.Bold),
|
||
ForeColor = Color.FromArgb(55, 65, 81),
|
||
Location = new Point(x + dotSize + 8, y),
|
||
AutoSize = true
|
||
};
|
||
|
||
parent.Controls.Add(dot);
|
||
parent.Controls.Add(lbl);
|
||
signals[i] = dot;
|
||
}
|
||
return signals;
|
||
}
|
||
|
||
private bool IsInDesignMode()
|
||
{
|
||
return LicenseManager.UsageMode == LicenseUsageMode.Designtime || DesignMode || (Site?.DesignMode ?? false);
|
||
}
|
||
|
||
private void ApplyDesignerPreviewLayout()
|
||
{
|
||
MirrorDesignerStationLayout(
|
||
pnl_Station2,
|
||
pnl_S2_WeightBox,
|
||
lbl_S2_WeightTitle,
|
||
lbl_S2_Weight,
|
||
lbl_S2_WeightUnit,
|
||
lbl_S2_Info1,
|
||
pnl_S2_MetricBox,
|
||
lbl_S2_DensityVal,
|
||
lbl_S2_Info2,
|
||
lbl_S2_ExtractOilVal,
|
||
lbl_S2_OilReleaseVal,
|
||
lbl_S2_ResidualOilVal,
|
||
lbl_S2_QualityVal,
|
||
lbl_S2_AlarmLabel,
|
||
lbl_S2_AlarmVal);
|
||
}
|
||
|
||
private void MirrorDesignerStationLayout(
|
||
Panel station,
|
||
Panel weightBox,
|
||
Label weightTitle,
|
||
Label weightValue,
|
||
Label weightUnit,
|
||
Label waterValue,
|
||
Panel metricBox,
|
||
Label densityValue,
|
||
Label addOilValue,
|
||
Label extractOilValue,
|
||
Label oilReleaseValue,
|
||
Label residualOilValue,
|
||
Label qualityValue,
|
||
Label alarmLabel,
|
||
Label alarmValue)
|
||
{
|
||
station.Controls.Remove(pnl_S2_WaterBox);
|
||
station.Controls.Add(waterValue);
|
||
station.Controls.Add(metricBox);
|
||
station.Controls.Add(weightBox);
|
||
station.Controls.Add(alarmLabel);
|
||
station.Controls.Add(alarmValue);
|
||
|
||
metricBox.Controls.Clear();
|
||
metricBox.Controls.Add(qualityValue);
|
||
metricBox.Controls.Add(residualOilValue);
|
||
metricBox.Controls.Add(oilReleaseValue);
|
||
metricBox.Controls.Add(densityValue);
|
||
metricBox.Controls.Add(extractOilValue);
|
||
metricBox.Controls.Add(addOilValue);
|
||
|
||
weightBox.Location = new Point(30, 401);
|
||
weightBox.Size = new Size(599, 164);
|
||
weightTitle.Location = new Point(15, 9);
|
||
weightTitle.Size = new Size(137, 30);
|
||
weightTitle.Font = new Font("宋体", 15F, FontStyle.Bold);
|
||
weightTitle.Text = "实时重量";
|
||
weightTitle.AutoSize = true;
|
||
weightValue.Location = new Point(114, 9);
|
||
weightValue.Size = new Size(327, 144);
|
||
weightValue.Font = new Font("Consolas", 54F, FontStyle.Bold);
|
||
weightValue.TextAlign = ContentAlignment.MiddleRight;
|
||
weightUnit.Location = new Point(456, 60);
|
||
weightUnit.Size = new Size(135, 87);
|
||
weightUnit.Font = new Font("微软雅黑", 20F, FontStyle.Bold);
|
||
weightUnit.TextAlign = ContentAlignment.BottomLeft;
|
||
|
||
waterValue.BackColor = Color.FromArgb(248, 250, 252);
|
||
waterValue.BorderStyle = BorderStyle.FixedSingle;
|
||
waterValue.Font = new Font("微软雅黑", 18F, FontStyle.Bold);
|
||
waterValue.Location = new Point(660, 401);
|
||
waterValue.Size = new Size(599, 164);
|
||
waterValue.Text = "最终重量:0.000 KG";
|
||
waterValue.TextAlign = ContentAlignment.MiddleCenter;
|
||
waterValue.AutoSize = false;
|
||
|
||
metricBox.Location = new Point(30, 594);
|
||
metricBox.Size = new Size(1230, 174);
|
||
metricBox.BackColor = Color.White;
|
||
|
||
ConfigureDesignerMetricLabel(densityValue, "油密度:0.00", 0, 0);
|
||
ConfigureDesignerMetricLabel(addOilValue, "加油量:0.00", 420, 0);
|
||
ConfigureDesignerMetricLabel(extractOilValue, "抽油量:0.00", 840, 0);
|
||
ConfigureDesignerMetricLabel(oilReleaseValue, "放油量:0.00", 0, 93);
|
||
ConfigureDesignerMetricLabel(residualOilValue, "残油量:0.00", 420, 93);
|
||
ConfigureDesignerMetricLabel(qualityValue, "水含量:0.00 ppm", 840, 93);
|
||
|
||
alarmLabel.Font = new Font("微软雅黑", 13F, FontStyle.Bold);
|
||
alarmLabel.Location = new Point(30, 792);
|
||
alarmLabel.Size = new Size(112, 42);
|
||
alarmValue.Font = new Font("微软雅黑", 13F, FontStyle.Bold);
|
||
alarmValue.Location = new Point(180, 792);
|
||
alarmValue.Size = new Size(1080, 42);
|
||
alarmValue.Text = "无报警";
|
||
}
|
||
|
||
private void ConfigureDesignerMetricLabel(Label label, string text, int x, int y)
|
||
{
|
||
label.BackColor = Color.FromArgb(248, 250, 252);
|
||
label.BorderStyle = BorderStyle.FixedSingle;
|
||
label.Font = new Font("微软雅黑", 13F, FontStyle.Bold);
|
||
label.ForeColor = Color.FromArgb(55, 65, 81);
|
||
label.Location = new Point(x, y);
|
||
label.Size = new Size(389, 80);
|
||
label.Text = text;
|
||
label.TextAlign = ContentAlignment.MiddleCenter;
|
||
label.AutoSize = false;
|
||
}
|
||
|
||
// ====================================================================
|
||
// PLC 轮询定时器 — 500ms
|
||
// ====================================================================
|
||
|
||
private void InitPLCTimer()
|
||
{
|
||
_tmrPLC = new Timer { Interval = 500 };
|
||
_tmrPLC.Tick += TmrPLC_Tick;
|
||
var tmrDelay = new Timer { Interval = 3000 };
|
||
tmrDelay.Tick += (s, e) =>
|
||
{
|
||
tmrDelay.Stop();
|
||
tmrDelay.Dispose();
|
||
|
||
var ops = MesWorkForm.StationOpNames;
|
||
if (ops != null && ops.Count >= 2)
|
||
{
|
||
_op1 = ops[0];
|
||
_op2 = ops[1];
|
||
}
|
||
else if (ops != null && ops.Count == 1)
|
||
{
|
||
_op1 = ops[0];
|
||
_op2 = ops[0];
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(_op1))
|
||
{
|
||
_tmrPLC.Start();
|
||
AppendLog($"PLC配置读取,工位1={_op1},工位2={_op2 ?? "无"}");
|
||
}
|
||
else
|
||
{
|
||
AppendLog("未检测到设备工位配置,PLC轮询未启动");
|
||
}
|
||
};
|
||
tmrDelay.Start();
|
||
}
|
||
|
||
private void TmrPLC_Tick(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (!string.IsNullOrEmpty(_op1))
|
||
PollStation(1, _op1, _s1Signals,
|
||
lbl_S1_Weight, lbl_S1_QualityVal, lbl_S1_Info2,
|
||
lbl_S1_PalletVal, lbl_S1_AlarmVal);
|
||
|
||
if (!string.IsNullOrEmpty(_op2))
|
||
PollStation(2, _op2, _s2Signals,
|
||
lbl_S2_Weight, lbl_S2_QualityVal, lbl_S2_Info2,
|
||
lbl_S2_PalletVal, lbl_S2_AlarmVal);
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 轮询单个工位的所有PLC数据
|
||
/// </summary>
|
||
private void PollStation(int station, string opName, Panel[] signals,
|
||
Label lblWeight, Label lblWater, Label lblOil,
|
||
Label lblPallet, Label lblAlarm)
|
||
{
|
||
try
|
||
{
|
||
// ── 前11个布尔信号灯 ──
|
||
for (int i = 0; i < 11; i++)
|
||
{
|
||
try
|
||
{
|
||
var val = PlcLinkForm.ReadPLC(SignalTagCodes[i], opName);
|
||
bool active = val != null && val.ToString().ToLower()
|
||
.Replace("true", "1").Replace("false", "0") == "1";
|
||
var newColor = active ? C_SignalOn : C_SignalOff;
|
||
if (signals[i].BackColor != newColor)
|
||
{
|
||
signals[i].BackColor = newColor;
|
||
signals[i].Invalidate();
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// ── 第12个:合格标志(21) — Int类型特殊处理 ──
|
||
try
|
||
{
|
||
var qVal = PlcLinkForm.ReadPLC(21, opName);
|
||
int qFlag = qVal != null ? Convert.ToInt32(qVal) : 0;
|
||
Color qColor = qFlag == 1 ? C_SignalOn : (qFlag == 2 ? C_SignalBad : C_SignalOff);
|
||
if (signals[11].BackColor != qColor)
|
||
{
|
||
signals[11].BackColor = qColor;
|
||
signals[11].Invalidate();
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// ── 实时重量(100220) ──
|
||
try
|
||
{
|
||
var wv = PlcLinkForm.ReadPLC(100220, opName);
|
||
if (wv != null)
|
||
{
|
||
decimal weight = Convert.ToDecimal(wv);
|
||
if (WeightCalibrationHelper.Apply(opName, weight, out decimal calibratedWeight, out _, out _))
|
||
{
|
||
weight = calibratedWeight;
|
||
}
|
||
string newTxt = Convert.ToDouble(weight).ToString("F3");
|
||
if (lblWeight.Text != newTxt) lblWeight.Text = newTxt;
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// ── 水含量(100180) ──
|
||
try
|
||
{
|
||
var sv = PlcLinkForm.ReadPLC(100180, opName);
|
||
if (sv != null)
|
||
lblWater.Text = $"{Convert.ToDouble(sv):F2} ppm";
|
||
}
|
||
catch { }
|
||
|
||
// ── 托盘号(80) ──
|
||
try
|
||
{
|
||
var pv = PlcLinkForm.ReadPLC(80, opName);
|
||
if (pv != null)
|
||
{
|
||
string pt = PLC_R.GetString_CleanGarbled(pv);
|
||
if (!string.IsNullOrEmpty(pt)) lblPallet.Text = pt;
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// ── 报警代码(112) — Int → 文本映射 ──
|
||
try
|
||
{
|
||
var av = PlcLinkForm.ReadPLC(112, opName);
|
||
int alarmCode = av != null ? Convert.ToInt32(av) : 0;
|
||
string alarmText = alarmCode >= 0 && alarmCode < AlarmCodeTexts.Length
|
||
? AlarmCodeTexts[alarmCode] : $"未知({alarmCode})";
|
||
lblAlarm.Text = alarmText;
|
||
lblAlarm.ForeColor = alarmCode > 0
|
||
? Color.FromArgb(239, 68, 68) // 红色
|
||
: Color.FromArgb(107, 114, 128); // 灰色
|
||
}
|
||
catch { }
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// ====================================================================
|
||
// 公共方法 — 供 PLC 通信层 / MIS_Funtion 调用
|
||
// ====================================================================
|
||
|
||
public void UpdateSignal(int station, int index, bool active)
|
||
{
|
||
var sigs = station == 1 ? _s1Signals : _s2Signals;
|
||
if (sigs != null && index >= 0 && index < sigs.Length)
|
||
{
|
||
sigs[index].BackColor = active ? C_SignalOn : C_SignalOff;
|
||
sigs[index].Invalidate();
|
||
}
|
||
}
|
||
|
||
public void UpdateWeight(int station, double weight)
|
||
{
|
||
var lbl = station == 1 ? lbl_S1_Weight : lbl_S2_Weight;
|
||
if (lbl.InvokeRequired)
|
||
lbl.Invoke(new Action(() => lbl.Text = weight.ToString("F3")));
|
||
else
|
||
lbl.Text = weight.ToString("F3");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新保存时采用的最终重量显示。
|
||
/// </summary>
|
||
public void UpdateFinalWeight(int station, decimal weight)
|
||
{
|
||
var lbl = station == 1 ? lbl_S1_Info1 : lbl_S2_Info1;
|
||
Action act = () => lbl.Text = weight.ToString("F3");
|
||
if (lbl.InvokeRequired) lbl.Invoke(act); else act();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清空保存结果相关显示。
|
||
/// </summary>
|
||
public void ClearSaveResult(int station)
|
||
{
|
||
Action act = () =>
|
||
{
|
||
if (station == 1)
|
||
{
|
||
lbl_S1_Info1.Text = "0.000";
|
||
lbl_S1_QualityVal.Text = "0.00 ppm";
|
||
lbl_S1_QualityVal.ForeColor = Color.FromArgb(30, 58, 95);
|
||
}
|
||
else
|
||
{
|
||
lbl_S2_Info1.Text = "0.000";
|
||
lbl_S2_QualityVal.Text = "0.00 ppm";
|
||
lbl_S2_QualityVal.ForeColor = Color.FromArgb(30, 58, 95);
|
||
}
|
||
};
|
||
if (InvokeRequired) Invoke(act); else act();
|
||
}
|
||
|
||
public void UpdateStationInfo(int station, string engineNo, string modelNo, string orderNo)
|
||
{
|
||
Action act = () =>
|
||
{
|
||
if (station == 1)
|
||
{
|
||
lbl_S1_EngineVal.Text = engineNo ?? "—";
|
||
lbl_S1_ModelVal.Text = modelNo ?? "—";
|
||
lbl_S1_OrderVal.Text = orderNo ?? "—";
|
||
}
|
||
else
|
||
{
|
||
lbl_S2_EngineVal.Text = engineNo ?? "—";
|
||
lbl_S2_ModelVal.Text = modelNo ?? "—";
|
||
lbl_S2_OrderVal.Text = orderNo ?? "—";
|
||
}
|
||
};
|
||
if (InvokeRequired) Invoke(act); else act();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新加油量显示
|
||
/// </summary>
|
||
public void UpdateOilInfo(int station, decimal addOilQty, decimal extractOilQty)
|
||
{
|
||
UpdateOilInfo(station, addOilQty, extractOilQty, 0, 0, 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新称重指标显示
|
||
/// </summary>
|
||
public void UpdateOilInfo(int station, decimal addOilQty, decimal extractOilQty, decimal density, decimal oilReleaseQty, decimal residualOilQty)
|
||
{
|
||
Action act = () =>
|
||
{
|
||
if (station == 1)
|
||
{
|
||
lbl_S1_Info2.Text = addOilQty.ToString("F2");
|
||
lbl_S1_ExtractOilVal.Text = extractOilQty.ToString("F2");
|
||
lbl_S1_DensityVal.Text = density.ToString("F4");
|
||
lbl_S1_OilReleaseVal.Text = oilReleaseQty.ToString("F2");
|
||
lbl_S1_ResidualOilVal.Text = residualOilQty.ToString("F2");
|
||
}
|
||
else
|
||
{
|
||
lbl_S2_Info2.Text = addOilQty.ToString("F2");
|
||
lbl_S2_ExtractOilVal.Text = extractOilQty.ToString("F2");
|
||
lbl_S2_DensityVal.Text = density.ToString("F4");
|
||
lbl_S2_OilReleaseVal.Text = oilReleaseQty.ToString("F2");
|
||
lbl_S2_ResidualOilVal.Text = residualOilQty.ToString("F2");
|
||
}
|
||
};
|
||
if (InvokeRequired) Invoke(act); else act();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新放油量和残油量显示
|
||
/// </summary>
|
||
public void UpdateOilResult(int station, decimal oilReleaseQty, decimal residualOilQty)
|
||
{
|
||
Action act = () =>
|
||
{
|
||
if (station == 1)
|
||
{
|
||
lbl_S1_OilReleaseVal.Text = oilReleaseQty.ToString("F2");
|
||
lbl_S1_ResidualOilVal.Text = residualOilQty.ToString("F2");
|
||
}
|
||
else
|
||
{
|
||
lbl_S2_OilReleaseVal.Text = oilReleaseQty.ToString("F2");
|
||
lbl_S2_ResidualOilVal.Text = residualOilQty.ToString("F2");
|
||
}
|
||
};
|
||
if (InvokeRequired) Invoke(act); else act();
|
||
}
|
||
|
||
public void UpdateAuxInfo(int station, string info1, string info2)
|
||
{
|
||
if (station == 1)
|
||
{
|
||
lbl_S1_Info1.Text = info1 ?? "";
|
||
lbl_S1_Info2.Text = info2 ?? "";
|
||
}
|
||
else
|
||
{
|
||
lbl_S2_Info1.Text = info1 ?? "";
|
||
lbl_S2_Info2.Text = info2 ?? "";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 追加操作日志(线程安全)
|
||
/// </summary>
|
||
public void AppendLog(string msg)
|
||
{
|
||
if (txt_Log == null) return;
|
||
if (txt_Log.InvokeRequired)
|
||
{
|
||
txt_Log.Invoke(new Action(() => AppendLog(msg)));
|
||
return;
|
||
}
|
||
if (txt_Log.Text.Length > 50000)
|
||
txt_Log.Text = txt_Log.Text.Substring(0, 30000);
|
||
txt_Log.Text = $"{DateTime.Now:HH:mm:ss} {msg}\r\n{txt_Log.Text}";
|
||
}
|
||
|
||
/// <summary>获取工位序号</summary>
|
||
public int GetStationIndex(string opName)
|
||
{
|
||
if (opName == _op1) return 1;
|
||
if (opName == _op2) return 2;
|
||
return 1;
|
||
}
|
||
}
|
||
}
|