Compare commits

...

10 Commits

Author SHA1 Message Date
0e3250c8b8 上传当前项目状态 2026-05-23 13:54:15 +08:00
XingCheng3
3b871f24b3 增加开机自启配置 2026-05-16 14:50:22 +08:00
XingCheng3
9aaa9a97e9 添加配置跳过重量前N个点 2026-05-16 13:53:41 +08:00
XingCheng3
e11c727114 增加码块订货号解析顺序配置项 2026-05-16 13:48:12 +08:00
XingCheng3
bb307f01db 隐藏主界面报警显示 2026-05-16 13:38:18 +08:00
XingCheng3
e0391dc32f 增加 兜底时记录订货号 增加常量 app.config配置文件 2026-05-14 13:06:53 +08:00
XingCheng3
fa121f4ab8 增加 称重分析 点位Tip弹窗显示点位详细信息 2026-05-13 01:35:16 +08:00
XingCheng3
185b197be2 简化无用参数 2026-05-13 01:25:44 +08:00
XingCheng3
8784a5a982 称重分析模块新增 2026-05-13 01:22:00 +08:00
XingCheng3
134a06a31d 改回水含量和合格结果UI 2026-05-13 01:09:21 +08:00
42 changed files with 24720 additions and 24305 deletions

13
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Framework: WC_GKJ_OIL",
"type": "clr",
"request": "launch",
"program": "${workspaceFolder}\\WC_GKJ_OIL_EXE\\WC_GKJ_OIL.exe",
"cwd": "${workspaceFolder}\\WC_GKJ_OIL_EXE",
"preLaunchTask": "build Debug"
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"dotnet.preferCSharpExtension": true
}

22
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build Debug",
"type": "process",
"command": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\MSBuild\\Current\\Bin\\MSBuild.exe",
"args": [
"${workspaceFolder}\\WC_GKJ_OIL_SLN.sln",
"/restore",
"/m",
"/p:Configuration=Debug",
"/p:Platform=Any CPU"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="IsDemoMesServer" value="1"/>
<add key="IsDemoMesServer" value="0"/>
<add key="BasicDataTableFile" value="SignalTable_CZ.xlsx"/>
<add key="IsAllowWrite" value="1"/>
<!--是启用写监控报错日志-->
@@ -9,22 +9,16 @@
<add key="IsWriteLog4" value="1"/>
<!--称重类型Ground=地面称重(两独立工位), Hanging=空中称重/吊称(放油前+放油后)-->
<add key="WeighingType" value="Hanging"/>
<!--码块订货号解析顺序0=订货号在前/流水号在后, 1=流水号在前/订货号在后-->
<add key="CodeBlockOrderNoReverse" value="0"/>
<!--逻辑工位映射必须与SignalTable_CZ.xlsx的设备类型对应表工位号一致-->
<add key="Station1OpName" value="ST04"/>
<add key="Station2OpName" value="ST05"/>
<!--数据库连接-->
<add key="ConnectionString" value="server=.,1333;database=MESBasicDB_WC_CZGKJ;uid=sa;pwd=126.com;Connection Reset=FALSE;Max Pool Size = 1000"/>
<add key="ConnectionString" value="server=LAPTOP-ELBDN3OJ\SQLEXPRESS;database=MESBasicDB_WC_CZGKJ;uid=sa;pwd=126.com;Connection Reset=FALSE;Max Pool Size = 1000"/>
<!--WebApi-->
<add key="WebApi_Port" value="9981"/>
<add key="WebApi_ReqUrl_WMS_Call" value="http://127.0.0.1:9981/api/wms/msgRequest_WMS_Call"/>
<add key="WebApi_ReqUrl_WMS_Return" value="http://127.0.0.1:9981/api/wms/msgRequest_WMS_Return"/>
<add key="WebUrl_AGV_HK" value=" http://127.0.0.1:9981/api/agv/MES_To_AGV_HK_Continue" />
<add key="WebUrl_AGV_HK_CTU" value="http://127.0.0.1:9981/api/agv/MES_To_AGV_HK_CTU" />
<add key="WebUrl_AGV_HK_agvCallback" value="http://127.0.0.1:9981/api/agv/agvCallback" />
<!--扫码枪1=启用, 0=禁用-->
<add key="IsUseBarcode" value="0"/>
<!--工位1扫码枪COM口如COM3-->
@@ -34,6 +28,13 @@
<!--扫码枪波特率-->
<add key="BarcodeBaudRate" value="9600"/>
<!--曲线采样间隔(ms)-->
<add key="CurveCollectorSampleIntervalMs" value="200"/>
<!--曲线稳定窗口点数-->
<add key="CurveStableWindowSize" value="20"/>
<!--最终重量计算时跳过前置称重点数0=不跳过-->
<add key="CurveStableSkipStartPointCount" value="50"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>

View File

@@ -41,12 +41,8 @@ namespace MesWork
public static int IsWriteLog4 = Convert.ToInt32(AppSettings["IsWriteLog4"]);
public static string ConnectionString = AppSettings["ConnectionString"];
public static string AuthorizationSet = AppSettings["AuthorizationSet"];
public static int WebApi_Port = Convert.ToInt32(AppSettings["WebApi_Port"]);
public static string WebApi_ReqUrl = AppSettings["WebApi_ReqUrl"];
/// <summary>
/// 当前工位号
/// </summary>
@@ -57,6 +53,11 @@ namespace MesWork
/// </summary>
public static string WeighingType = AppSettings["WeighingType"] ?? "Hanging";
/// <summary>
/// 码块订货号解析顺序0=订货号在前, 1=流水号在前
/// </summary>
public static int CodeBlockOrderNoReverse = int.TryParse(AppSettings["CodeBlockOrderNoReverse"], out var _cbonr) ? _cbonr : 0;
/// <summary>
/// 工位1现场工位号Hanging模式下代表放油前工位
/// </summary>
@@ -169,6 +170,8 @@ namespace MesWork
[STAThread]
static void Main()
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
new System.Threading.Mutex(true, $"MES_PWT_A95", out bool bCanRun);
if (!bCanRun)
{

View File

@@ -30,19 +30,28 @@ namespace MesWork
this.pnl_Card.SuspendLayout();
this.SuspendLayout();
//
// pnl_Card - 白色登录卡片
// pnl_Card
//
this.pnl_Card.BackColor = System.Drawing.Color.White;
this.pnl_Card.Controls.Add(this.lbl_Title);
this.pnl_Card.Controls.Add(this.lbl_SubTitle);
this.pnl_Card.Controls.Add(this.lbl_User);
this.pnl_Card.Controls.Add(this.txt_LoginID);
this.pnl_Card.Controls.Add(this.lbl_Pwd);
this.pnl_Card.Controls.Add(this.txt_userPassword);
this.pnl_Card.Controls.Add(this.btn_Login);
this.pnl_Card.Controls.Add(this.btn_Cancel);
this.pnl_Card.Controls.Add(this.lbl_Msg);
this.pnl_Card.Location = new System.Drawing.Point(310, 180);
this.pnl_Card.Name = "pnl_Card";
this.pnl_Card.Size = new System.Drawing.Size(400, 380);
this.pnl_Card.TabIndex = 0;
this.pnl_Card.Paint += new System.Windows.Forms.PaintEventHandler(this.pnl_Card_Paint);
//
// lbl_Title - 系统图标+名称
// lbl_Title
//
this.lbl_Title.Font = new System.Drawing.Font("微软雅黑", 20F, System.Drawing.FontStyle.Bold);
this.lbl_Title.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.lbl_Title.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(58)))), ((int)(((byte)(95)))));
this.lbl_Title.Location = new System.Drawing.Point(20, 30);
this.lbl_Title.Name = "lbl_Title";
this.lbl_Title.Size = new System.Drawing.Size(360, 40);
@@ -53,7 +62,7 @@ namespace MesWork
// lbl_SubTitle
//
this.lbl_SubTitle.Font = new System.Drawing.Font("微软雅黑", 11F);
this.lbl_SubTitle.ForeColor = System.Drawing.Color.FromArgb(107, 114, 128);
this.lbl_SubTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(114)))), ((int)(((byte)(128)))));
this.lbl_SubTitle.Location = new System.Drawing.Point(20, 72);
this.lbl_SubTitle.Name = "lbl_SubTitle";
this.lbl_SubTitle.Size = new System.Drawing.Size(360, 24);
@@ -64,7 +73,7 @@ namespace MesWork
// lbl_User
//
this.lbl_User.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_User.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_User.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(55)))), ((int)(((byte)(65)))), ((int)(((byte)(81)))));
this.lbl_User.Location = new System.Drawing.Point(40, 120);
this.lbl_User.Name = "lbl_User";
this.lbl_User.Size = new System.Drawing.Size(60, 24);
@@ -85,7 +94,7 @@ namespace MesWork
// lbl_Pwd
//
this.lbl_Pwd.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_Pwd.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_Pwd.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(55)))), ((int)(((byte)(65)))), ((int)(((byte)(81)))));
this.lbl_Pwd.Location = new System.Drawing.Point(40, 195);
this.lbl_Pwd.Name = "lbl_Pwd";
this.lbl_Pwd.Size = new System.Drawing.Size(60, 24);
@@ -104,9 +113,9 @@ namespace MesWork
this.txt_userPassword.Text = "admin";
this.txt_userPassword.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_userPassword_KeyDown);
//
// btn_Login - Steel Blue 主按钮
// btn_Login
//
this.btn_Login.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.btn_Login.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(144)))), ((int)(((byte)(217)))));
this.btn_Login.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Login.FlatAppearance.BorderSize = 0;
this.btn_Login.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
@@ -120,15 +129,14 @@ namespace MesWork
this.btn_Login.UseVisualStyleBackColor = false;
this.btn_Login.Click += new System.EventHandler(this.btn_Login_Click);
//
// btn_Cancel - 灰色边框按钮
// btn_Cancel
//
this.btn_Cancel.BackColor = System.Drawing.Color.White;
this.btn_Cancel.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Cancel.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(209, 213, 219);
this.btn_Cancel.FlatAppearance.BorderSize = 1;
this.btn_Cancel.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(209)))), ((int)(((byte)(213)))), ((int)(((byte)(219)))));
this.btn_Cancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Cancel.Font = new System.Drawing.Font("微软雅黑", 12F);
this.btn_Cancel.ForeColor = System.Drawing.Color.FromArgb(107, 114, 128);
this.btn_Cancel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(114)))), ((int)(((byte)(128)))));
this.btn_Cancel.Location = new System.Drawing.Point(210, 280);
this.btn_Cancel.Name = "btn_Cancel";
this.btn_Cancel.Size = new System.Drawing.Size(150, 42);
@@ -140,31 +148,19 @@ namespace MesWork
// lbl_Msg
//
this.lbl_Msg.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_Msg.ForeColor = System.Drawing.Color.FromArgb(239, 68, 68);
this.lbl_Msg.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(68)))), ((int)(((byte)(68)))));
this.lbl_Msg.Location = new System.Drawing.Point(40, 332);
this.lbl_Msg.Name = "lbl_Msg";
this.lbl_Msg.Size = new System.Drawing.Size(320, 24);
this.lbl_Msg.TabIndex = 8;
this.lbl_Msg.Visible = false;
//
// 添加控件到卡片
//
this.pnl_Card.Controls.Add(this.lbl_Title);
this.pnl_Card.Controls.Add(this.lbl_SubTitle);
this.pnl_Card.Controls.Add(this.lbl_User);
this.pnl_Card.Controls.Add(this.txt_LoginID);
this.pnl_Card.Controls.Add(this.lbl_Pwd);
this.pnl_Card.Controls.Add(this.txt_userPassword);
this.pnl_Card.Controls.Add(this.btn_Login);
this.pnl_Card.Controls.Add(this.btn_Cancel);
this.pnl_Card.Controls.Add(this.lbl_Msg);
//
// Frm_Login
//
this.AcceptButton = this.btn_Login;
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(240, 244, 248);
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(244)))), ((int)(((byte)(248)))));
this.ClientSize = new System.Drawing.Size(1024, 720);
this.Controls.Add(this.pnl_Card);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
@@ -174,6 +170,7 @@ namespace MesWork
this.pnl_Card.ResumeLayout(false);
this.pnl_Card.PerformLayout();
this.ResumeLayout(false);
}
#endregion

View File

@@ -152,6 +152,8 @@ namespace MesWork
MesWorkForm.Curr_UserPassWord = txt_userPassword.Text;
MesWorkForm.Curr_UserName = name;
B_DB_Opera.LoginState_Save(MesWorkForm.Curr_LoginID, MesWorkForm.Curr_UserName, perm);
// 记录登录日志
try { B_DB_Opera.SaveLog_Request(MesWorkForm.Curr_Station, Log_Type.ZK_Login, "", Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"用户[{name}({txt_LoginID.Text.Trim()})]登录系统", out long _); } catch { }

View File

@@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">

View File

@@ -15,40 +15,56 @@ namespace MesWork
public class B_DB_Opera
{
public static void Event_WorkStart(string opName, int tagTypeCodeID, string partType, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_WorkStart";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@PartType", partType),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
public static bool LoginState_Query(out LoginStateInfo loginState)
{
loginState = null;
try
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@机器名", Environment.MachineName)
};
SqlOperation.ExecuteStoredProcedure("系统登录状态_查询", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
loginState = new LoginStateInfo
{
LoginID = dt.Rows[0]["登录账号"]?.ToString() ?? "",
UserName = dt.Rows[0]["用户姓名"]?.ToString() ?? "",
Permission = dt.Rows[0]["权限"]?.ToString() ?? ""
};
return !string.IsNullOrWhiteSpace(loginState.LoginID) && !string.IsNullOrWhiteSpace(loginState.UserName);
}
}
catch { }
return false;
}
public static void Event_WorkEnd(string opName, int tagTypeCodeID, string partType
, string barCode, int qualityMask, int outCount, int duration
, string tagID)
public static void LoginState_Save(string loginID, string userName, string permission)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_WorkEnd";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@PartType", partType),
new SqlParameter("@BarCode", barCode),
new SqlParameter("@QualityMask", qualityMask),
new SqlParameter("@OutCount", outCount),
new SqlParameter("@Duration", duration),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
try
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@机器名", Environment.MachineName),
new SqlParameter("@登录账号", loginID ?? ""),
new SqlParameter("@用户姓名", userName ?? ""),
new SqlParameter("@权限", permission ?? "")
};
SqlOperation.ExecuteStoredProcedure("系统登录状态_保存", sqlParameter, out string err);
}
catch { }
}
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
public static void LoginState_Clear()
{
try
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@机器名", Environment.MachineName)
};
SqlOperation.ExecuteStoredProcedure("系统登录状态_清空", sqlParameter, out string err);
}
catch { }
}
@@ -101,36 +117,6 @@ namespace MesWork
}
public static void Event_AlarmAndTip_Start(string opName, int tagTypeCodeID, string alarmText, string tagID, int Alarm1_Tip2)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_AlarmAndTip_Start";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@AlarmText", alarmText),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID),
new SqlParameter("@Alarm1_Tip2", Alarm1_Tip2),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void MES_StatusChange(string opName, int StatusCode, string TriggerTime)
{
var procedureName = "设备管理_状态变化新增";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@opName", opName),
new SqlParameter("@StatusCode", StatusCode),
new SqlParameter("@TriggerTime", TriggerTime),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Query_AlarmData(string opName, out int count, out string text)
{
count = 0;
@@ -151,22 +137,6 @@ namespace MesWork
}
public static void Event_DeviceStatus_UseTime(string opName, string Onhours, string Workhours, string DeviceStatus)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_DeviceStatus_UseTime";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@Onhours", Onhours),
new SqlParameter("@Workhours", Workhours),
new SqlParameter("@DeviceStatus", DeviceStatus),
new SqlParameter("@ChangeTime",ChangeTime),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_DeviceStatus_Change(string opName, string DeviceStatus)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
@@ -445,12 +415,22 @@ namespace MesWork
/// <summary>
/// 更新过站记录为完成case 19 请求保存时调用)
/// </summary>
public static bool UpdateStationComplete(string engineNo, string stationNo, decimal weight)
public static bool UpdateStationComplete(string engineNo, string stationNo, decimal weight,
string modelNo = "", string orderNo = "", string palletNo = "", string stationName = "",
decimal addOilQty = 0, decimal extractOilQty = 0, decimal density = 0, string worker = "")
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重重量", weight)
new SqlParameter("@称重重量", weight),
new SqlParameter("@机型号", modelNo),
new SqlParameter("@工单号", orderNo),
new SqlParameter("@托盘号", palletNo),
new SqlParameter("@工位名称", stationName),
new SqlParameter("@加油量", addOilQty),
new SqlParameter("@抽油量", extractOilQty),
new SqlParameter("@油密度", density),
new SqlParameter("@操作者", worker)
};
SqlOperation.ExecuteStoredProcedure("过站记录_完成", sqlParameter, out DataTable dt, out string err);
return dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1";
@@ -521,6 +501,13 @@ namespace MesWork
}
public class LoginStateInfo
{
public string LoginID;
public string UserName;
public string Permission;
}
/// <summary>
/// 曲线采样点数据结构
/// </summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Threading;
@@ -30,8 +31,17 @@ namespace MesWork
private static readonly ConcurrentDictionary<string, CurveCollectorState> _curveCollectors
= new ConcurrentDictionary<string, CurveCollectorState>(StringComparer.OrdinalIgnoreCase);
private const int CurveCollectorSampleIntervalMs = 200;
private const int CurveStableWindowSize = 20;
private static readonly int CurveCollectorSampleIntervalMs = int.TryParse(
ConfigurationManager.AppSettings["CurveCollectorSampleIntervalMs"], out int _sampleIntervalMs)
? _sampleIntervalMs : 200;
private static readonly int CurveStableWindowSize = int.TryParse(
ConfigurationManager.AppSettings["CurveStableWindowSize"], out int _stableWindowSize)
? _stableWindowSize : 20;
private static readonly int CurveStableSkipStartPointCount = int.TryParse(
ConfigurationManager.AppSettings["CurveStableSkipStartPointCount"], out int _skipStartPointCount)
? _skipStartPointCount : 0;
/// <summary>
/// 允许工作后启动曲线采集,每个工位同一时间只保留一个采集段。
@@ -230,18 +240,19 @@ namespace MesWork
}
/// <summary>
/// 找连续20点中极差最小的一段,去掉一个最大值和一个最小值后取平均。
/// 跳过前置配置点数后,找连续稳定窗口中极差最小的一段,去掉一个最大值和一个最小值后取平均。
/// </summary>
private bool CurveCollector_CalculateStableWeight(List<decimal> weights, out decimal stableWeight, out int startSeq, out int endSeq)
{
stableWeight = 0;
startSeq = 0;
endSeq = 0;
if (weights == null || weights.Count < CurveStableWindowSize) return false;
int skipStartPointCount = Math.Max(0, CurveStableSkipStartPointCount);
if (weights == null || weights.Count - skipStartPointCount < CurveStableWindowSize) return false;
int bestStart = 0; // 0-based窗口起点保存时转成1-based序号
decimal bestRange = decimal.MaxValue;
for (int i = 0; i <= weights.Count - CurveStableWindowSize; i++)
for (int i = skipStartPointCount; i <= weights.Count - CurveStableWindowSize; i++)
{
decimal min = weights[i];
decimal max = weights[i];

View File

@@ -171,7 +171,6 @@ namespace MesWork
// 清除结果信息
WeighingPage?.UpdateOilResult(stIdx, 0, 0);
WeighingPage?.ClearSaveResult(stIdx);
}
catch (Exception err)
@@ -186,6 +185,7 @@ namespace MesWork
/// 支持格式:
/// 0/DHN2.5Q0219-36/D426E016767
/// DHH02K0014-400/B726E00584555
/// CodeBlockOrderNoReverse=0订货号/流水号CodeBlockOrderNoReverse=1流水号/订货号
/// </summary>
private bool TryConvertCodeBlockData(string codeBlock, out string orderNo, out string productNo)
{
@@ -200,8 +200,16 @@ namespace MesWork
string[] parts = normalizedCode.Split('/');
if (parts.Length >= 2)
{
orderNo = parts[parts.Length - 2].Trim();
productNo = parts[parts.Length - 1].Trim();
if (CodeBlockOrderNoReverse == 1)
{
productNo = parts[parts.Length - 2].Trim();
orderNo = parts[parts.Length - 1].Trim();
}
else
{
orderNo = parts[parts.Length - 2].Trim();
productNo = parts[parts.Length - 1].Trim();
}
}
else
{
@@ -320,9 +328,16 @@ namespace MesWork
decimal plcWeight = Convert.ToDecimal(ReadPLC(100140, opName)); // PLC锁定重量曲线无效时兜底使用
decimal weight = plcWeight; // 默认使用PLC锁定重量曲线算法成功后覆盖
decimal waterContent = Convert.ToDecimal(ReadPLC(100180, opName)); // 水含量
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName)); // 产品编号
if (TryConvertCodeBlockData(engineNo, out _, out string parsedProductNo))
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName)); // 原始代码串/产品编号
string modelNo = PLC_R.GetString_CleanGarbled(ReadPLC(11, opName)); // 机型号
string orderNo = PLC_R.GetString_CleanGarbled(ReadPLC(39, opName)); // 工单号/订货号
string palletNo = PLC_R.GetString_CleanGarbled(ReadPLC(80, opName)); // 托盘号
if (TryConvertCodeBlockData(engineNo, out string parsedOrderNo, out string parsedProductNo))
{
if (!string.IsNullOrWhiteSpace(parsedOrderNo))
{
orderNo = parsedOrderNo;
}
engineNo = parsedProductNo;
}
else if (string.IsNullOrWhiteSpace(engineNo))
@@ -352,9 +367,30 @@ namespace MesWork
}
long curveWeighingRecordId = 0;
B_DB_Opera.UpdateStationComplete(engineNo, opName, weight); // 过站记录保存最终采用重量
WeighingPage?.UpdateFinalWeight(WeighingPage?.GetStationIndex(opName) ?? 1, weight);
string weighType = GetWeighingType(opName);
decimal addOilQty = 0;
decimal extractOilQty = 0;
decimal density = 0;
decimal residualOilUpperLimit = 0;
decimal residualOilLowerLimit = 0;
if (!string.IsNullOrWhiteSpace(orderNo) &&
B_DB_Opera.QueryWorkpieceByOrderNo(orderNo, out string matchedModelNo, out decimal matchedAddOilQty,
out decimal matchedExtractOilQty, out decimal matchedDensity, out decimal matchedResidualOilUpperLimit,
out decimal matchedResidualOilLowerLimit))
{
modelNo = matchedModelNo;
addOilQty = matchedAddOilQty;
extractOilQty = matchedExtractOilQty;
density = matchedDensity;
residualOilUpperLimit = matchedResidualOilUpperLimit;
residualOilLowerLimit = matchedResidualOilLowerLimit;
}
if (!B_DB_Opera.UpdateStationComplete(engineNo, opName, weight, modelNo, orderNo, palletNo, weighType,
addOilQty, extractOilQty, density, Curr_UserName)) // 过站记录保存最终采用重量找不到则由SP补异常完成记录
{
LogWeighingFallback(opName, engineNo, "请求保存-过站", "未找到请求工作过站记录,已补异常过站完成记录", AID);
}
decimal oilReleaseQty = 0;
decimal residualOilQty = 0;
int finalQualityFlag = 1;
@@ -378,8 +414,8 @@ namespace MesWork
else
{
// 兜底机制请求工作阶段资料异常可能没有占位记录保存时补一条异常完成记录避免PLC等不到保存完成。
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "地面", engineNo, "", "", "",
0, weight, 0, 0, 0, 0, 0, 0, 0, 2, waterContent,
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "地面", engineNo, modelNo, orderNo, palletNo,
0, weight, addOilQty, extractOilQty, density, residualOilUpperLimit, residualOilLowerLimit, 0, 0, 2, waterContent,
opName, Curr_UserName, isComplete: 1);
finalQualityFlag = 2;
qualityMsg = "未找到请求工作占位记录,已按异常地面记录保存";
@@ -402,8 +438,8 @@ namespace MesWork
else
{
// 兜底机制:没有占位记录时补异常记录并完成握手,后续放油后仍可按异常链路继续。
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "空中", engineNo, "", "", "",
weight, 0, 0, 0, 0, 0, 0, 0, 0, 2, waterContent,
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "空中", engineNo, modelNo, orderNo, palletNo,
weight, 0, addOilQty, extractOilQty, density, residualOilUpperLimit, residualOilLowerLimit, 0, 0, 2, waterContent,
opName, Curr_UserName, isComplete: 1);
finalQualityFlag = 2;
qualityMsg = "未找到请求工作占位记录,已按异常放油前记录保存";
@@ -429,8 +465,8 @@ namespace MesWork
else
{
// 找不到放油前记录(可能直接进放油后工位):创建一条放油后记录并标记完成
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "空中放油后", engineNo, "", "", "",
0, weight, 0, 0, 0, 0, 0, 0, 0, 2, waterContent,
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "空中放油后", engineNo, modelNo, orderNo, palletNo,
0, weight, addOilQty, extractOilQty, density, residualOilUpperLimit, residualOilLowerLimit, 0, 0, 2, waterContent,
opName, Curr_UserName, isComplete: 1); // 异常兜底记录,质量直接判不合格
finalQualityFlag = 2;
qualityMsg = "未找到放油前记录";

View File

@@ -21,56 +21,69 @@ namespace MesWork
public MesWorkForm()
{
Frm_Login frm_Login = new Frm_Login();
if (frm_Login.ShowDialog() == DialogResult.OK)
if (!TryAutoLogin())
{
OnStart();
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
IsMdiContainer = false;
// 加载窗口图标
LoadWindowIcon();
// 设置用户名
lbl_UserName.Text = $"操作员:{MesWorkForm.Curr_UserName}";
// 标题追加工位号
if (!string.IsNullOrEmpty(Curr_Station))
Text = $"称重数据采集分析管理系统 - {Curr_Station}";
// 启动定时器
timer_OnLine_Show.Interval = 1000;
timer_OnLine_Show.Enabled = true;
timer_Clock.Interval = 1000;
timer_Clock.Enabled = true;
// 启动SQL在线检测
CommunicationCheckOnLine();
// 初始化扫码枪
if (MesWorkForm.IsUseBarcode == 1)
Frm_Login frm_Login = new Frm_Login();
if (frm_Login.ShowDialog() != DialogResult.OK)
{
BarcodeManager.Init();
BarcodeManager.OnBarcodeScanned = (opName, barcode) =>
{
// 在线程池中执行业务处理与PLC信号处理一致
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
try { Barcode_HandleScan(opName, barcode); }
catch { }
});
};
Process.GetCurrentProcess().Kill();
return;
}
}
// 默认选中"称重作业"
SetActiveNav(pnl_NavWeighing);
}
else
OnStart();
InitializeComponent();
InsertWeighingAnalysisNav();
CheckForIllegalCrossThreadCalls = false;
IsMdiContainer = false;
// 加载窗口图标
LoadWindowIcon();
// 设置用户名
lbl_UserName.Text = $"操作员:{MesWorkForm.Curr_UserName}";
// 标题追加工位号
if (!string.IsNullOrEmpty(Curr_Station))
Text = $"称重数据采集分析管理系统 - {Curr_Station}";
// 启动定时器
timer_OnLine_Show.Interval = 1000;
timer_OnLine_Show.Enabled = true;
timer_Clock.Interval = 1000;
timer_Clock.Enabled = true;
// 启动SQL在线检测
CommunicationCheckOnLine();
// 初始化扫码枪
if (MesWorkForm.IsUseBarcode == 1)
{
Process.GetCurrentProcess().Kill();
BarcodeManager.Init();
BarcodeManager.OnBarcodeScanned = (opName, barcode) =>
{
// 在线程池中执行业务处理与PLC信号处理一致
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
try { Barcode_HandleScan(opName, barcode); }
catch { }
});
};
}
// 默认选中"称重作业"
SetActiveNav(pnl_NavWeighing);
}
private bool TryAutoLogin()
{
if (!B_DB_Opera.LoginState_Query(out LoginStateInfo loginState)) return false;
Curr_LoginID = loginState.LoginID;
Curr_UserName = loginState.UserName;
Curr_UserPassWord = "";
return true;
}
// ====================================================================
@@ -161,7 +174,13 @@ namespace MesWork
string pageKey = "";
UserControl uc = null;
if (navTag.Contains("称重"))
if (navTag.Contains("称重分析"))
{
pageKey = "weighing_analysis";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_WeighingAnalysis();
}
else if (navTag.Contains("称重"))
{
pageKey = "weighing";
if (!_pageCache.ContainsKey(pageKey))
@@ -228,6 +247,43 @@ namespace MesWork
}
}
/// <summary>
/// 在统计分析后插入称重分析入口,减少对设计器文件的手工改动。
/// </summary>
private void InsertWeighingAnalysisNav()
{
var pnlNav = new Panel
{
BackColor = Color.FromArgb(30, 58, 95),
Cursor = Cursors.Hand,
Location = new Point(1, 470),
Name = "pnl_NavWeighingAnalysis",
Size = new Size(78, 64),
Tag = "📈\n称重分析"
};
var lblNav = new Label
{
AutoSize = false,
Cursor = Cursors.Hand,
Dock = DockStyle.Fill,
Font = new Font("微软雅黑", 9F),
ForeColor = Color.FromArgb(148, 163, 184),
Text = "📈\n称重分析",
TextAlign = ContentAlignment.MiddleCenter
};
pnlNav.Controls.Add(lblNav);
pnlNav.Click += Nav_Click;
lblNav.Click += Nav_Click;
pnl_NavSettings.Top += 66; // 称重分析占用一个导航位,后续菜单整体下移。
pnl_NavLog.Top += 66;
pnl_NavPLC.Top += 66;
pnl_SidebarScroll.Controls.Add(pnlNav);
pnlNav.BringToFront();
pnl_NavActiveIndicator.BringToFront();
}
// ====================================================================
// Timer Events
// ====================================================================
@@ -280,6 +336,7 @@ namespace MesWork
BarcodeManager.Shutdown();
B_DB_Opera.SaveLog_Request(Curr_Station, Log_Type.ZK_Login, "", Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"用户[{Curr_UserName}({Curr_LoginID})]退出称重系统", out long AID);
B_DB_Opera.LoginState_Clear();
Process.GetCurrentProcess().Kill();
}
}

View File

@@ -1,7 +1,11 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Security;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
namespace MesWork.Pages
{
@@ -10,6 +14,13 @@ namespace MesWork.Pages
/// </summary>
public partial class UC_SystemSettings : UserControl
{
private const string AutoStartKeyName = "WC_GKJ_OIL";
private const string AutoStartRunKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
private const string AutoStartTaskName = "WC_GKJ_OIL_AutoStart";
private bool _isLoadingAutoStart;
private CheckBox chk_AutoStart;
private Label lbl_AutoStartStatus;
// 工位1控件
private ComboBox cmb_Com1;
private Button btn_Connect1, btn_Disconnect1, btn_ManualSend1;
@@ -32,6 +43,7 @@ namespace MesWork.Pages
grp_Station2.Text = $" 工位2{station2} ";
grp_Scanner1.Text = $" {station1} 扫码枪 ";
grp_Scanner2.Text = $" {station2} 扫码枪 ";
BuildAutoStartPanel();
if (BarcodeManager.IsEnabled)
{
@@ -52,6 +64,46 @@ namespace MesWork.Pages
}
}
/// <summary>
/// 构建开机自启设置区域状态直接读取Windows当前用户启动项。
/// </summary>
private void BuildAutoStartPanel()
{
var grpAutoStart = new GroupBox
{
Text = " 开机自启 ",
Font = new Font("微软雅黑", 12F, FontStyle.Bold),
ForeColor = Color.FromArgb(30, 58, 95),
Location = new Point(30, 410),
Size = new Size(840, 120)
};
chk_AutoStart = new CheckBox
{
Text = "启用开机自启",
Font = new Font("微软雅黑", 11F, FontStyle.Bold),
ForeColor = Color.FromArgb(31, 41, 55),
Location = new Point(24, 36),
AutoSize = true,
Cursor = Cursors.Hand
};
chk_AutoStart.CheckedChanged += chk_AutoStart_CheckedChanged;
grpAutoStart.Controls.Add(chk_AutoStart);
lbl_AutoStartStatus = new Label
{
Text = "--",
Font = new Font("微软雅黑", 10F),
ForeColor = Color.FromArgb(75, 85, 99),
Location = new Point(24, 72),
AutoSize = true
};
grpAutoStart.Controls.Add(lbl_AutoStartStatus);
Controls.Add(grpAutoStart);
RefreshAutoStartState();
}
/// <summary>
/// 动态构建单个扫码枪面板的控件
/// </summary>
@@ -144,6 +196,232 @@ namespace MesWork.Pages
// ── 操作方法 ──
private string GetAutoStartCommand()
{
return Application.ExecutablePath;
}
private bool RunSchtasks(string arguments, out string output)
{
output = "";
try
{
var psi = new ProcessStartInfo
{
FileName = "schtasks.exe",
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (Process process = Process.Start(psi))
{
output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd();
process.WaitForExit();
return process.ExitCode == 0;
}
}
catch (Exception ex)
{
output = ex.Message;
return false;
}
}
private bool TryGetAutoStartTaskXml(out string taskXml, out string errMsg)
{
taskXml = "";
errMsg = "";
if (RunSchtasks($"/Query /TN \"{AutoStartTaskName}\" /XML", out string output))
{
taskXml = output;
return true;
}
if (IsTaskNotFoundOutput(output))
{
return true;
}
errMsg = output;
return false;
}
private bool IsTaskNotFoundOutput(string output)
{
return output.IndexOf("cannot find", StringComparison.OrdinalIgnoreCase) >= 0 ||
output.Contains("找不到") ||
output.Contains("不存在");
}
private void DeleteLegacyRegistryAutoStart()
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(AutoStartRunKeyPath, true))
{
key?.DeleteValue(AutoStartKeyName, false);
}
}
catch
{
}
}
private bool TryCreateAutoStartTask(out string errMsg)
{
errMsg = "";
string xmlPath = Path.Combine(Path.GetTempPath(), $"{AutoStartTaskName}.xml");
string exePath = GetAutoStartCommand();
string workDir = Path.GetDirectoryName(exePath);
string taskXml =
$@"<?xml version=""1.0"" encoding=""UTF-16""?>
<Task version=""1.2"" xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task"">
<RegistrationInfo>
<Description>潍柴称重工控机程序开机自启</Description>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
<Delay>PT30S</Delay>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id=""Author"">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context=""Author"">
<Exec>
<Command>{SecurityElement.Escape(exePath)}</Command>
<WorkingDirectory>{SecurityElement.Escape(workDir)}</WorkingDirectory>
</Exec>
</Actions>
</Task>";
try
{
File.WriteAllText(xmlPath, taskXml, System.Text.Encoding.Unicode);
if (RunSchtasks($"/Create /TN \"{AutoStartTaskName}\" /XML \"{xmlPath}\" /F", out string output))
{
DeleteLegacyRegistryAutoStart();
return true;
}
errMsg = output;
return false;
}
catch (Exception ex)
{
errMsg = ex.Message;
return false;
}
finally
{
try { if (File.Exists(xmlPath)) File.Delete(xmlPath); } catch { }
}
}
private bool IsCurrentProgramAutoStart(string taskXml)
{
return !string.IsNullOrWhiteSpace(taskXml) &&
taskXml.IndexOf(GetAutoStartCommand(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private bool TrySetAutoStart(bool enabled, out string errMsg)
{
errMsg = "";
if (enabled)
{
return TryCreateAutoStartTask(out errMsg);
}
DeleteLegacyRegistryAutoStart();
if (RunSchtasks($"/Delete /TN \"{AutoStartTaskName}\" /F", out string output))
{
return true;
}
if (IsTaskNotFoundOutput(output))
{
return true;
}
errMsg = output;
return false;
}
private void RefreshAutoStartState()
{
if (chk_AutoStart == null || lbl_AutoStartStatus == null) return;
_isLoadingAutoStart = true;
try
{
if (!TryGetAutoStartTaskXml(out string taskXml, out string errMsg))
{
chk_AutoStart.Checked = false;
lbl_AutoStartStatus.Text = $"读取任务计划失败:{errMsg}";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(239, 68, 68);
return;
}
bool isCurrentProgramAutoStart = IsCurrentProgramAutoStart(taskXml);
chk_AutoStart.Checked = isCurrentProgramAutoStart;
if (isCurrentProgramAutoStart)
{
lbl_AutoStartStatus.Text = $"已启用任务计划:{Application.ExecutablePath}";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(16, 185, 129);
}
else if (string.IsNullOrWhiteSpace(taskXml))
{
lbl_AutoStartStatus.Text = "未启用";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(107, 114, 128);
}
else
{
lbl_AutoStartStatus.Text = "任务计划存在但路径不是当前程序,请重新勾选修正";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(245, 158, 11);
}
}
finally
{
_isLoadingAutoStart = false;
}
}
private void chk_AutoStart_CheckedChanged(object sender, EventArgs e)
{
if (_isLoadingAutoStart) return;
bool enabled = chk_AutoStart.Checked;
if (!TrySetAutoStart(enabled, out string errMsg))
{
MessageBox.Show($"开机自启设置失败:{errMsg}", "设置失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
RefreshAutoStartState();
}
private void DoConnect(string opName, ComboBox cmb)
{
string com = cmb.SelectedItem?.ToString();

File diff suppressed because it is too large Load Diff

View File

@@ -253,9 +253,7 @@ namespace MesWork.Pages
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";
ConfigureMetricCard(station, waterBox, waterTitle, waterValue, waterUnit, "水含量", "ppm", margin + topCardWidth + gap, weightY, topCardWidth, 128, 28F);
metricBox.BackColor = Color.White;
metricBox.Location = new Point(margin, metricY);
@@ -268,9 +266,7 @@ namespace MesWork.Pages
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";
ConfigureMetricCard(metricBox, qualityBox, qualityTitle, qualityValue, null, "合格结果", "", (metricCardWidth + gap) * 2, 74 + metricRowGap, metricCardWidth, 74, 18F);
}
private void LayoutStationHeaderSection(
@@ -386,7 +382,7 @@ namespace MesWork.Pages
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 unitWidth = unit == "KG/L" ? 66 : (unit == "ppm" ? 76 : 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);
@@ -601,7 +597,7 @@ namespace MesWork.Pages
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.Text = "水含0.00 ppm";
waterValue.TextAlign = ContentAlignment.MiddleCenter;
waterValue.AutoSize = false;
@@ -614,7 +610,7 @@ namespace MesWork.Pages
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);
ConfigureDesignerMetricLabel(qualityValue, "合格结果:--", 840, 93);
alarmLabel.Font = new Font("微软雅黑", 13F, FontStyle.Bold);
alarmLabel.Location = new Point(30, 792);
@@ -683,12 +679,12 @@ namespace MesWork.Pages
{
if (!string.IsNullOrEmpty(_op1))
PollStation(1, _op1, _s1Signals,
lbl_S1_Weight, lbl_S1_QualityVal, lbl_S1_Info2,
lbl_S1_Weight, lbl_S1_Info1, 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_Weight, lbl_S2_Info1, lbl_S2_Info2,
lbl_S2_PalletVal, lbl_S2_AlarmVal);
}
catch { }
@@ -732,6 +728,9 @@ namespace MesWork.Pages
signals[11].BackColor = qColor;
signals[11].Invalidate();
}
var lblQuality = station == 1 ? lbl_S1_QualityVal : lbl_S2_QualityVal;
lblQuality.Text = qFlag == 1 ? "合格" : (qFlag == 2 ? "不合格" : "--");
lblQuality.ForeColor = qFlag == 2 ? C_SignalBad : Color.FromArgb(30, 58, 95);
}
catch { }
@@ -757,7 +756,7 @@ namespace MesWork.Pages
{
var sv = PlcLinkForm.ReadPLC(100180, opName);
if (sv != null)
lblWater.Text = $"{Convert.ToDouble(sv):F2} ppm";
lblWater.Text = Convert.ToDouble(sv).ToString("F2");
}
catch { }
@@ -813,39 +812,6 @@ namespace MesWork.Pages
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 = () =>

View File

@@ -0,0 +1,545 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
using MesWork;
namespace MesWork.Pages
{
/// <summary>
/// 称重分析页面:查询称重记录,查看曲线点位和最终重量取值依据。
/// </summary>
public class UC_WeighingAnalysis : UserControl
{
private readonly Panel pnl_QueryBar = new Panel();
private readonly DateTimePicker dtp_Start = new DateTimePicker();
private readonly DateTimePicker dtp_End = new DateTimePicker();
private readonly TextBox txt_ProductNo = new TextBox();
private readonly TextBox txt_ModelNo = new TextBox();
private readonly TextBox txt_OrderNo = new TextBox();
private readonly Button btn_Query = new Button();
private readonly SplitContainer split_Main = new SplitContainer();
private readonly Panel pnl_Chart = new Panel();
private readonly Panel pnl_Info = new Panel();
private readonly DataGridView dgv_Data = new DataGridView();
private readonly Label lbl_Title = new Label();
private readonly Label lbl_FinalWeight = new Label();
private readonly Label lbl_Window = new Label();
private readonly Label lbl_Method = new Label();
private readonly Label lbl_Product = new Label();
private readonly Label lbl_Sample = new Label();
private readonly Label lbl_Status = new Label();
private readonly ToolTip tip_Point = new ToolTip();
private readonly List<decimal> _points = new List<decimal>();
private PointF[] _chartPoints = new PointF[0];
private int _finalStartSeq;
private int _finalEndSeq;
private decimal? _finalWeight;
private int _sampleIntervalMs;
private int _lastTipSeq;
private static readonly Color ClrPrimary = Color.FromArgb(74, 144, 217);
private static readonly Color ClrPrimaryDark = Color.FromArgb(30, 58, 95);
private static readonly Color ClrAccent = Color.FromArgb(245, 158, 11);
private static readonly Color ClrGood = Color.FromArgb(5, 150, 105);
private static readonly Color ClrGrid = Color.FromArgb(229, 231, 235);
private static readonly Color ClrText = Color.FromArgb(55, 65, 81);
private static readonly Color ClrMuted = Color.FromArgb(107, 114, 128);
public UC_WeighingAnalysis()
{
InitializeLayout();
InitGrid();
dtp_Start.Value = DateTime.Today.AddDays(-30);
dtp_End.Value = DateTime.Today;
SetHint(txt_ProductNo, "产品编号...");
SetHint(txt_ModelNo, "机型号...");
SetHint(txt_OrderNo, "工单号...");
pnl_Chart.Paint += pnl_Chart_Paint;
pnl_Chart.Resize += (s, e) => pnl_Chart.Invalidate();
pnl_Chart.MouseMove += pnl_Chart_MouseMove;
pnl_Chart.MouseLeave += (s, e) => HidePointTip();
}
private void InitializeLayout()
{
BackColor = Color.FromArgb(245, 247, 250);
Dock = DockStyle.Fill;
pnl_QueryBar.BackColor = Color.White;
pnl_QueryBar.Dock = DockStyle.Top;
pnl_QueryBar.Height = 56;
pnl_QueryBar.Padding = new Padding(16, 0, 16, 0);
Controls.Add(split_Main);
Controls.Add(pnl_QueryBar);
AddQueryLabel("从", 16);
InitDatePicker(dtp_Start, 40);
AddQueryLabel("到", 180);
InitDatePicker(dtp_End, 204);
InitTextBox(txt_ProductNo, 365, 150);
InitTextBox(txt_ModelNo, 525, 130);
InitTextBox(txt_OrderNo, 665, 150);
InitButton();
split_Main.Dock = DockStyle.Fill;
split_Main.Orientation = Orientation.Horizontal;
split_Main.SplitterDistance = 430;
split_Main.SplitterWidth = 6;
split_Main.Panel1.Padding = new Padding(12, 8, 12, 4);
split_Main.Panel2.Padding = new Padding(12, 4, 12, 12);
split_Main.Panel1.Controls.Add(pnl_Chart);
split_Main.Panel1.Controls.Add(pnl_Info);
split_Main.Panel2.Controls.Add(dgv_Data);
pnl_Info.BackColor = Color.FromArgb(248, 250, 252);
pnl_Info.Dock = DockStyle.Right;
pnl_Info.Width = 340;
pnl_Info.Padding = new Padding(20, 12, 20, 12);
pnl_Info.Controls.Add(lbl_Status);
pnl_Info.Controls.Add(lbl_Sample);
pnl_Info.Controls.Add(lbl_Product);
pnl_Info.Controls.Add(lbl_Method);
pnl_Info.Controls.Add(lbl_Window);
pnl_Info.Controls.Add(lbl_FinalWeight);
InitInfoLabel(lbl_FinalWeight, 18, FontStyle.Bold, ClrGood, 18);
InitInfoLabel(lbl_Window, 11, FontStyle.Bold, ClrPrimaryDark, 78);
InitInfoLabel(lbl_Method, 10, FontStyle.Regular, ClrText, 124);
InitInfoLabel(lbl_Product, 10, FontStyle.Regular, ClrText, 210);
InitInfoLabel(lbl_Sample, 10, FontStyle.Regular, ClrText, 246);
InitInfoLabel(lbl_Status, 10, FontStyle.Regular, ClrMuted, 282);
pnl_Chart.BackColor = Color.White;
pnl_Chart.Dock = DockStyle.Fill;
pnl_Chart.Padding = new Padding(18, 8, 18, 8);
pnl_Chart.Controls.Add(lbl_Title);
lbl_Title.Dock = DockStyle.Top;
lbl_Title.Height = 34;
lbl_Title.Font = new Font("微软雅黑", 12F, FontStyle.Bold);
lbl_Title.ForeColor = ClrPrimaryDark;
lbl_Title.Text = "称重曲线与最终取值依据";
lbl_Title.TextAlign = ContentAlignment.MiddleLeft;
}
private void AddQueryLabel(string text, int x)
{
var label = new Label
{
AutoSize = true,
Font = new Font("微软雅黑", 10F),
ForeColor = ClrText,
Location = new Point(x, 18),
Text = text
};
pnl_QueryBar.Controls.Add(label);
}
private void InitDatePicker(DateTimePicker picker, int x)
{
picker.Font = new Font("微软雅黑", 10F);
picker.Format = DateTimePickerFormat.Short;
picker.Location = new Point(x, 13);
picker.Size = new Size(130, 25);
pnl_QueryBar.Controls.Add(picker);
}
private void InitTextBox(TextBox textBox, int x, int width)
{
textBox.BorderStyle = BorderStyle.FixedSingle;
textBox.Font = new Font("微软雅黑", 10F);
textBox.Location = new Point(x, 13);
textBox.Size = new Size(width, 27);
pnl_QueryBar.Controls.Add(textBox);
}
private void InitButton()
{
btn_Query.BackColor = ClrPrimary;
btn_Query.Cursor = Cursors.Hand;
btn_Query.FlatAppearance.BorderSize = 0;
btn_Query.FlatStyle = FlatStyle.Flat;
btn_Query.Font = new Font("微软雅黑", 10F, FontStyle.Bold);
btn_Query.ForeColor = Color.White;
btn_Query.Location = new Point(830, 12);
btn_Query.Size = new Size(110, 30);
btn_Query.Text = "查询分析";
btn_Query.UseVisualStyleBackColor = false;
btn_Query.Click += btn_Query_Click;
pnl_QueryBar.Controls.Add(btn_Query);
}
private void InitInfoLabel(Label label, float fontSize, FontStyle style, Color color, int y)
{
label.Font = new Font("微软雅黑", fontSize, style);
label.ForeColor = color;
label.Location = new Point(20, y);
label.Size = new Size(300, y == 124 ? 72 : 32);
label.TextAlign = ContentAlignment.MiddleLeft;
}
private void InitGrid()
{
dgv_Data.AllowUserToAddRows = false;
dgv_Data.AllowUserToDeleteRows = false;
dgv_Data.AllowUserToResizeRows = false;
dgv_Data.AutoGenerateColumns = false;
dgv_Data.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dgv_Data.BackgroundColor = Color.White;
dgv_Data.BorderStyle = BorderStyle.None;
dgv_Data.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal;
dgv_Data.ColumnHeadersHeight = 40;
dgv_Data.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
dgv_Data.ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(232, 237, 242);
dgv_Data.ColumnHeadersDefaultCellStyle.ForeColor = ClrPrimaryDark;
dgv_Data.ColumnHeadersDefaultCellStyle.Font = new Font("微软雅黑", 10F, FontStyle.Bold);
dgv_Data.DefaultCellStyle.Font = new Font("微软雅黑", 10F);
dgv_Data.DefaultCellStyle.ForeColor = Color.FromArgb(31, 41, 55);
dgv_Data.DefaultCellStyle.SelectionBackColor = Color.FromArgb(219, 234, 254);
dgv_Data.DefaultCellStyle.SelectionForeColor = ClrPrimaryDark;
dgv_Data.DefaultCellStyle.Padding = new Padding(6, 0, 6, 0);
dgv_Data.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(248, 250, 252);
dgv_Data.Dock = DockStyle.Fill;
dgv_Data.EnableHeadersVisualStyles = false;
dgv_Data.GridColor = ClrGrid;
dgv_Data.MultiSelect = false;
dgv_Data.ReadOnly = true;
dgv_Data.RowHeadersVisible = false;
dgv_Data.RowTemplate.Height = 36;
dgv_Data.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgv_Data.SelectionChanged += dgv_Data_SelectionChanged;
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "检测时间", DataPropertyName = "测试时间", FillWeight = 95 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "产品编号", DataPropertyName = "发动机号", FillWeight = 110 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "工单号", DataPropertyName = "订单号", FillWeight = 95 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "机型号", DataPropertyName = "机型号", FillWeight = 65 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "称重类型", DataPropertyName = "称重类型", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "工位号", DataPropertyName = "设备编号", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "最终重量", DataPropertyName = "最终重量", FillWeight = 70, DefaultCellStyle = new DataGridViewCellStyle { Format = "F3" } });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "取点区间", DataPropertyName = "取点区间", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "采样点数", DataPropertyName = "采样点数", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "操作员", DataPropertyName = "操作员", FillWeight = 60 });
}
private void SetHint(TextBox txt, string hint)
{
txt.Tag = hint;
txt.Text = hint;
txt.ForeColor = Color.FromArgb(156, 163, 175);
txt.GotFocus += (s, e) => { if (txt.Text == hint) { txt.Text = ""; txt.ForeColor = ClrPrimaryDark; } };
txt.LostFocus += (s, e) => { if (string.IsNullOrWhiteSpace(txt.Text)) { txt.Text = hint; txt.ForeColor = Color.FromArgb(156, 163, 175); } };
}
private string GetHintValue(TextBox txt)
{
return txt.Text == txt.Tag?.ToString() ? "" : txt.Text.Trim();
}
private void btn_Query_Click(object sender, EventArgs e)
{
QueryData();
}
private void QueryData()
{
try
{
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", GetHintValue(txt_ProductNo)),
new SqlParameter("@机型号", GetHintValue(txt_ModelNo)),
new SqlParameter("@工单号", GetHintValue(txt_OrderNo)),
new SqlParameter("@开始时间", dtp_Start.Value.ToString("yyyy-MM-dd")),
new SqlParameter("@结束时间", dtp_End.Value.AddDays(1).ToString("yyyy-MM-dd"))
};
SqlOperation.ExecuteStoredProcedure("称重分析_查询", parms, out DataTable dt, out string err);
if (!string.IsNullOrWhiteSpace(err))
throw new Exception(err);
dgv_Data.DataSource = dt;
if (dt == null || dt.Rows.Count == 0)
{
ClearCurve("查询范围内无曲线数据");
MessageBox.Show("查询范围内无称重曲线记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void dgv_Data_SelectionChanged(object sender, EventArgs e)
{
if (dgv_Data.CurrentRow?.DataBoundItem is DataRowView rowView)
{
LoadCurve(rowView.Row);
}
}
private void LoadCurve(DataRow row)
{
_points.Clear();
HidePointTip();
string pointValues = row.Table.Columns.Contains("点位值") ? row["点位值"]?.ToString() : "";
foreach (string item in (pointValues ?? "").Split('|'))
{
if (decimal.TryParse(item, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal value))
_points.Add(value);
}
_finalStartSeq = ToInt(row, "最终取点开始序号");
_finalEndSeq = ToInt(row, "最终取点结束序号");
_sampleIntervalMs = ToInt(row, "采样间隔ms");
_finalWeight = ToNullableDecimal(row, "最终重量");
string productNo = GetText(row, "发动机号");
string stationNo = GetText(row, "设备编号");
lbl_FinalWeight.Text = _finalWeight.HasValue ? $"最终重量 {_finalWeight.Value:F3} kg" : "最终重量 —";
lbl_Window.Text = _finalStartSeq > 0 && _finalEndSeq > 0
? $"依据点位 第 {_finalStartSeq} - {_finalEndSeq} 点"
: "依据点位 未记录";
lbl_Method.Text = "计算方式 取连续20点中极差最小的一段去掉一个最大值和一个最小值剩余18点求平均。";
lbl_Product.Text = $"产品编号 {productNo}";
lbl_Sample.Text = $"工位号 {stationNo} 采样点数 {_points.Count} 间隔 {_sampleIntervalMs}ms";
lbl_Status.Text = _points.Count > 0 ? "曲线状态 已加载,可追溯最终取值区间" : "曲线状态 无点位数据";
pnl_Chart.Invalidate();
}
private void ClearCurve(string msg)
{
_points.Clear();
HidePointTip();
_finalWeight = null;
_finalStartSeq = 0;
_finalEndSeq = 0;
lbl_FinalWeight.Text = "最终重量 —";
lbl_Window.Text = "依据点位 —";
lbl_Method.Text = "计算方式 —";
lbl_Product.Text = "";
lbl_Sample.Text = "";
lbl_Status.Text = msg;
pnl_Chart.Invalidate();
}
private int ToInt(DataRow row, string column)
{
if (!row.Table.Columns.Contains(column) || row[column] == DBNull.Value) return 0;
int.TryParse(row[column].ToString(), out int value);
return value;
}
private decimal? ToNullableDecimal(DataRow row, string column)
{
if (!row.Table.Columns.Contains(column) || row[column] == DBNull.Value) return null;
return decimal.TryParse(row[column].ToString(), out decimal value) ? value : (decimal?)null;
}
private string GetText(DataRow row, string column)
{
return row.Table.Columns.Contains(column) && row[column] != DBNull.Value ? row[column].ToString() : "";
}
private void pnl_Chart_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
Rectangle rect = pnl_Chart.ClientRectangle;
using (var bg = new SolidBrush(Color.White))
g.FillRectangle(bg, rect);
int padL = 70, padR = 36, padT = 54, padB = 54;
int chartW = rect.Width - padL - padR;
int chartH = rect.Height - padT - padB;
if (chartW < 80 || chartH < 80) return;
if (_points.Count == 0)
{
_chartPoints = new PointF[0];
DrawCenterText(g, rect, "选择一条称重记录后显示曲线");
return;
}
decimal minDec = _points.Min();
decimal maxDec = _points.Max();
double minVal = (double)minDec;
double maxVal = (double)maxDec;
double range = maxVal - minVal;
if (range < 0.001) range = Math.Max(Math.Abs(maxVal) * 0.1, 1);
minVal -= range * 0.15;
maxVal += range * 0.15;
DrawGrid(g, padL, padT, chartW, chartH, minVal, maxVal);
_chartPoints = BuildPointLocations(padL, padT, chartW, chartH, minVal, maxVal);
DrawHighlightWindow(g, _chartPoints, padT, chartH);
DrawCurve(g, _chartPoints);
DrawPoints(g, _chartPoints);
DrawFinalLine(g, padL, padT, chartW, chartH, minVal, maxVal);
}
private void DrawGrid(Graphics g, int padL, int padT, int chartW, int chartH, double minVal, double maxVal)
{
using (var axisPen = new Pen(Color.FromArgb(200, 210, 220), 1))
using (var gridPen = new Pen(ClrGrid, 1) { DashStyle = DashStyle.Dot })
using (var font = new Font("微软雅黑", 9F))
using (var brush = new SolidBrush(ClrMuted))
{
g.DrawLine(axisPen, padL, padT, padL, padT + chartH);
g.DrawLine(axisPen, padL, padT + chartH, padL + chartW, padT + chartH);
for (int i = 0; i <= 5; i++)
{
int y = padT + chartH - (int)(chartH * i / 5.0);
if (i > 0) g.DrawLine(gridPen, padL + 1, y, padL + chartW, y);
string text = (minVal + (maxVal - minVal) * i / 5.0).ToString("F2");
SizeF size = g.MeasureString(text, font);
g.DrawString(text, font, brush, padL - size.Width - 6, y - size.Height / 2);
}
}
}
private PointF[] BuildPointLocations(int padL, int padT, int chartW, int chartH, double minVal, double maxVal)
{
var points = new PointF[_points.Count];
for (int i = 0; i < _points.Count; i++)
{
float x = _points.Count == 1 ? padL + chartW / 2f : padL + chartW * i / (float)(_points.Count - 1);
float y = padT + chartH - (float)(chartH * (((double)_points[i] - minVal) / (maxVal - minVal)));
points[i] = new PointF(x, y);
}
return points;
}
private void DrawHighlightWindow(Graphics g, PointF[] points, int padT, int chartH)
{
if (_finalStartSeq <= 0 || _finalEndSeq <= 0 || _finalStartSeq > points.Length) return;
int startIndex = Math.Max(0, _finalStartSeq - 1);
int endIndex = Math.Min(points.Length - 1, _finalEndSeq - 1);
float x1 = points[startIndex].X;
float x2 = points[endIndex].X;
using (var brush = new SolidBrush(Color.FromArgb(46, 245, 158, 11)))
g.FillRectangle(brush, x1, padT, Math.Max(2, x2 - x1), chartH);
using (var pen = new Pen(ClrAccent, 1.6f) { DashStyle = DashStyle.Dash })
{
g.DrawLine(pen, x1, padT, x1, padT + chartH);
g.DrawLine(pen, x2, padT, x2, padT + chartH);
}
}
private void DrawCurve(Graphics g, PointF[] points)
{
if (points.Length < 2) return;
using (var shadow = new Pen(Color.FromArgb(34, 74, 144, 217), 5f) { LineJoin = LineJoin.Round })
using (var line = new Pen(ClrPrimary, 2.2f) { LineJoin = LineJoin.Round })
{
g.DrawLines(shadow, points);
g.DrawLines(line, points);
}
}
private void DrawPoints(Graphics g, PointF[] points)
{
int step = Math.Max(1, points.Length / 120);
for (int i = 0; i < points.Length; i += step)
{
Brush brush = IsInFinalWindow(i + 1) ? new SolidBrush(ClrAccent) : new SolidBrush(ClrPrimary);
using (brush)
g.FillEllipse(brush, points[i].X - 3, points[i].Y - 3, 6, 6);
}
}
private void DrawFinalLine(Graphics g, int padL, int padT, int chartW, int chartH, double minVal, double maxVal)
{
if (!_finalWeight.HasValue) return;
float y = padT + chartH - (float)(chartH * (((double)_finalWeight.Value - minVal) / (maxVal - minVal)));
if (y < padT || y > padT + chartH) return;
using (var pen = new Pen(ClrGood, 1.8f) { DashStyle = DashStyle.Dash })
using (var font = new Font("微软雅黑", 9F, FontStyle.Bold))
using (var brush = new SolidBrush(ClrGood))
{
g.DrawLine(pen, padL, y, padL + chartW, y);
string text = $"最终 {_finalWeight.Value:F3} kg";
SizeF size = g.MeasureString(text, font);
g.DrawString(text, font, brush, padL + chartW - size.Width - 4, y - size.Height - 4);
}
}
private bool IsInFinalWindow(int seq)
{
return _finalStartSeq > 0 && _finalEndSeq > 0 && seq >= _finalStartSeq && seq <= _finalEndSeq;
}
private void pnl_Chart_MouseMove(object sender, MouseEventArgs e)
{
int index = FindNearestPointIndex(e.Location);
if (index < 0)
{
HidePointTip();
return;
}
int seq = index + 1;
if (seq == _lastTipSeq) return;
_lastTipSeq = seq;
string finalText = IsInFinalWindow(seq) ? "\r\n取值依据最终采用区间内" : "";
string tipText = $"第 {seq} 点\r\n重量{_points[index]:F3} kg\r\n时间{FormatElapsedTime(seq)}{finalText}";
tip_Point.Show(tipText, pnl_Chart, e.Location.X + 16, e.Location.Y + 16, 3000);
}
private int FindNearestPointIndex(Point mouse)
{
if (_chartPoints.Length == 0 || _points.Count == 0) return -1;
int nearestIndex = -1;
double nearestDistance = double.MaxValue;
for (int i = 0; i < _chartPoints.Length; i++)
{
double dx = _chartPoints[i].X - mouse.X;
double dy = _chartPoints[i].Y - mouse.Y;
double distance = dx * dx + dy * dy;
if (distance < nearestDistance)
{
nearestDistance = distance;
nearestIndex = i;
}
}
return nearestDistance <= 144 ? nearestIndex : -1; // 鼠标离点 12px 内才显示,避免误触。
}
private string FormatElapsedTime(int seq)
{
int interval = _sampleIntervalMs > 0 ? _sampleIntervalMs : 200;
int totalMs = Math.Max(0, seq - 1) * interval; // 第1点按采集起点0ms估算。
int seconds = totalMs / 1000;
int milliseconds = totalMs % 1000;
return $"{seconds}秒{milliseconds:D3}毫秒";
}
private void HidePointTip()
{
_lastTipSeq = 0;
tip_Point.Hide(pnl_Chart);
}
private void DrawCenterText(Graphics g, Rectangle rect, string text)
{
using (var font = new Font("微软雅黑", 14F))
using (var brush = new SolidBrush(Color.FromArgb(156, 163, 175)))
{
SizeF size = g.MeasureString(text, font);
g.DrawString(text, font, brush, (rect.Width - size.Width) / 2, (rect.Height - size.Height) / 2);
}
}
}
}

View File

@@ -252,6 +252,9 @@
<Compile Include="Pages\UC_Statistics.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Pages\UC_WeighingAnalysis.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Pages\UC_Statistics.Designer.cs">
<DependentUpon>UC_Statistics.cs</DependentUpon>
</Compile>

View File

@@ -1,6 +1,7 @@
using MesWork;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
@@ -221,7 +222,11 @@ namespace PLMTEST
req.ContentType = "application/json";
// 添加自定义 Header
req.Headers.Add("Authorization", MesWorkForm.AuthorizationSet);
string authorization = ConfigurationManager.AppSettings["AuthorizationSet"];
if (!string.IsNullOrWhiteSpace(authorization))
{
req.Headers.Add("Authorization", authorization);
}
req.Headers.Add("uuid", Guid.NewGuid().ToString().ToUpper());
#region Post