首次提交

This commit is contained in:
XingCheng3
2026-05-11 11:09:04 +08:00
commit 79c0f9cd43
260 changed files with 33504 additions and 0 deletions

BIN
DLL/BasicData.dll Normal file

Binary file not shown.

BIN
DLL/BouncyCastle.Crypto.dll Normal file

Binary file not shown.

BIN
DLL/ConLink.dll Normal file

Binary file not shown.

BIN
DLL/ConLink19.dll Normal file

Binary file not shown.

BIN
DLL/DataLinkMesWork.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
DLL/EnBaseM.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
DLL/MQTTnet.dll Normal file

Binary file not shown.

BIN
DLL/MW_Log.dll Normal file

Binary file not shown.

BIN
DLL/MesWork.Control.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
DLL/MySql.Data.dll Normal file

Binary file not shown.

17803
DLL/MySql.Data.xml Normal file

File diff suppressed because it is too large Load Diff

BIN
DLL/NPOI.OOXML.dll Normal file

Binary file not shown.

BIN
DLL/NPOI.OpenXml4Net.dll Normal file

Binary file not shown.

BIN
DLL/NPOI.OpenXmlFormats.dll Normal file

Binary file not shown.

BIN
DLL/NPOI.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
DLL/Newtonsoft.Json.dll Normal file

Binary file not shown.

BIN
DLL/Opc.Ua.Client.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
DLL/Opc.Ua.Core.dll Normal file

Binary file not shown.

BIN
DLL/OpcUaHelper.dll Normal file

Binary file not shown.

BIN
DLL/Rhino3dm.dll Normal file

Binary file not shown.

BIN
DLL/Robot.Common.dll Normal file

Binary file not shown.

BIN
DLL/Robot.MessageBus.dll Normal file

Binary file not shown.

BIN
DLL/RobotMapper.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
DLL/Robots.dll Normal file

Binary file not shown.

BIN
DLL/S7.Net.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
DLL/Serilog.dll Normal file

Binary file not shown.

BIN
DLL/SqlCmd.dll Normal file

Binary file not shown.

BIN
DLL/System.Buffers.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
DLL/System.Net.Http.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
DLL/System.Web.Cors.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
DLL/System.Web.Http.dll Normal file

Binary file not shown.

BIN
DLL/Ubiety.Dns.Core.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
DLL/Zstandard.Net.dll Normal file

Binary file not shown.

BIN
DLL/log4net.dll Normal file

Binary file not shown.

BIN
DLL/netstandard.dll Normal file

Binary file not shown.

57
MW_Log/01_MW_Log.csproj Normal file
View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1AC112C4-2400-4D03-A26D-2E3ACE6FE6D4}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MW_Log</RootNamespace>
<AssemblyName>MW_Log</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net">
<HintPath>..\DLL\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="LoggerHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="log4net.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

49
MW_Log/LoggerHelper.cs Normal file
View File

@@ -0,0 +1,49 @@
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MW_Log
{
/// <summary>
/// LoggerHelper
/// </summary>
public class LoggerHelper
{
/// <summary>
/// LogInfo
/// </summary>
private static readonly ILog LogInfo = LogManager.GetLogger("LogInfo");
/// <summary>
/// 记录Info日志
/// </summary>
/// <param name="msg"></param>
/// <param name="ex"></param>
public static void Info(string type, string msg, Exception ex = null)
{
try
{
msg = $"【类别:{type}】\r\n【内容】{msg}";
if (ex != null)
{
LogInfo.Info(msg, ex);
}
else
{
LogInfo.Info(msg);
}
}
catch (Exception err)
{
}
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("MW_Log")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MW_Log")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("1ac112c4-2400-4d03-a26d-2e3ace6fe6d4")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]

Binary file not shown.

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<!--Info日志-->
<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
<!--日志文件存放位置,可以为绝对路径也可以为相对路径 -->
<param name="File" value="C:\MWLOG" />
<!--是否支持分割文件-->
<param name="AppendToFile" value="true" />
<param name="MaxFileSize" value="10240" />
<!--当日志文件达到MaxFileSize大小就自动创建备份文件。-->
<param name="MaxSizeRollBackups" value="100" />
<!-- 当将日期作为日志文件的名字时必须将staticLogFileName的值设置为false -->
<param name="StaticLogFileName" value="false" />
<!-- 日志文件的命名规则 -->
<param name="DatePattern" value="\\yyyy_MM\\yyyy_MM_dd'.log'" />
<!--日志文件的记录形式-->
<param name="RollingStyle" value="Date" />
<!--日志文件的布局格式:%newline【%date】【级别%-5level】【线程ID%thread】%n【内容】%message%newline %n%n-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="【%date】【线程ID%thread】%message%newline %n%n" />
</layout>
</appender>
<!--Info日志-->
<logger name="LogInfo">
<level value="INFO" />
<appender-ref ref="InfoAppender" />
</logger>
</log4net>
</configuration>

Binary file not shown.

35
MW_Log/log4net.config Normal file
View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<!--Info日志-->
<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
<!--日志文件存放位置,可以为绝对路径也可以为相对路径 -->
<param name="File" value="C:\MWLOG" />
<!--是否支持分割文件-->
<param name="AppendToFile" value="true" />
<param name="MaxFileSize" value="10240" />
<!--当日志文件达到MaxFileSize大小就自动创建备份文件。-->
<param name="MaxSizeRollBackups" value="100" />
<!-- 当将日期作为日志文件的名字时必须将staticLogFileName的值设置为false -->
<param name="StaticLogFileName" value="false" />
<!-- 日志文件的命名规则 -->
<param name="DatePattern" value="\\yyyy_MM\\yyyy_MM_dd'.log'" />
<!--日志文件的记录形式-->
<param name="RollingStyle" value="Date" />
<!--日志文件的布局格式:%newline【%date】【级别%-5level】【线程ID%thread】%n【内容】%message%newline %n%n-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="【%date】【线程ID%thread】%message%newline %n%n" />
</layout>
</appender>
<!--Info日志-->
<logger name="LogInfo">
<level value="INFO" />
<appender-ref ref="InfoAppender" />
</logger>
</log4net>
</configuration>

38
SCADA/App.config Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="IsDemoMesServer" value="1"/>
<add key="BasicDataTableFile" value="SignalTable_CZ.xlsx"/>
<add key="IsAllowWrite" value="1"/>
<!--是启用写监控报错日志-->
<add key="IsWriteMonitorLog" value="0"/>
<add key="IsWriteLog4" value="1"/>
<!--称重类型Ground=地面称重(两独立工位), Hanging=空中称重/吊称(放油前+放油后)-->
<add key="WeighingType" value="Hanging"/>
<!--数据库连接-->
<add key="ConnectionString" value="server=.,1333;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"/>
<!--OP10扫码枪COM口如COM3-->
<add key="OP10BarCOM" value="COM2"/>
<!--OP20扫码枪COM口如COM4-->
<add key="OP20BarCOM" value="COM3"/>
<!--扫码枪波特率-->
<add key="BarcodeBaudRate" value="9600"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
</configuration>

317
SCADA/Core/AppConfig.cs Normal file
View File

@@ -0,0 +1,317 @@
using HslCommunication.WebSocket;
using Newtonsoft.Json;
using NPOI.POIFS.Properties;
using NPOI.SS.Formula.Functions;
using OpcBasic;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Threading;
using System.Windows.Forms;
using WebApi;
using static System.Configuration.ConfigurationManager;
namespace MesWork
{
/// <summary>
///
/// </summary>
public partial class Temp {
public static MesWorkForm mesWorkForm = null;
}
/// <summary>
/// 配置信息
/// </summary>
public partial class MesWorkForm
{
public static int workerThreads = 4000;
public static int completionPortThreads = 2000;
public static string BasicDataTableFile = AppSettings["BasicDataTableFile"];
public static int IsDemoMesServer = Convert.ToInt32(AppSettings["IsDemoMesServer"]);
public static int IsAllowWrite = Convert.ToInt32(AppSettings["IsAllowWrite"]);
public static int IsWriteMonitorLog = Convert.ToInt32(AppSettings["IsWriteMonitorLog"]);
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>
public static string Curr_Station = AppSettings["Curr_Station"] ?? "";
/// <summary>
/// 称重类型Ground=地面称重, Hanging=空中称重/吊称
/// </summary>
public static string WeighingType = AppSettings["WeighingType"] ?? "Hanging";
/// <summary>
/// 是否启用扫码枪1=启用, 0=禁用
/// </summary>
public static int IsUseBarcode = int.TryParse(AppSettings["IsUseBarcode"], out var _iub) ? _iub : 0;
/// <summary>
/// OP10扫码枪COM口如COM3空则不启用
/// </summary>
public static string OP10BarCOM = AppSettings["OP10BarCOM"] ?? "";
/// <summary>
/// OP20扫码枪COM口如COM4空则不启用
/// </summary>
public static string OP20BarCOM = AppSettings["OP20BarCOM"] ?? "";
/// <summary>
/// 扫码枪波特率
/// </summary>
public static int BarcodeBaudRate = int.TryParse(AppSettings["BarcodeBaudRate"], out var _bbr) ? _bbr : 9600;
/// <summary>
/// 设备工位名称列表(如 OP10, OP20在OnStart中从设备表加载
/// </summary>
public static System.Collections.Generic.List<string> StationOpNames = new System.Collections.Generic.List<string>();
/// <summary>
/// 工位显示名称映射opName → "OP10 — 称重位1"在OnStart中从设备表加载
/// </summary>
public static System.Collections.Generic.Dictionary<string, string> StationDisplayNames
= new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 称重作业页面实例引用供MIS_Funtion推送交互消息
/// </summary>
public static Pages.UC_Weighing WeighingPage;
public static Color color_Faw = Color.FromArgb(0, 44, 101);//蓝色大众0,44,101
public static Color color_Good = Color.FromArgb(132, 185, 235);//浅蓝,自定义
public static Color color_Wait = Color.FromArgb(240, 230, 140);//淡黄,显示等待状态
public static Color color_Bad = SystemColors.ActiveBorder;//灰色,显示操作过程
public static Color color_Grey = Color.FromArgb(192, 192, 192);//灰色Table底色
public static Color color_Green = Color.FromArgb(22, 161, 136);//绿色
public static Color color_Yellow = Color.FromArgb(246, 227, 74);//黄色
public static Color color_Red = Color.FromArgb(255, 0, 0);//红色
public static Color color_Blue_FJ = Color.FromArgb(19, 34, 122);//深蓝
public static Color color_White_FJ = Color.White;//白色
public static Color color_Control = SystemColors.Control;//控件默认颜色
public static bool OnLine_Sql = false;
public static string Curr_UserName = "";
public static string Curr_LoginID = "";
public static string Curr_UserPassWord = "";
/// <summary>
/// WebApi
/// </summary>
public static InitServer initServer = null;
/// <summary>
/// 监控数据的所有变化,从这里产生
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public override void OPCTagData_TagDataOnChangeMIS(object sender, DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(MIS_Funtion), e);
}
/// <summary>
/// 定时触发检测PLC在线状态周期3 s
/// </summary>
/// <param name="sender"></param>
/// <param name="eState"></param>
public override void PlcStateEvent(object sender, DeviceDriver_BasicData.PlcStateEvetnArgs eState)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(MIS_Device), eState);
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <param name="location"></param>
/// <param name="content"></param>
public override void E_WriteLog(Enum_LogType t, string location, string content)
{
if (content .Contains("GetTagIDByTagTypeCodeID()"))
{
return;
}
WriteLog_Info(t.ToString(), $"<[<{location}>]>{content}");
}
/// <summary>
///
/// </summary>
[STAThread]
static void Main()
{
new System.Threading.Mutex(true, $"MES_PWT_A95", out bool bCanRun);
if (!bCanRun)
{
MessageBox.Show("MES采集系统 已经在运行!不可重复启动!");
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(Temp.mesWorkForm = new MesWorkForm());
}
/// <summary>
///
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private string Hello_You_All_Never_Know(string[] args)
{
return Guid.NewGuid().ToString() + Guid.NewGuid().ToString();
}
/// <summary>
/// 写入信号
/// </summary>
/// <param name="tagTypeCodeID"></param>
/// <param name="ststionName"></param>
/// <param name="value"></param>
public static void WritePLC_IF(int tagTypeCodeID,string ststionName,object value)
{
if (IsAllowWrite == 1)
{
WritePLC(tagTypeCodeID, ststionName, value);
}
}
/// <summary>
/// 写日志
/// 2023.10.30.llx
/// </summary>
public static void WriteLog_Info(string type, string msg, Exception ex = null)
{
if (IsWriteLog4 == 1)
{
MW_Log.LoggerHelper.Info(type, msg, ex);
}
}
private void CommunicationCheckOnLine()
{
new Thread(new ThreadStart(delegate ()
{
while (true)
{
try
{
MesWorkForm.OnLine_Sql = DataLinkMesWork.SQLCommon.ExecuteDataTable("select getdate()", ConnectionString, out DataTable dt, out string err);
}
catch (Exception) { }
Thread.Sleep(5000);
}
}))
{ IsBackground = true }.Start();
}
private void Loop_SelectAlarmShow()
{
while (true)
{
try
{
B_DB_Opera.Query_AlarmData(Curr_Station, out int count, out string text);
if (count > 0)
{
label_Alarm.Text = $"({count}) {text}";
panel_Alarm.BackColor = Color.Red;
}
else
{
label_Alarm.Text = $"";
panel_Alarm.BackColor = Color.FromArgb(30, 58, 95);
}
}
catch (Exception err)
{
}
Thread.Sleep(1000);
}
}
// Loop_ReadRealPageShow 已移除旧UI控件引用PLC信号显示将在 UC_Weighing 中实现
public void ShowSignal(Control c,string opName)
{
if (!c.Name.Contains("_Show_"))
{
return;
}
var tagTypeCodeID = Convert.ToInt32(c.Name.Split('_')[3]);
if (c is TextBox)
{
try
{
c.Text = PlcLinkForm.ReadPLC(tagTypeCodeID, opName).ToString();
}
catch (Exception err)
{
c.Text = "不存在该信号!";
}
}
}
public void ShowSignal(Control c, int tagTypeCodeID, string opName)
{
if (c is Panel)
{
try
{
c.BackColor = PlcLinkForm.ReadPLC(tagTypeCodeID, opName).ToString().ToLower().Replace("true", "1").Replace("false", "0") == "1" ? Color.Lime : color_Grey;
}
catch (Exception err)
{
c.BackColor = color_Grey;
}
}
}
public static ConcurrentDictionary<string, TagFormat> ReadPLC_OpName(string opName)
{
try
{
var res = new ConcurrentDictionary<string, TagFormat>();
var tagAdrListTagFormat0 = PlcLinkForm.ReadPLC_GroupData_SingleTag(opName);
foreach (var item in tagAdrListTagFormat0)
{
var key = item.Key;
var value = (TagFormat)item.Value;
res.TryAdd(key, value);
}
return res;
}
catch (Exception err)
{
return null;
}
}
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MesWork
{
/// <summary>
/// 扫码枪管理器 — 管理最多2把扫码枪的生命周期和事件分发
/// </summary>
public static class BarcodeManager
{
/// <summary>
/// 扫码枪实例字典key = opNameOP10/OP20
/// </summary>
public static Dictionary<string, BarcodeScanner> Scanners { get; } = new Dictionary<string, BarcodeScanner>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 是否启用扫码枪功能
/// </summary>
public static bool IsEnabled => MesWorkForm.IsUseBarcode == 1;
/// <summary>
/// 扫码事件回调(由外部设置,通常指向 MIS_Funtion 的处理方法)
/// </summary>
public static Action<string, string> OnBarcodeScanned;
/// <summary>
/// 初始化扫码枪(读取配置并创建实例)
/// </summary>
public static void Init()
{
if (!IsEnabled) return;
int baudRate = MesWorkForm.BarcodeBaudRate;
// OP10
string op10Com = MesWorkForm.OP10BarCOM?.Trim();
if (!string.IsNullOrEmpty(op10Com))
{
var scanner = new BarcodeScanner("OP10", baudRate);
scanner.BarcodeReceived += HandleBarcodeReceived;
scanner.Log += HandleLog;
Scanners["OP10"] = scanner;
scanner.Connect(op10Com);
}
// OP20
string op20Com = MesWorkForm.OP20BarCOM?.Trim();
if (!string.IsNullOrEmpty(op20Com))
{
var scanner = new BarcodeScanner("OP20", baudRate);
scanner.BarcodeReceived += HandleBarcodeReceived;
scanner.Log += HandleLog;
Scanners["OP20"] = scanner;
scanner.Connect(op20Com);
}
}
/// <summary>
/// 统一扫码事件处理
/// </summary>
private static void HandleBarcodeReceived(string opName, string barcode)
{
OnBarcodeScanned?.Invoke(opName, barcode);
}
/// <summary>
/// 扫码枪日志 → 系统日志 + 称重页面
/// </summary>
private static void HandleLog(string message)
{
MesWorkForm.WriteLog_Info("BarcodeScanner", message);
MesWorkForm.WeighingPage?.AppendLog(message);
}
/// <summary>
/// 手动发送条码(模拟扫码触发)
/// </summary>
public static void ManualSend(string opName, string barcode)
{
// 直接走事件回调,和自动扫码一样的路径
if (!string.IsNullOrWhiteSpace(barcode))
HandleBarcodeReceived(opName, barcode.Trim());
}
/// <summary>
/// 释放所有扫码枪
/// </summary>
public static void Shutdown()
{
foreach (var scanner in Scanners.Values)
{
try { scanner.Dispose(); } catch { }
}
Scanners.Clear();
}
/// <summary>
/// 获取所有可用COM口
/// </summary>
public static string[] GetAvailablePorts()
{
try { return System.IO.Ports.SerialPort.GetPortNames(); }
catch { return new string[0]; }
}
}
}

View File

@@ -0,0 +1,323 @@
using System;
using System.IO.Ports;
using System.Text;
using System.Threading.Tasks;
namespace MesWork
{
/// <summary>
/// 单把扫码枪的串口通信封装
/// 包含数据接收(缓冲+空闲提交)、防抖、断线自动重连
/// </summary>
public class BarcodeScanner : IDisposable
{
// ── 基本属性 ──
/// <summary>关联工位号OP10/OP20</summary>
public string OpName { get; }
/// <summary>当前COM口</summary>
public string ComPort { get; private set; }
/// <summary>是否已连接</summary>
public bool IsConnected
{
get { try { return _serial != null && _serial.IsOpen; } catch { return false; } }
}
/// <summary>最近一次扫到的条码</summary>
public string LastBarcode { get; private set; } = "";
/// <summary>最近一次扫码时间</summary>
public DateTime? LastScanTime { get; private set; }
// ── 事件 ──
/// <summary>扫码完成事件opName, barcode</summary>
public event Action<string, string> BarcodeReceived;
/// <summary>日志事件message由外部接入系统日志</summary>
public event Action<string> Log;
// ── 私有字段 ──
private SerialPort _serial;
private readonly int _baudRate;
private readonly object _bufferLock = new object();
private readonly StringBuilder _buffer = new StringBuilder(256);
private System.Windows.Forms.Timer _flushTimer;
private const int FlushIntervalMs = 60;
// 防抖
private string _lastDebounceCode = "";
private DateTime _lastDebounceTime = DateTime.MinValue;
private const int DebounceMs = 500;
// 断线重连
private System.Windows.Forms.Timer _reconnectTimer;
private volatile bool _reconnecting = false;
private bool _disposed = false;
public BarcodeScanner(string opName, int baudRate = 9600)
{
OpName = opName;
_baudRate = baudRate;
}
/// <summary>
/// 连接指定COM口
/// </summary>
public bool Connect(string comPort)
{
if (string.IsNullOrWhiteSpace(comPort) || comPort.Length < 3)
return false;
try
{
Disconnect();
ComPort = comPort;
_serial = new SerialPort
{
PortName = comPort,
BaudRate = _baudRate,
Parity = Parity.None,
StopBits = StopBits.One,
ReadTimeout = 1000,
DtrEnable = true
};
_serial.DataReceived += Serial_DataReceived;
_serial.Open();
// 空闲提交定时器
if (_flushTimer == null)
{
_flushTimer = new System.Windows.Forms.Timer { Interval = FlushIntervalMs };
_flushTimer.Tick += FlushTimer_Tick;
}
// 断线重连定时器每3秒检测
if (_reconnectTimer == null)
{
_reconnectTimer = new System.Windows.Forms.Timer { Interval = 3000 };
_reconnectTimer.Tick += ReconnectTimer_Tick;
}
_reconnectTimer.Start();
Log?.Invoke($"[{OpName}] 扫码枪已连接 ({comPort})");
return true;
}
catch (Exception ex)
{
Log?.Invoke($"[{OpName}] 扫码枪连接失败 ({comPort}){ex.Message}");
return false;
}
}
/// <summary>
/// 断开连接
/// </summary>
public void Disconnect()
{
try
{
_reconnectTimer?.Stop();
_flushTimer?.Stop();
if (_serial != null)
{
_serial.DataReceived -= Serial_DataReceived;
if (_serial.IsOpen) _serial.Close();
_serial.Dispose();
_serial = null;
}
}
catch { }
}
/// <summary>
/// 测试当前COM口是否可连接
/// </summary>
public bool TestConnection(string comPort)
{
try
{
using (var sp = new SerialPort(comPort, _baudRate))
{
sp.Open();
sp.Close();
return true;
}
}
catch { return false; }
}
// ── 串口数据接收 ──
private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
if (_serial == null || !_serial.IsOpen) return;
int bytesToRead = _serial.BytesToRead;
if (bytesToRead <= 0) return;
byte[] buf = new byte[bytesToRead];
_serial.Read(buf, 0, bytesToRead);
string chunk = Encoding.UTF8.GetString(buf, 0, bytesToRead);
lock (_bufferLock)
{
_buffer.Append(chunk);
string acc = _buffer.ToString();
// 按换行符(\r \n拆分完整条码
int startIndex = 0;
for (int i = 0; i < acc.Length; i++)
{
if (acc[i] == '\r' || acc[i] == '\n')
{
int len = i - startIndex;
if (len > 0)
{
string line = acc.Substring(startIndex, len).Trim();
if (!string.IsNullOrEmpty(line))
SubmitBarcode(line);
}
startIndex = i + 1;
}
}
if (startIndex >= acc.Length)
_buffer.Clear();
else if (startIndex > 0)
{
_buffer.Clear();
_buffer.Append(acc.Substring(startIndex));
}
// 无换行 → 启动空闲定时器
if (_buffer.Length > 0)
{
try
{
_flushTimer?.Stop();
_flushTimer?.Start();
}
catch { }
}
}
}
catch (Exception ex)
{
if (ex is System.IO.IOException || ex is InvalidOperationException)
{
Log?.Invoke($"[{OpName}] 扫码枪通信异常,尝试重连:{ex.Message}");
TryReopen();
}
}
finally
{
try { _serial?.DiscardInBuffer(); } catch { }
}
}
// 空闲超时提交缓冲区
private void FlushTimer_Tick(object sender, EventArgs e)
{
_flushTimer.Stop();
string toSend;
lock (_bufferLock)
{
toSend = _buffer.ToString().Trim();
_buffer.Clear();
}
if (!string.IsNullOrEmpty(toSend))
SubmitBarcode(toSend);
}
// 提交条码(含防抖)
private void SubmitBarcode(string barcode)
{
barcode = barcode.Replace("\r", "").Replace("\n", "").Replace("\0", "").Trim();
if (string.IsNullOrEmpty(barcode)) return;
// 防抖
var now = DateTime.Now;
if (barcode == _lastDebounceCode && (now - _lastDebounceTime).TotalMilliseconds < DebounceMs)
return;
_lastDebounceCode = barcode;
_lastDebounceTime = now;
// 更新状态
LastBarcode = barcode;
LastScanTime = now;
// 触发事件
BarcodeReceived?.Invoke(OpName, barcode);
}
/// <summary>
/// 手动发送条码(模拟扫码触发,走完整业务流程)
/// </summary>
public void ManualSend(string barcode)
{
if (!string.IsNullOrWhiteSpace(barcode))
SubmitBarcode(barcode.Trim());
}
// ── 断线重连 ──
private void ReconnectTimer_Tick(object sender, EventArgs e)
{
if (_reconnecting || string.IsNullOrWhiteSpace(ComPort)) return;
if (IsConnected) return;
_reconnecting = true;
Task.Run(() =>
{
try { TryReopen(); }
catch { }
finally { _reconnecting = false; }
});
}
private void TryReopen()
{
try
{
string[] ports = SerialPort.GetPortNames();
bool exists = Array.Exists(ports, p => string.Equals(p, ComPort, StringComparison.OrdinalIgnoreCase));
if (!exists) return;
if (_serial != null)
{
try { if (_serial.IsOpen) _serial.Close(); } catch { }
try { _serial.Dispose(); } catch { }
}
_serial = new SerialPort
{
PortName = ComPort,
BaudRate = _baudRate,
Parity = Parity.None,
StopBits = StopBits.One,
ReadTimeout = 1000,
DtrEnable = true
};
_serial.DataReceived += Serial_DataReceived;
_serial.Open();
Log?.Invoke($"[{OpName}] 扫码枪自动重连成功 ({ComPort})");
}
catch (Exception ex)
{
Log?.Invoke($"[{OpName}] 扫码枪自动重连失败 ({ComPort}){ex.Message}");
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
Disconnect();
_flushTimer?.Dispose();
_reconnectTimer?.Dispose();
}
}
}

40
SCADA/Core/IconHelper.cs Normal file
View File

@@ -0,0 +1,40 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork
{
/// <summary>
/// 窗口图标加载工具类(多路径搜索 + exe回退
/// </summary>
public static class IconHelper
{
private static readonly string[] IconSearchPaths = new[]
{
System.IO.Path.Combine(Application.StartupPath, "任务管理.ico"),
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location) ?? "", "任务管理.ico"),
"任务管理.ico"
};
/// <summary>
/// 为指定窗体加载图标
/// </summary>
public static void ApplyIcon(Form form)
{
try
{
foreach (var path in IconSearchPaths)
{
if (System.IO.File.Exists(path))
{
form.Icon = new Icon(path);
return;
}
}
form.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
}
catch { }
}
}
}

626
SCADA/Core/OExcel.cs Normal file
View File

@@ -0,0 +1,626 @@
using NPOI.HPSF;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NPOITest
{
/// <summary>
/// Execl工具辅助类
/// </summary>
public class ExeclHelper
{
/// <summary>
/// 读取Execl数据到DataTable中
/// </summary>
/// <param name="filePath">指定Execl文件路径</param>
/// <param name="isColumnName">设置第一行是否是列名</param>
/// <returns>返回一个DataTable数据集</returns>
public static DataTable ExcelToDataTable(string filePath, string sheetName, bool isColumnName)
{
DataTable dataTable = null;
FileStream fs = null;
DataColumn column = null;
DataRow dataRow = null;
IWorkbook workbook = null;
ISheet sheet = null;
IRow row = null;
ICell cell = null;
int startRow = 0;
try
{
using (fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// 2007版本
if (filePath.IndexOf(".xlsx") > 0)
workbook = new XSSFWorkbook(fs);
// 2003版本
else if (filePath.IndexOf(".xls") > 0)
workbook = new HSSFWorkbook(fs);
if (workbook != null)
{
sheet = workbook.GetSheet(sheetName);//读取第一个sheet当然也可以循环读取每个sheet
dataTable = new DataTable();
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//总行数
if (rowCount > 0)
{
IRow firstRow = sheet.GetRow(0);//第一行
int cellCount = firstRow.LastCellNum;//列数
//构建datatable的列
if (isColumnName)
{
startRow = 1;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
column = new DataColumn("column" + (i + 1));
dataTable.Columns.Add(column);
}
}
//填充行
for (int i = startRow; i <= rowCount; ++i)
{
row = sheet.GetRow(i);
if (row == null) continue;
dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.Blank:
dataRow[j] = "";
break;
case CellType.Numeric:
short format = cell.CellStyle.DataFormat;
//对时间格式2015.12.5、2015/12/5、2015-12-5等的处理
if (format == 14 || format == 31 || format == 57 || format == 58)
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.String:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
dataTable.Rows.Add(dataRow);
}
}
}
}
}
return dataTable;
}
catch (Exception err)
{
if (fs != null)
{
fs.Close();
}
return null;
}
}
public static DataTable ExcelToDataTable(string filePath,int sheetIndex, bool isColumnName)
{
DataTable dataTable = null;
FileStream fs = null;
DataColumn column = null;
DataRow dataRow = null;
IWorkbook workbook = null;
ISheet sheet = null;
IRow row = null;
ICell cell = null;
int startRow = 0;
try
{
using (fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// 2007版本
if (filePath.IndexOf(".xlsx") > 0)
workbook = new XSSFWorkbook(fs);
// 2003版本
else if (filePath.IndexOf(".xls") > 0)
workbook = new HSSFWorkbook(fs);
if (workbook != null)
{
sheet = workbook.GetSheetAt(sheetIndex);//读取第一个sheet当然也可以循环读取每个sheet
dataTable = new DataTable();
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//总行数
if (rowCount > 0)
{
IRow firstRow = sheet.GetRow(0);//第一行
int cellCount = firstRow.LastCellNum;//列数
//构建datatable的列
if (isColumnName)
{
startRow = 1;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
column = new DataColumn("column" + (i + 1));
dataTable.Columns.Add(column);
}
}
//填充行
for (int i = startRow; i <= rowCount; ++i)
{
row = sheet.GetRow(i);
if (row == null) continue;
dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.Blank:
dataRow[j] = "";
break;
case CellType.Numeric:
short format = cell.CellStyle.DataFormat;
//对时间格式2015.12.5、2015/12/5、2015-12-5等的处理
if (format == 14 || format == 31 || format == 57 || format == 58 || format == 22)
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.String:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
dataTable.Rows.Add(dataRow);
}
}
}
}
}
return dataTable;
}
catch (Exception)
{
if (fs != null)
{
fs.Close();
}
return null;
}
}
public static void DataTableToExcel(DataTable dataTable, string templatePath, string outputPath, int startRow, int startCol)
{
// 加载模板文件
using (FileStream fs = new FileStream(templatePath, FileMode.Open, FileAccess.Read))
{
IWorkbook workbook = new XSSFWorkbook(fs);
ISheet sheet = workbook.GetSheetAt(0); // 获取第一个工作表
// 创建单元格样式
ICellStyle cellStyle = workbook.CreateCellStyle();
IFont font = workbook.CreateFont();
font.FontHeightInPoints = 12;
cellStyle.SetFont(font);
cellStyle.BorderTop = BorderStyle.Thin;
cellStyle.BorderBottom = BorderStyle.Thin;
cellStyle.BorderLeft = BorderStyle.Thin;
cellStyle.BorderRight = BorderStyle.Thin;
cellStyle.Alignment = HorizontalAlignment.Center;
// 遍历DataTable的每一行
for (int i = 0; i < dataTable.Rows.Count; i++)
{
IRow row = sheet.CreateRow(startRow + i); // 创建新行
// 遍历DataTable的每一列
for (int j = 0; j < dataTable.Columns.Count; j++)
{
ICell cell = row.CreateCell(startCol + j); // 创建新单元格
cell.SetCellValue(dataTable.Rows[i][j].ToString());
cell.CellStyle = cellStyle; // 应用单元格样式
}
}
// 保存输出文件
using (FileStream outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write))
{
workbook.Write(outputStream);
}
}
}
/// <summary>
/// 将DataTable导出到Excel文档自动根据扩展名选择 .xlsx / .xls 格式)
/// </summary>
/// <param name="dt">传入一个DataTable数据集</param>
/// <param name="sheetName">工作表名称</param>
/// <param name="Outpath">导出文件完整路径(支持 .xlsx 和 .xls</param>
/// <returns>True表示导出成功False表示导出失败</returns>
public static bool DataTableToExcel(DataTable dt, string sheetName, string Outpath)
{
if (dt == null || dt.Rows.Count == 0 || string.IsNullOrWhiteSpace(Outpath))
return false;
IWorkbook workbook = null;
try
{
// ── 1. 根据文件扩展名选择正确的 Workbook 类型 ──
string ext = Path.GetExtension(Outpath).ToLowerInvariant();
if (ext == ".xlsx")
workbook = new XSSFWorkbook(); // OOXML 格式
else
workbook = new HSSFWorkbook(); // BIFF8 格式
ISheet sheet = workbook.CreateSheet(sheetName);
int rowCount = dt.Rows.Count;
int columnCount = dt.Columns.Count;
// ── 2. 创建表头样式(加粗 + 背景色 + 边框) ──
ICellStyle headerStyle = workbook.CreateCellStyle();
IFont headerFont = workbook.CreateFont();
headerFont.IsBold = true;
headerFont.FontHeightInPoints = 11;
headerFont.FontName = "微软雅黑";
headerStyle.SetFont(headerFont);
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
headerStyle.FillPattern = FillPattern.SolidForeground;
headerStyle.Alignment = HorizontalAlignment.Center;
headerStyle.VerticalAlignment = VerticalAlignment.Center;
headerStyle.BorderTop = BorderStyle.Thin;
headerStyle.BorderBottom = BorderStyle.Thin;
headerStyle.BorderLeft = BorderStyle.Thin;
headerStyle.BorderRight = BorderStyle.Thin;
// ── 3. 创建数据行样式(边框) ──
ICellStyle dataStyle = workbook.CreateCellStyle();
IFont dataFont = workbook.CreateFont();
dataFont.FontHeightInPoints = 10;
dataFont.FontName = "微软雅黑";
dataStyle.SetFont(dataFont);
dataStyle.VerticalAlignment = VerticalAlignment.Center;
dataStyle.BorderTop = BorderStyle.Thin;
dataStyle.BorderBottom = BorderStyle.Thin;
dataStyle.BorderLeft = BorderStyle.Thin;
dataStyle.BorderRight = BorderStyle.Thin;
// ── 4. 写入列头 ──
IRow headerRow = sheet.CreateRow(0);
headerRow.HeightInPoints = 22;
for (int c = 0; c < columnCount; c++)
{
ICell cell = headerRow.CreateCell(c);
cell.SetCellValue(dt.Columns[c].ColumnName);
cell.CellStyle = headerStyle;
}
// ── 5. 写入数据行 ──
for (int i = 0; i < rowCount; i++)
{
IRow row = sheet.CreateRow(i + 1);
for (int j = 0; j < columnCount; j++)
{
ICell cell = row.CreateCell(j);
object val = dt.Rows[i][j];
// 根据数据类型设置单元格值,保留数值精度
if (val == null || val == DBNull.Value)
{
cell.SetCellValue("");
}
else if (val is DateTime dtVal)
{
cell.SetCellValue(dtVal.ToString("yyyy-MM-dd HH:mm:ss"));
}
else if (val is double || val is float || val is decimal)
{
cell.SetCellValue(Convert.ToDouble(val));
}
else if (val is int || val is long || val is short)
{
cell.SetCellValue(Convert.ToDouble(val));
}
else
{
cell.SetCellValue(val.ToString());
}
cell.CellStyle = dataStyle;
}
}
// ── 6. 自动调整列宽(限制最大宽度避免过宽) ──
for (int c = 0; c < columnCount; c++)
{
sheet.AutoSizeColumn(c);
int colWidth = sheet.GetColumnWidth(c);
// 最大列宽限制为 50 个字符宽度 (50 * 256)
if (colWidth > 50 * 256)
sheet.SetColumnWidth(c, 50 * 256);
// 最小列宽
else if (colWidth < 10 * 256)
sheet.SetColumnWidth(c, 10 * 256);
}
// ── 7. 写入文件(使用 FileMode.Create 确保覆盖写入) ──
using (FileStream fs = new FileStream(Outpath, FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ExeclHelper] 导出Excel失败: {ex.Message}");
return false;
}
finally
{
if (workbook != null)
{
workbook.Close();
workbook = null;
}
}
}
/// <summary>
/// 读取Execl数据到DataTable(DataSet)中
/// </summary>
/// <param name="filePath">指定Execl文件路径</param>
/// <param name="isFirstLineColumnName">设置第一行是否是列名</param>
/// <returns>返回一个DataTable数据集</returns>
public static DataSet ExcelToDataSet(string filePath, bool isFirstLineColumnName)
{
DataSet dataSet = new DataSet();
int startRow = 0;
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
IWorkbook workbook = null;
// 如果是2007+的Excel版本
if (filePath.IndexOf(".xlsx") > 0)
{
workbook = new XSSFWorkbook(fs);
}
// 如果是2003-的Excel版本
else if (filePath.IndexOf(".xls") > 0)
{
workbook = new HSSFWorkbook(fs);
}
if (workbook != null)
{
//循环读取Excel的每个sheet每个sheet页都转换为一个DataTable并放在DataSet中
for (int p = 0; p < workbook.NumberOfSheets; p++)
{
ISheet sheet = workbook.GetSheetAt(p);
DataTable dataTable = new DataTable();
dataTable.TableName = sheet.SheetName;
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//获取总行数
if (rowCount > 0)
{
IRow firstRow = sheet.GetRow(0);//获取第一行
int cellCount = firstRow.LastCellNum;//获取总列数
//构建datatable的列
if (isFirstLineColumnName)
{
startRow = 1;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
ICell cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
DataColumn column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
DataColumn column = new DataColumn("column" + (i + 1));
dataTable.Columns.Add(column);
}
}
//填充行
for (int i = startRow; i <= rowCount; ++i)
{
IRow row = sheet.GetRow(i);
if (row == null) continue;
DataRow dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
ICell cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.Blank:
dataRow[j] = "";
break;
case CellType.Numeric:
short format = cell.CellStyle.DataFormat;
//对时间格式2015.12.5、2015/12/5、2015-12-5等的处理
if (format == 14 || format == 31 || format == 57 || format == 58)
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.String:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
dataTable.Rows.Add(dataRow);
}
}
}
dataSet.Tables.Add(dataTable);
}
}
}
return dataSet;
}
catch (Exception err)
{
return null;
}
}
/// <summary>
/// 将DataTable(DataSet)导出到Execl文档
/// </summary>
/// <param name="dataSet">传入一个DataSet</param>
/// <param name="Outpath">导出路径(可以不加扩展名,不加默认为.xls</param>
/// <returns>返回一个Bool类型的值表示是否导出成功</returns>
/// True表示导出成功Flase表示导出失败
public static bool DataSetToExcel(DataSet dataSet, string Outpath)
{
bool result = false;
try
{
if (dataSet == null || dataSet.Tables == null || dataSet.Tables.Count == 0 || string.IsNullOrEmpty(Outpath))
throw new Exception("输入的DataSet或路径异常");
int sheetIndex = 0;
//根据输出路径的扩展名判断workbook的实例类型
IWorkbook workbook = null;
string pathExtensionName = Outpath.Trim().Substring(Outpath.Length - 5);
if (pathExtensionName.Contains(".xlsx"))
{
workbook = new XSSFWorkbook();
}
else if (pathExtensionName.Contains(".xls"))
{
workbook = new HSSFWorkbook();
}
else
{
Outpath = Outpath.Trim() + ".xls";
workbook = new HSSFWorkbook();
}
//将DataSet导出为Excel
foreach (DataTable dt in dataSet.Tables)
{
sheetIndex++;
if (dt != null && dt.Rows.Count > 0)
{
ISheet sheet = workbook.CreateSheet(string.IsNullOrEmpty(dt.TableName) ? ("sheet" + sheetIndex) : dt.TableName);//创建一个名称为Sheet0的表
int rowCount = dt.Rows.Count;//行数
int columnCount = dt.Columns.Count;//列数
//设置列头
IRow row = sheet.CreateRow(0);//excel第一行设为列头
for (int c = 0; c < columnCount; c++)
{
ICell cell = row.CreateCell(c);
cell.SetCellValue(dt.Columns[c].ColumnName);
}
//设置每行每列的单元格,
for (int i = 0; i < rowCount; i++)
{
row = sheet.CreateRow(i + 1);
for (int j = 0; j < columnCount; j++)
{
ICell cell = row.CreateCell(j);//excel第二行开始写入数据
cell.SetCellValue(dt.Rows[i][j].ToString());
}
}
}
}
//向outPath输出数据
using (FileStream fs = File.OpenWrite(Outpath))
{
workbook.Write(fs);//向打开的这个xls文件中写入数据
result = true;
}
return result;
}
catch (Exception ex)
{
return false;
}
}
}
}

25
SCADA/Core/PLC_R.cs Normal file
View File

@@ -0,0 +1,25 @@
using System;
using System.Text;
namespace DC_A95
{
public class PLC_R
{
/// <summary>
/// 清理PLC字符串乱码统一入口支持object参数
/// 过滤不可见字符保留可打印ASCII和中文等有效字符
/// </summary>
public static string GetString_CleanGarbled(object input)
{
if (input == null) return "";
string str = input.ToString();
if (string.IsNullOrEmpty(str)) return "";
var sb = new StringBuilder();
foreach (char c in str)
{
if (c >= 0x20 && c < 0xFFFE) sb.Append(c);
}
return sb.ToString().Trim();
}
}
}

View File

@@ -0,0 +1,72 @@
using MesWork;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
namespace MesWork
{
public class SqlOperation
{
public static bool ExecuteStoredProcedure(string name, SqlParameter[] sqlParameters, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteStoredProcedure(name, MesWorkForm.ConnectionString, ref sqlParameters, out errorMessage);
}
public static bool ExecuteStoredProcedure(string name, SqlParameter[] sqlParameters, out DataTable dt, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteStoredProcedure(name, MesWorkForm.ConnectionString, ref sqlParameters, out dt, out errorMessage);
}
public static bool ExecuteStoredProcedure(string name, SqlParameter[] sqlParameters, out DataSet ds, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteStoredProcedure(name, MesWorkForm.ConnectionString, ref sqlParameters, out ds, out errorMessage);
}
public static bool ExecuteSql(string sql, out DataTable dt, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteSql(sql, MesWorkForm.ConnectionString, out dt, out errorMessage);
}
public static bool ExecuteSql(string sql, out DataSet ds, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteDataset(sql, MesWorkForm.ConnectionString, out ds, out errorMessage);
}
public static bool ExecuteSql(string sql, out string errorMessage)
{
return DataLinkMesWork.SQLCommon.ExecuteSql(sql, MesWorkForm.ConnectionString, out errorMessage);
}
/// <summary>
/// 通过SqlBulkCopy复制table数据到数据库
/// </summary>
/// <param name="dataset"></param>
public static bool SqlbulkcopyInsert(DataTable dt, out string ErrMsg)
{
bool success = false;
ErrMsg = "";
try
{
// SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(connectString, SqlBulkCopyOptions.KeepIdentity);//删除自增ID插入原始数据
SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(MesWorkForm.ConnectionString, SqlBulkCopyOptions.UseInternalTransaction);//批量事务处理
sqlbulkcopy.DestinationTableName = dt.TableName;//数据库中的表名
for (int i = 0; i < dt.Columns.Count; i++)
{
var columnName = dt.Columns[i].ColumnName.ToString();
sqlbulkcopy.ColumnMappings.Add(columnName, columnName);
}
sqlbulkcopy.WriteToServer(dt);
success = true;
}
catch (Exception err)
{
ErrMsg = err.Message;
}
return success;
}
}
}

192
SCADA/Frm_Login.Designer.cs generated Normal file
View File

@@ -0,0 +1,192 @@
namespace MesWork
{
partial class Frm_Login
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.pnl_Card = new System.Windows.Forms.Panel();
this.lbl_Title = new System.Windows.Forms.Label();
this.lbl_SubTitle = new System.Windows.Forms.Label();
this.lbl_User = new System.Windows.Forms.Label();
this.txt_LoginID = new System.Windows.Forms.TextBox();
this.lbl_Pwd = new System.Windows.Forms.Label();
this.txt_userPassword = new System.Windows.Forms.TextBox();
this.btn_Login = new System.Windows.Forms.Button();
this.btn_Cancel = new System.Windows.Forms.Button();
this.lbl_Msg = new System.Windows.Forms.Label();
this.pnl_Card.SuspendLayout();
this.SuspendLayout();
//
// pnl_Card - 白色登录卡片
//
this.pnl_Card.BackColor = System.Drawing.Color.White;
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 - 系统图标+名称
//
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.Location = new System.Drawing.Point(20, 30);
this.lbl_Title.Name = "lbl_Title";
this.lbl_Title.Size = new System.Drawing.Size(360, 40);
this.lbl_Title.TabIndex = 0;
this.lbl_Title.Text = "⚖ 称重数据采集分析";
this.lbl_Title.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// 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.Location = new System.Drawing.Point(20, 72);
this.lbl_SubTitle.Name = "lbl_SubTitle";
this.lbl_SubTitle.Size = new System.Drawing.Size(360, 24);
this.lbl_SubTitle.TabIndex = 1;
this.lbl_SubTitle.Text = "管理系统";
this.lbl_SubTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// 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.Location = new System.Drawing.Point(40, 120);
this.lbl_User.Name = "lbl_User";
this.lbl_User.Size = new System.Drawing.Size(60, 24);
this.lbl_User.TabIndex = 2;
this.lbl_User.Text = "👤 用户名";
//
// txt_LoginID
//
this.txt_LoginID.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_LoginID.Font = new System.Drawing.Font("微软雅黑", 12F);
this.txt_LoginID.Location = new System.Drawing.Point(40, 148);
this.txt_LoginID.Name = "txt_LoginID";
this.txt_LoginID.Size = new System.Drawing.Size(320, 29);
this.txt_LoginID.TabIndex = 1;
this.txt_LoginID.Text = "admin";
this.txt_LoginID.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_LoginID_KeyDown);
//
// 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.Location = new System.Drawing.Point(40, 195);
this.lbl_Pwd.Name = "lbl_Pwd";
this.lbl_Pwd.Size = new System.Drawing.Size(60, 24);
this.lbl_Pwd.TabIndex = 4;
this.lbl_Pwd.Text = "🔒 密码";
//
// txt_userPassword
//
this.txt_userPassword.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_userPassword.Font = new System.Drawing.Font("微软雅黑", 12F);
this.txt_userPassword.Location = new System.Drawing.Point(40, 223);
this.txt_userPassword.Name = "txt_userPassword";
this.txt_userPassword.PasswordChar = '*';
this.txt_userPassword.Size = new System.Drawing.Size(320, 29);
this.txt_userPassword.TabIndex = 2;
this.txt_userPassword.Text = "admin";
this.txt_userPassword.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_userPassword_KeyDown);
//
// btn_Login - Steel Blue 主按钮
//
this.btn_Login.BackColor = System.Drawing.Color.FromArgb(74, 144, 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;
this.btn_Login.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold);
this.btn_Login.ForeColor = System.Drawing.Color.White;
this.btn_Login.Location = new System.Drawing.Point(40, 280);
this.btn_Login.Name = "btn_Login";
this.btn_Login.Size = new System.Drawing.Size(150, 42);
this.btn_Login.TabIndex = 3;
this.btn_Login.Text = "登 录";
this.btn_Login.UseVisualStyleBackColor = false;
this.btn_Login.Click += new System.EventHandler(this.btn_Login_Click);
//
// 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.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.Location = new System.Drawing.Point(210, 280);
this.btn_Cancel.Name = "btn_Cancel";
this.btn_Cancel.Size = new System.Drawing.Size(150, 42);
this.btn_Cancel.TabIndex = 4;
this.btn_Cancel.Text = "取 消";
this.btn_Cancel.UseVisualStyleBackColor = false;
this.btn_Cancel.Click += new System.EventHandler(this.btn_Cancel_Click);
//
// 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.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.ClientSize = new System.Drawing.Size(1024, 720);
this.Controls.Add(this.pnl_Card);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "Frm_Login";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "称重数据采集分析管理系统 - 登录";
this.pnl_Card.ResumeLayout(false);
this.pnl_Card.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel pnl_Card;
private System.Windows.Forms.Label lbl_Title;
private System.Windows.Forms.Label lbl_SubTitle;
private System.Windows.Forms.Label lbl_User;
private System.Windows.Forms.TextBox txt_LoginID;
private System.Windows.Forms.Label lbl_Pwd;
private System.Windows.Forms.TextBox txt_userPassword;
private System.Windows.Forms.Button btn_Login;
private System.Windows.Forms.Button btn_Cancel;
private System.Windows.Forms.Label lbl_Msg;
}
}

219
SCADA/Frm_Login.cs Normal file
View File

@@ -0,0 +1,219 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
namespace MesWork
{
/// <summary>
/// 登录页面 — 全屏背景图 + 右侧白色登录卡片
/// </summary>
public partial class Frm_Login : Form
{
/// <summary>
/// 背景图片缓存
/// </summary>
private Image _bgImage;
public Frm_Login()
{
InitializeComponent();
DoubleBuffered = true;
LoadBackgroundImage();
LoadIcon();
}
/// <summary>
/// 加载任务栏图标
/// </summary>
private void LoadIcon()
{
IconHelper.ApplyIcon(this);
}
/// <summary>
/// 加载背景图片(从 Resources 目录)
/// </summary>
private void LoadBackgroundImage()
{
try
{
string bgPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "login_bg.png");
if (File.Exists(bgPath))
_bgImage = Image.FromFile(bgPath);
}
catch { /* 图片加载失败时使用渐变背景兜底 */ }
}
/// <summary>
/// 绘制背景:优先使用背景图,否则渐变兜底
/// </summary>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
if (_bgImage != null)
{
// 全屏铺满背景图
g.DrawImage(_bgImage, 0, 0, ClientSize.Width, ClientSize.Height);
// 右侧半透明白色蒙层(让登录卡片区域更清晰)
using (var overlay = new SolidBrush(Color.FromArgb(180, 255, 255, 255)))
{
g.FillRectangle(overlay, ClientSize.Width / 2, 0, ClientSize.Width / 2, ClientSize.Height);
}
}
else
{
// 渐变兜底
using (var brush = new LinearGradientBrush(
ClientRectangle,
Color.FromArgb(30, 58, 95),
Color.FromArgb(240, 244, 248),
LinearGradientMode.Horizontal))
{
g.FillRectangle(brush, ClientRectangle);
}
}
// 底部版本信息(放在右下角白色区域,确保清晰可读)
using (var font = new Font("微软雅黑", 9F))
using (var brush = new SolidBrush(Color.FromArgb(160, 120, 120, 120)))
{
string ver = "v1.0.0 | 潍柴动力 · 称重数据采集分析管理系统";
var sz = g.MeasureString(ver, font);
g.DrawString(ver, font, brush, ClientSize.Width - sz.Width - 20, ClientSize.Height - 36);
}
}
/// <summary>
/// 窗口大小变化时重新定位登录卡片到右半区中央
/// </summary>
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (pnl_Card != null)
{
// 卡片放在右半区的中心位置
int rightHalfCenter = ClientSize.Width / 2 + ClientSize.Width / 4;
pnl_Card.Location = new Point(
rightHalfCenter - pnl_Card.Width / 2,
(ClientSize.Height - pnl_Card.Height) / 2);
}
}
/// <summary>
/// 卡片圆角绘制
/// </summary>
private void pnl_Card_Paint(object sender, PaintEventArgs e)
{
var panel = (Panel)sender;
int radius = 12;
var rect = new Rectangle(0, 0, panel.Width - 1, panel.Height - 1);
using (var path = new GraphicsPath())
{
path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90);
path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90);
path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);
path.CloseFigure();
panel.Region = new Region(path);
}
}
// ====================================================================
// 登录逻辑
// ====================================================================
private void btn_Cancel_Click(object sender, EventArgs e)
{
if (MesWorkForm.Curr_UserName == "") Application.Exit();
else DialogResult = DialogResult.Cancel;
}
private void btn_Login_Click(object sender, EventArgs e)
{
if (txt_LoginID.Text.Trim() == "")
{
lbl_Msg.Text = "⚠ 用户名不能为空";
lbl_Msg.Visible = true;
return;
}
// 调用新的用户管理表登录验证
var res = User_Login(txt_LoginID.Text.Trim(), txt_userPassword.Text, out string name, out string perm);
if (res)
{
MesWorkForm.Curr_LoginID = txt_LoginID.Text.Trim();
MesWorkForm.Curr_UserPassWord = txt_userPassword.Text;
MesWorkForm.Curr_UserName = name;
// 记录登录日志
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 { }
DialogResult = DialogResult.OK;
}
else
{
lbl_Msg.Text = "⚠ 登录失败,请检查用户名和密码";
lbl_Msg.Visible = true;
}
}
/// <summary>
/// 调用 用户管理_登录验证 SP
/// </summary>
private bool User_Login(string userName, string pwd, out string name, out string permission)
{
name = "";
permission = "";
try
{
var sp = "用户管理_登录验证";
var parms = new System.Data.SqlClient.SqlParameter[]
{
new System.Data.SqlClient.SqlParameter("@用户名", userName),
new System.Data.SqlClient.SqlParameter("@密码", pwd)
};
SqlOperation.ExecuteStoredProcedure(sp, parms, out System.Data.DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0)
{
name = dt.Rows[0]["姓名"].ToString();
permission = dt.Rows[0]["权限"].ToString();
return true;
}
}
catch (Exception ex)
{
MesWorkForm.WriteLog_Info("Login", $"登录异常:{ex.Message}", ex);
}
return false;
}
/// <summary>
/// 回车切换焦点:用户名 → 密码
/// </summary>
private void txt_LoginID_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) { txt_userPassword.Focus(); e.SuppressKeyPress = true; }
}
/// <summary>
/// 回车触发登录:密码 → 登录
/// </summary>
private void txt_userPassword_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) { btn_Login_Click(sender, e); e.SuppressKeyPress = true; }
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
_bgImage?.Dispose();
base.OnFormClosed(e);
}
}
}

61
SCADA/Frm_Login.resx Normal file
View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<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">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

506
SCADA/Funtion/B_DB_Opera.cs Normal file
View File

@@ -0,0 +1,506 @@
using DC_A95;
using ExternalDataSync;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace MesWork
{
/// <summary>
/// 数据库操作
/// </summary>
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 void Event_WorkEnd(string opName, int tagTypeCodeID, string partType
, string barCode, int qualityMask, int outCount, int duration
, string tagID)
{
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)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_Signal_Log(string opName, int tagTypeCodeID, string value, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_Signal_Log";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@Value", value),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_Alarm_Start(string opName, int tagTypeCodeID, string alarmText, string alarmLevel, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_Alarm_Start";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@AlarmText", alarmText),
new SqlParameter("@AlarmLevel", alarmLevel),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_Alarm_End(string opName, int tagTypeCodeID, string alarmText, string alarmLevel, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_Alarm_End";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@AlarmText", alarmText),
new SqlParameter("@AlarmLevel", alarmLevel),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
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;
text = "";
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Query_AlarmData";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out DataTable dt, out string errorMessage);
if (dt.Rows.Count > 0)
{
count = Convert.ToInt32(dt.Rows[0]["COUNT"].ToString());
text = dt.Rows[0]["TEXT"].ToString();
}
}
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");
var procedureName = "Event_DeviceStatus_Change";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@DeviceStatus", DeviceStatus),
new SqlParameter("@ChangeTime",ChangeTime),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
/// <summary>
/// 请求日志记录
/// </summary>
public static void SaveLog_Request(string StationName, Log_Type LogType, string ReqCode, Log_FromAndTo ReqFrom, Log_FromAndTo ReqTo, string ReqStr, out long AID, string ReqStrSafe = "")
{
AID = -1;
var CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
ReqStr = PLC_R.GetString_CleanGarbled(ReqStr);
var param1 = new SqlParameter[] {
new SqlParameter("@工位号", StationName),
new SqlParameter("@日志类型", LogType.ToString()),
new SqlParameter("@TaskID", ReqCode),
new SqlParameter("@请求CMD",""),
new SqlParameter("@请求FROM",ReqFrom.ToString()),
new SqlParameter("@请求TO",ReqTo.ToString()),
new SqlParameter("@请求时间",CreateTime),
new SqlParameter("@请求内容",ReqStr),
new SqlParameter("@请求内容安全",ReqStrSafe)
};
DataLinkMesWork.SQLCommon.ExecuteStoredProcedure("Event_Log_Request", MesWorkForm.ConnectionString, ref param1, out DataTable dt, out string errorMessage);
if (dt.Rows.Count > 0)
{
AID = Convert.ToInt64(dt.Rows[0]["AID"]);
}
}
/// <summary>
/// 响应日志记录
/// </summary>
public static void SaveLog_Response(string result, long AID)
{
try
{
var CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var param4 = new SqlParameter[]
{
new SqlParameter("@AID",AID),
new SqlParameter("@响应时间",CreateTime),
new SqlParameter("@响应内容",JsonConvert.SerializeObject(result))
};
DataLinkMesWork.SQLCommon.ExecuteStoredProcedure("Event_Log_Respond", MesWorkForm.ConnectionString, ref param4, out string errorMessage);
}
catch (Exception err)
{
}
}
// ====================================================================
// 称重业务数据库方法(调用存储过程,统一 result/msg 返回格式)
// ====================================================================
/// <summary>
/// 从工件管理表查询机型参数(加油量/抽油量/密度/残油量上下限)
/// </summary>
public static bool QueryWorkpieceByModel(string modelNo, out decimal addOilQty, out decimal extractOilQty, out decimal density,
out decimal residualOilUpperLimit, out decimal residualOilLowerLimit)
{
addOilQty = 0; extractOilQty = 0; density = 0; residualOilUpperLimit = 0; residualOilLowerLimit = 0;
var sqlParameter = new SqlParameter[] {
new SqlParameter("@机型号", modelNo)
};
SqlOperation.ExecuteStoredProcedure("称重_查询工件参数", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
var row = dt.Rows[0];
decimal.TryParse(row["加油量"]?.ToString(), out addOilQty);
decimal.TryParse(row["抽油量"]?.ToString(), out extractOilQty);
decimal.TryParse(row["密度"]?.ToString(), out density);
decimal.TryParse(row["残油量上限"]?.ToString(), out residualOilUpperLimit);
decimal.TryParse(row["残油量下限"]?.ToString(), out residualOilLowerLimit);
return true;
}
return false;
}
/// <summary>
/// 插入称重记录
/// </summary>
/// <returns>新记录ID失败返回-1</returns>
public static long InsertWeighingRecord(
string stationNo, string weighType, string engineNo, string modelNo,
string orderNo, string palletNo,
decimal inWeight, decimal outWeight,
decimal addOilQty, decimal extractOilQty, decimal density,
decimal residualOilUpperLimit, decimal residualOilLowerLimit,
decimal oilRelease, decimal residualOil,
int qualityFlag, decimal waterContent,
string dataSource, string worker, int isComplete)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重类型", weighType),
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@机型号", modelNo),
new SqlParameter("@工单号", orderNo),
new SqlParameter("@托盘号", palletNo),
new SqlParameter("@进站重量", inWeight),
new SqlParameter("@离站重量", outWeight),
new SqlParameter("@加油量", addOilQty),
new SqlParameter("@抽油量", extractOilQty),
new SqlParameter("@密度", density),
new SqlParameter("@残油量上限", residualOilUpperLimit),
new SqlParameter("@残油量下限", residualOilLowerLimit),
new SqlParameter("@放油量", oilRelease),
new SqlParameter("@残油量", residualOil),
new SqlParameter("@合格标志", qualityFlag),
new SqlParameter("@水含量", waterContent),
new SqlParameter("@数据来源", dataSource),
new SqlParameter("@操作者", worker),
new SqlParameter("@是否完成", isComplete)
};
SqlOperation.ExecuteStoredProcedure("称重记录_增加", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
return Convert.ToInt64(dt.Rows[0]["ID"]);
}
return -1;
}
/// <summary>
/// 查询放油前记录(按发动机号匹配,称重类型='空中'且未完成)
/// </summary>
public static bool QueryWeighingBeforeOil(string engineNo, out long recordId, out decimal preWeight, out decimal addOilQty, out decimal extractOilQty, out decimal density)
{
recordId = 0; preWeight = 0; addOilQty = 0; extractOilQty = 0; density = 0;
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo)
};
SqlOperation.ExecuteStoredProcedure("称重记录_查询放油前", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
var row = dt.Rows[0];
recordId = Convert.ToInt64(row["ID"]);
decimal.TryParse(row["进站重量"]?.ToString(), out preWeight);
decimal.TryParse(row["加油量"]?.ToString(), out addOilQty);
decimal.TryParse(row["抽油量"]?.ToString(), out extractOilQty);
decimal.TryParse(row["密度"]?.ToString(), out density);
return true;
}
return false;
}
/// <summary>
/// 查询指定工位最新的称重记录(不过滤完成状态)
/// </summary>
public static bool QueryLatestRecord(string stationNo, string engineNo,
out long recordId, out decimal preWeight,
out decimal addOilQty, out decimal extractOilQty, out decimal density)
{
recordId = 0; preWeight = 0; addOilQty = 0; extractOilQty = 0; density = 0;
var sqlParameter = new SqlParameter[] {
new SqlParameter("@工位号", stationNo),
new SqlParameter("@发动机号", engineNo)
};
SqlOperation.ExecuteStoredProcedure("称重记录_查询最新", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
var row = dt.Rows[0];
recordId = Convert.ToInt64(row["ID"]);
decimal.TryParse(row["进站重量"]?.ToString(), out preWeight);
decimal.TryParse(row["加油量"]?.ToString(), out addOilQty);
decimal.TryParse(row["抽油量"]?.ToString(), out extractOilQty);
decimal.TryParse(row["密度"]?.ToString(), out density);
return true;
}
return false;
}
/// <summary>
/// 更新称重记录为完成状态(保存称重结果)
/// </summary>
public static bool UpdateWeighingComplete(long recordId, string weighType, string stationNo,
decimal weight, decimal oilRelease, decimal residualOil, decimal waterContent, out int qualityFlag, out string msg)
{
qualityFlag = 2;
msg = "";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@ID", recordId),
new SqlParameter("@称重类型", weighType),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重重量", weight),
new SqlParameter("@放油量", oilRelease),
new SqlParameter("@残油量", residualOil),
new SqlParameter("@水含量", waterContent)
};
SqlOperation.ExecuteStoredProcedure("称重记录_完成更新", sqlParameter, out DataTable dt, out string err);
if (dt == null || dt.Rows.Count == 0)
{
msg = string.IsNullOrEmpty(err) ? "称重记录完成更新未返回结果" : err;
return false;
}
qualityFlag = Convert.ToInt32(dt.Rows[0]["result"]);
msg = dt.Rows[0]["msg"].ToString();
return true;
}
// ====================================================================
// 过站记录操作方法
// ====================================================================
/// <summary>
/// 新增过站记录case 44 请求工作时调用)
/// </summary>
public static long InsertStationRecord(string engineNo, string modelNo, string orderNo, string palletNo,
string stationNo, string stationName, decimal addOilQty, decimal extractOilQty, decimal density, string worker)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@机型号", modelNo),
new SqlParameter("@工单号", orderNo),
new SqlParameter("@托盘号", palletNo),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@工位名称", stationName),
new SqlParameter("@加油量", addOilQty),
new SqlParameter("@抽油量", extractOilQty),
new SqlParameter("@油密度", density),
new SqlParameter("@操作者", worker)
};
SqlOperation.ExecuteStoredProcedure("过站记录_新增", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
return Convert.ToInt64(dt.Rows[0]["ID"]);
return -1;
}
/// <summary>
/// 更新过站记录为完成case 19 请求保存时调用)
/// </summary>
public static bool UpdateStationComplete(string engineNo, string stationNo, decimal weight)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重重量", weight)
};
SqlOperation.ExecuteStoredProcedure("过站记录_完成", sqlParameter, out DataTable dt, out string err);
return dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1";
}
// ====================================================================
// 称重曲线数据操作方法
// ====================================================================
/// <summary>
/// 创建曲线段记录(工件到位时调用)
/// </summary>
/// <returns>段ID失败返回-1</returns>
public static long Curve_StartSegment(string opName, string engineNo, string modelNo)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@工位号", opName),
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@机型号", modelNo ?? (object)DBNull.Value)
};
SqlOperation.ExecuteStoredProcedure("称重曲线_开始段", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
return Convert.ToInt64(dt.Rows[0]["ID"]);
}
return -1;
}
/// <summary>
/// 批量写入曲线采样点采集线程每攒够N条调用一次
/// </summary>
/// <param name="segmentId">段ID</param>
/// <param name="points">采样点列表</param>
/// <returns>写入条数失败返回0</returns>
public static int Curve_WritePoints(long segmentId, List<CurveSamplePoint> points)
{
if (points == null || points.Count == 0) return 0;
// 用 JSON 序列化避免小数区域格式和字符串拼接转义问题。
var json = JsonConvert.SerializeObject(points.Select(p => new
{
seq = p.SeqNo,
time = p.SampleTime.ToString("yyyy-MM-ddTHH:mm:ss.fff"),
weight = p.Weight
}));
var sqlParameter = new SqlParameter[] {
new SqlParameter("@段ID", segmentId),
new SqlParameter("@采样数据", json)
};
SqlOperation.ExecuteStoredProcedure("称重曲线_批量写入", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
return Convert.ToInt32(dt.Rows[0]["count"]);
}
return 0;
}
/// <summary>
/// 结束曲线段请求保存时调用关联称重记录ID
/// </summary>
public static bool Curve_EndSegment(long segmentId, long weighingRecordId)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@段ID", segmentId),
new SqlParameter("@称重记录ID", weighingRecordId > 0 ? (object)weighingRecordId : DBNull.Value)
};
SqlOperation.ExecuteStoredProcedure("称重曲线_结束段", sqlParameter, out DataTable dt, out string err);
return dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1";
}
}
/// <summary>
/// 曲线采样点数据结构
/// </summary>
public class CurveSamplePoint
{
public int SeqNo;
public DateTime SampleTime;
public decimal Weight;
}
}

View File

@@ -0,0 +1,40 @@
using DC_A95;
using OpcBasic;
using OpcData;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using SystemFramework;
namespace MesWork
{
/// <summary>
/// 设备状态的处理
/// </summary>
public partial class MesWorkForm
{
/// <summary>
/// 设备状态的处理
/// </summary>
/// <param name="e"></param>
private void MIS_Device(object e)
{
var eState = (DeviceDriver_BasicData.PlcStateEvetnArgs)e;
var ip = eState.IP.ToString();
var onLine = eState.OnLine;
}
}
}

View File

@@ -0,0 +1,509 @@
using ExternalDataSync;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using DC_A95;
/// <summary>
///
/// </summary>
namespace MesWork
{
/// <summary>
/// 处理MIS逻辑
/// </summary>
public partial class MesWorkForm
{
/// <summary>
/// PLC信号变化处理入口
/// 当Bool型信号值变化时由框架自动触发
/// </summary>
/// <param name="e"></param>
private void MIS_Funtion(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
if (msgEvent.TagValue == null) return;
var tagID = msgEvent.TagID.ToString();
var opName = msgEvent.OpName.ToString();
var tagTypeCodeID = (int)msgEvent.TagTypeCodeID;
var tagTypeID = (EnumTagTypeID)msgEvent.TagTypeID;
var isFirstValue = msgEvent.IsFirstValue.ToString().ToLower();
var alarmLevel = msgEvent.ShaftID.ToString();
var alarmMsg = msgEvent.ItemName.ToString();
string tagValue;
switch (tagTypeID)
{
case EnumTagTypeID.BOOL:
tagValue = (Convert.ToInt32(msgEvent.TagValue)).ToString();
break;
default:
tagValue = msgEvent.TagValue.ToString();
break;
}
// ── 报警处理编码900~2000──
if (tagTypeCodeID >= 900 & tagTypeCodeID < 2000)
{
if (ValueT(isFirstValue))
{
//不处理第一次变化的值
return;
}
var opNameNew = opName.Replace("_Alarm", "");
if (tagValue == "1")
{
B_DB_Opera.Event_Alarm_Start(opNameNew, tagTypeCodeID, alarmMsg, alarmLevel, tagID);
}
else
{
B_DB_Opera.Event_Alarm_End(opNameNew, tagTypeCodeID, alarmMsg, alarmLevel, tagID);
}
}
// ── 信号分发 ──
switch (tagTypeCodeID)
{
case 2: // 工件到位 → 启动/停止实时重量曲线采集
if (tagValue == "1")
{
WeighingPage?.AppendLog($"[{opName}] 工件到位信号触发");
CurveCollector_Start(opName);
}
else
{
CurveCollector_Stop(opName);
}
break;
case 14: // PLC心跳 → 回写PC心跳
WritePLC_IF(15, opName, tagValue);
break;
case 44: // 请求工作
if (ValueT(isFirstValue)) return;
if (tagValue == "1")
{
WeighingPage?.AppendLog($"[{opName}] PLC请求工作开始处理...");
Weighing_HandleRequestWork(opName, tagID);
}
else
{
WritePLC_IF(66, opName, false); //允许工作
}
break;
case 19: // 请求保存
if (ValueT(isFirstValue)) return;
if (tagValue == "1")
{
CurveCollector_StopSampling(opName, true); // 先停止采样保存完成后再关联称重记录ID
WeighingPage?.AppendLog($"[{opName}] PLC请求保存数据...");
Weighing_HandleRequestSave(opName, tagID);
}
else
{
WritePLC_IF(20, opName, false); //保存完成
WritePLC_IF(21, opName, false); //合格标志
}
break;
case 203: // 设备状态
B_DB_Opera.Event_DeviceStatus_Change(opName, tagValue);
break;
default:
break;
}
}
catch (Exception err)
{ }
}
// ====================================================================
// 称重交互核心方法
// ====================================================================
/// <summary>
/// 获取称重类型:地面/放油前/放油后
/// Ground模式统一返回"地面"
/// Hanging模式OP10=放油前OP20=放油后
/// </summary>
private string GetWeighingType(string opName)
{
if (WeighingType == "Ground")
return "地面";
// Hanging模式
return opName == "OP10" ? "放油前" : "放油后";
}
/// <summary>
/// 处理请求工作信号 (tagTypeCodeID=44)
/// 流程:读取工件信息 → 查询参数 → 写过站记录 → 按类型处理称重记录 → 回写允许工作
/// </summary>
private void Weighing_HandleRequestWork(string opName, string tagID)
{
B_DB_Opera.SaveLog_Request(opName, Log_Type.ZK_MesHandler, "MIS_Function",
Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"触发信号【44-请求工作】", out long AID);
try
{
// 1. 读取PLC工件信息
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)); // 托盘号
B_DB_Opera.SaveLog_Response($"【{opName}】读取工件:发动机={engineNo},机型={modelNo},工单={orderNo},托盘={palletNo}", AID);
// 推送工件信息到称重UI
int stIdx = WeighingPage?.GetStationIndex(opName) ?? 1;
WeighingPage?.UpdateStationInfo(stIdx, engineNo, modelNo, orderNo);
WeighingPage?.AppendLog($"[{opName}] 读取工件:{engineNo},机型={modelNo}");
// 2. 查询工件管理参数(加油量/抽油量/密度/残油量上下限)
if (!B_DB_Opera.QueryWorkpieceByModel(modelNo, out decimal addOilQty, out decimal extractOilQty, out decimal density,
out decimal residualOilUpperLimit, out decimal residualOilLowerLimit))
{
WritePLC_IF(112, opName, 3); // 报警代码=3机型错误
B_DB_Opera.SaveLog_Response($"【{opName}】工件管理中未找到机型[{modelNo}]的参数,发动机={engineNo}", AID);
WeighingPage?.AppendLog($"[{opName}] 机型[{modelNo}]未找到配置参数");
return;
}
// 3. 推送工件参数到UI
WeighingPage?.UpdateOilInfo(stIdx, addOilQty, extractOilQty, density, 0, 0);
// 4. 确定称重类型和工位名称
string weighType = GetWeighingType(opName); // 地面/放油前/放油后
string stationName = weighType; // 工位名称直接用称重类型名
// 4. 写过站记录所有类型统一INSERT一条到达时间=NOW
B_DB_Opera.InsertStationRecord(engineNo, modelNo, orderNo, palletNo,
opName, stationName, addOilQty, extractOilQty, density, Curr_UserName);
// 5. 按类型处理称重记录
if (weighType == "放油后")
{
// 空中放油后:查找最新放油前记录(无论是否完成,支持多次测量)
if (!B_DB_Opera.QueryWeighingBeforeOil(engineNo, out _, out decimal preWeight, out _, out _, out _))
{
// 写报警代码=3发动机号错误阻断流程
WritePLC_IF(112, opName, 3);
B_DB_Opera.SaveLog_Response($"【{opName}】未找到发动机[{engineNo}]的空中称重记录", AID);
WeighingPage?.AppendLog($"[{opName}] 未找到[{engineNo}]放油前记录,进站失败");
return;
}
else
{
B_DB_Opera.SaveLog_Response($"【{opName}】空中放油后准备完成,发动机={engineNo},机型={modelNo},放油前重量={preWeight}kg", AID);
}
}
else
{
// 地面 或 空中放油前:每次都新建称重记录(支持同一产品多次测量取最新)
string recordType = weighType == "地面" ? "地面" : "空中";
B_DB_Opera.InsertWeighingRecord(opName, recordType, engineNo, modelNo, orderNo, palletNo,
0, 0, addOilQty, extractOilQty, density, residualOilUpperLimit, residualOilLowerLimit, 0, 0, 0, 0,
opName, Curr_UserName, isComplete: 0);
B_DB_Opera.SaveLog_Response($"【{opName}】{weighType}准备完成,发动机={engineNo},机型={modelNo},残油量范围={residualOilLowerLimit:F4}-{residualOilUpperLimit:F4}L", AID);
}
// 6. 回写允许工作
WritePLC_IF(66, opName, true);
}
catch (Exception err)
{
WritePLC_IF(112, opName, 1); // 报警代码=1获取数据失败
B_DB_Opera.SaveLog_Response($"【{opName}】请求工作处理失败:{err.Message}", AID);
}
}
/// <summary>
/// 处理请求保存信号 (tagTypeCodeID=19)
/// 流程:读取称重数据 → 更新过站记录 → 按类型处理称重记录 → 回写保存完成
/// </summary>
private void Weighing_HandleRequestSave(string opName, string tagID)
{
B_DB_Opera.SaveLog_Request(opName, Log_Type.ZK_MesHandler, "MIS_Function",
Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"触发信号【19-请求保存】", out long AID);
try
{
// 1. 读取PLC数据
decimal weight = Convert.ToDecimal(ReadPLC(100140, opName)); // 重量
decimal waterContent = Convert.ToDecimal(ReadPLC(100180, opName)); // 水含量
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName)); // 产品编号
long curveWeighingRecordId = 0;
// 2. 更新过站记录(离开时间+称重重量)
B_DB_Opera.UpdateStationComplete(engineNo, opName, weight);
// 3. 确定类型并处理
string weighType = GetWeighingType(opName);
decimal oilReleaseQty = 0;
decimal residualOilQty = 0;
int finalQualityFlag = 1;
string qualityMsg = "";
if (weighType == "地面")
{
// 地面:查最新记录 → 放油量=重量/密度 → 标记完成
B_DB_Opera.QueryLatestRecord(opName, engineNo,
out long recordId, out _, out decimal addOil, out decimal extractOil, out decimal dens);
curveWeighingRecordId = recordId;
oilReleaseQty = dens > 0 ? weight / dens : 0;
residualOilQty = addOil - extractOil - oilReleaseQty;
B_DB_Opera.UpdateWeighingComplete(recordId, "地面", opName,
weight, oilReleaseQty, residualOilQty, waterContent, out finalQualityFlag, out qualityMsg);
B_DB_Opera.SaveLog_Response(
$"【{opName}】地面保存完成:发动机={engineNo},重量={weight}kg放油量={oilReleaseQty:F4}L残油量={residualOilQty:F4}L合格标志={finalQualityFlag}{qualityMsg}", AID);
WeighingPage?.UpdateOilResult(WeighingPage?.GetStationIndex(opName) ?? 1, oilReleaseQty, residualOilQty);
WeighingPage?.AppendLog($"[{opName}] 保存完成:{engineNo},重量={weight:F2}kg放油量={oilReleaseQty:F4}L");
}
else if (weighType == "放油前")
{
// 空中放油前:查最新记录 → 仅写入进站重量,不标记完成
B_DB_Opera.QueryLatestRecord(opName, engineNo,
out long recordId, out _, out _, out _, out _);
curveWeighingRecordId = recordId;
B_DB_Opera.UpdateWeighingComplete(recordId, "空中放油前", opName,
weight, 0, 0, waterContent, out finalQualityFlag, out qualityMsg);
B_DB_Opera.SaveLog_Response(
$"【{opName}】空中放油前保存完成:发动机={engineNo},进站重量={weight}kg合格标志={finalQualityFlag}{qualityMsg}", AID);
WeighingPage?.AppendLog($"[{opName}] 放油前保存:{engineNo},重量={weight:F2}kg");
}
else // 放油后
{
// 空中放油后:查最新放油前记录(无论是否完成),计算放油量更新到该记录
if (B_DB_Opera.QueryWeighingBeforeOil(engineNo, out long recordId, out decimal preWeight,
out decimal addOil, out decimal extractOil, out decimal dens))
{
curveWeighingRecordId = recordId;
oilReleaseQty = dens > 0 ? (preWeight - weight) / dens : 0;
residualOilQty = addOil - extractOil - oilReleaseQty;
B_DB_Opera.UpdateWeighingComplete(recordId, "空中放油后", opName,
weight, oilReleaseQty, residualOilQty, waterContent, out finalQualityFlag, out qualityMsg);
B_DB_Opera.SaveLog_Response(
$"【{opName}】空中放油后保存完成:发动机={engineNo},放油前={preWeight}kg放油后={weight}kg放油量={oilReleaseQty:F4}L残油量={residualOilQty:F4}L合格标志={finalQualityFlag}{qualityMsg}", AID);
WeighingPage?.UpdateOilResult(WeighingPage?.GetStationIndex(opName) ?? 1, oilReleaseQty, residualOilQty);
WeighingPage?.AppendLog($"[{opName}] 放油后保存:{engineNo},放油量={oilReleaseQty:F4}L残油量={residualOilQty:F4}L");
}
else
{
// 找不到放油前记录可能直接进OP20创建一条放油后记录并标记完成
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "空中放油后", engineNo, "", "", "",
0, weight, 0, 0, 0, 0, 0, 0, 0, 2, waterContent,
opName, Curr_UserName, isComplete: 1);
finalQualityFlag = 2;
qualityMsg = "未找到放油前记录";
B_DB_Opera.SaveLog_Response(
$"【{opName}】放油后保存(无放油前记录):发动机={engineNo},放油后重量={weight}kg", AID);
WeighingPage?.AppendLog($"[{opName}] ⚠️ 放油后保存:{engineNo}(无放油前记录,已新建)");
}
}
CurveCollector_EndSegment(opName, curveWeighingRecordId);
// TODO: MES接口数据上传接口对接后实现
if (finalQualityFlag == 2 && !string.IsNullOrWhiteSpace(qualityMsg))
{
WeighingPage?.AppendLog($"[{opName}] 判定不合格:{qualityMsg}");
}
// 4. 先回写上位机判定的合格标志,再回写保存完成
WritePLC_IF(21, opName, finalQualityFlag);
WritePLC_IF(20, opName, true);
}
catch (Exception err)
{
CurveCollector_EndSegment(opName, 0);
WritePLC_IF(112, opName, 2); // 报警代码=2保存失败
B_DB_Opera.SaveLog_Response($"【{opName}】保存处理失败:{err.Message}", AID);
WeighingPage?.AppendLog($"[{opName}] ❌ 保存异常:{err.Message}");
}
}
// ====================================================================
// 扫码枪业务处理
// ====================================================================
/// <summary>
/// 扫码枪扫码完成后的业务处理
/// 将码值写入PLC地址102500触发完成信号100016=11秒后复位
/// </summary>
/// <param name="opName">工位号OP10/OP20由COM口绑定决定</param>
/// <param name="barcode">扫到的条码值</param>
public void Barcode_HandleScan(string opName, string barcode)
{
B_DB_Opera.SaveLog_Request(opName, Log_Type.ZK_MesHandler, "MIS_Function",
Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"扫码枪触发【{opName}】码值={barcode}", out long AID);
try
{
// 1. 写入码值到 PLC 地址 102500PLC_PC_扫码枪值
WritePLC_IF(102500, opName, barcode);
// 2. 写入扫码完成信号 100016 = 1PC_PLC_扫码枪扫码完成
WritePLC_IF(100016, opName, true);
// 3. 日志
B_DB_Opera.SaveLog_Response($"【{opName}】扫码完成:码值={barcode}", AID);
WeighingPage?.AppendLog($"[{opName}] 触发扫码 码值:{barcode}");
// 4. 1秒后复位 100016 = 0
System.Threading.Tasks.Task.Delay(1000).ContinueWith(_ =>
{
try { WritePLC_IF(100016, opName, false); }
catch { }
});
}
catch (Exception err)
{
B_DB_Opera.SaveLog_Response($"【{opName}】扫码处理失败:{err.Message}", AID);
WeighingPage?.AppendLog($"[{opName}] ❌ 扫码处理异常:{err.Message}");
}
}
private class CurveCollectorState
{
public string OpName;
public long SegmentId;
public Thread WorkerThread;
public volatile bool IsRunning;
public bool IsSegmentEnded;
public bool IsWaitingSave;
public int SeqNo;
public readonly object BufferLock = new object();
public readonly List<CurveSamplePoint> Buffer = new List<CurveSamplePoint>();
}
private static readonly ConcurrentDictionary<string, CurveCollectorState> _curveCollectors
= new ConcurrentDictionary<string, CurveCollectorState>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 工件到位后启动实时重量曲线采集。
/// </summary>
private void CurveCollector_Start(string opName)
{
try
{
if (_curveCollectors.TryGetValue(opName, out CurveCollectorState oldState) && !oldState.IsSegmentEnded)
{
if (oldState.IsRunning) return;
CurveCollector_EndSegment(opName, 0);
}
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName));
string modelNo = PLC_R.GetString_CleanGarbled(ReadPLC(11, opName));
long segmentId = B_DB_Opera.Curve_StartSegment(opName, engineNo, modelNo);
if (segmentId <= 0)
{
WeighingPage?.AppendLog($"[{opName}] 曲线段创建失败,未启动采集");
return;
}
var state = new CurveCollectorState
{
OpName = opName,
SegmentId = segmentId,
IsRunning = true
};
state.WorkerThread = new Thread(() => CurveCollector_Work(state))
{
IsBackground = true,
Name = $"CurveCollector_{opName}"
};
_curveCollectors[opName] = state;
state.WorkerThread.Start();
WeighingPage?.AppendLog($"[{opName}] 实时重量曲线采集启动段ID={segmentId}");
}
catch (Exception err)
{
WeighingPage?.AppendLog($"[{opName}] 曲线采集启动异常:{err.Message}");
}
}
/// <summary>
/// 采集线程200ms读取一次实时重量满10条批量写库。
/// </summary>
private void CurveCollector_Work(CurveCollectorState state)
{
while (state.IsRunning)
{
try
{
decimal weight = Convert.ToDecimal(ReadPLC(100220, state.OpName));
List<CurveSamplePoint> writePoints = null;
lock (state.BufferLock)
{
state.Buffer.Add(new CurveSamplePoint
{
SeqNo = ++state.SeqNo,
SampleTime = DateTime.Now,
Weight = weight
});
if (state.Buffer.Count >= 10)
{
writePoints = new List<CurveSamplePoint>(state.Buffer);
state.Buffer.Clear();
}
}
if (writePoints != null)
{
B_DB_Opera.Curve_WritePoints(state.SegmentId, writePoints);
}
}
catch (Exception err)
{
WeighingPage?.AppendLog($"[{state.OpName}] 曲线采样异常:{err.Message}");
}
Thread.Sleep(200);
}
CurveCollector_Flush(state);
}
/// <summary>
/// 停止采样线程并刷出剩余点,但暂不关闭曲线段。
/// </summary>
private void CurveCollector_StopSampling(string opName, bool waitingSave = false)
{
if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state)) return;
if (waitingSave)
{
state.IsWaitingSave = true;
}
state.IsRunning = false;
if (state.WorkerThread != null && state.WorkerThread.IsAlive)
{
state.WorkerThread.Join(2000);
}
CurveCollector_Flush(state);
}
/// <summary>
/// 工件离开时停止采集并关闭曲线段。
/// </summary>
private void CurveCollector_Stop(string opName)
{
if (_curveCollectors.TryGetValue(opName, out CurveCollectorState state) && state.IsWaitingSave)
{
return;
}
CurveCollector_EndSegment(opName, 0);
}
/// <summary>
/// 保存完成后关闭曲线段并关联称重记录ID。
/// </summary>
private void CurveCollector_EndSegment(string opName, long weighingRecordId)
{
if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state)) return;
CurveCollector_StopSampling(opName);
if (!state.IsSegmentEnded)
{
B_DB_Opera.Curve_EndSegment(state.SegmentId, weighingRecordId);
state.IsSegmentEnded = true;
WeighingPage?.AppendLog($"[{opName}] 实时重量曲线采集结束段ID={state.SegmentId}");
}
_curveCollectors.TryRemove(opName, out _);
}
/// <summary>
/// 将采集缓冲区剩余点写入数据库。
/// </summary>
private void CurveCollector_Flush(CurveCollectorState state)
{
List<CurveSamplePoint> writePoints = null;
lock (state.BufferLock)
{
if (state.Buffer.Count > 0)
{
writePoints = new List<CurveSamplePoint>(state.Buffer);
state.Buffer.Clear();
}
}
if (writePoints != null)
{
B_DB_Opera.Curve_WritePoints(state.SegmentId, writePoints);
}
}
}
}

518
SCADA/MAIN_PAGE.Designer.cs generated Normal file
View File

@@ -0,0 +1,518 @@
using System.Windows.Forms;
namespace MesWork
{
partial class MesWorkForm : PlcLinkForm
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
// ── Timers ──
this.timer_OnLine_Show = new System.Windows.Forms.Timer(this.components);
this.timer_Clock = new System.Windows.Forms.Timer(this.components);
// ── Top Bar ──
this.pnl_TopBar = new System.Windows.Forms.Panel();
this.pnl_TopBarRight = new System.Windows.Forms.Panel();
this.lbl_SystemTitle = new System.Windows.Forms.Label();
this.lbl_DateTime = new System.Windows.Forms.Label();
this.lbl_UserName = new System.Windows.Forms.Label();
this.pnl_SqlIndicator = new System.Windows.Forms.Panel();
this.lbl_SqlStatus = new System.Windows.Forms.Label();
// ── Sidebar ──
this.pnl_Sidebar = new System.Windows.Forms.Panel();
this.pnl_SidebarScroll = new System.Windows.Forms.Panel();
this.pnl_NavWeighing = new System.Windows.Forms.Panel();
this.lbl_NavWeighing = new System.Windows.Forms.Label();
this.pnl_NavUserMgmt = new System.Windows.Forms.Panel();
this.lbl_NavUserMgmt = new System.Windows.Forms.Label();
this.pnl_NavWorkpiece = new System.Windows.Forms.Panel();
this.lbl_NavWorkpiece = new System.Windows.Forms.Label();
this.pnl_NavTool = new System.Windows.Forms.Panel();
this.lbl_NavTool = new System.Windows.Forms.Label();
this.pnl_NavReport = new System.Windows.Forms.Panel();
this.lbl_NavReport = new System.Windows.Forms.Label();
this.pnl_NavStation = new System.Windows.Forms.Panel();
this.lbl_NavStation = new System.Windows.Forms.Label();
this.pnl_NavStatistics = new System.Windows.Forms.Panel();
this.lbl_NavStatistics = new System.Windows.Forms.Label();
this.pnl_NavSettings = new System.Windows.Forms.Panel();
this.lbl_NavSettings = new System.Windows.Forms.Label();
this.pnl_NavLog = new System.Windows.Forms.Panel();
this.lbl_NavLog = new System.Windows.Forms.Label();
this.pnl_NavPLC = new System.Windows.Forms.Panel();
this.lbl_NavPLC = new System.Windows.Forms.Label();
this.pnl_NavFullscreen = new System.Windows.Forms.Panel();
this.lbl_NavFullscreen = new System.Windows.Forms.Label();
this.pnl_NavClose = new System.Windows.Forms.Panel();
this.lbl_NavClose = new System.Windows.Forms.Label();
this.pnl_NavActiveIndicator = new System.Windows.Forms.Panel();
// ── Alarm Bar ──
this.panel_Alarm = new System.Windows.Forms.Panel();
this.label_Alarm = new System.Windows.Forms.Label();
this.lbl_AlarmIcon = new System.Windows.Forms.Label();
// ── Content ──
this.pnl_Content = new System.Windows.Forms.Panel();
this.pnl_TopBar.SuspendLayout();
this.pnl_TopBarRight.SuspendLayout();
this.pnl_Sidebar.SuspendLayout();
this.pnl_SidebarScroll.SuspendLayout();
this.panel_Alarm.SuspendLayout();
this.SuspendLayout();
// ====================================================================
// timer_OnLine_Show — 框架心跳/SQL检测
// ====================================================================
this.timer_OnLine_Show.Interval = 1000;
this.timer_OnLine_Show.Tick += new System.EventHandler(this.timer_OnLine_Show_Tick);
// ====================================================================
// timer_Clock — 时钟刷新
// ====================================================================
this.timer_Clock.Interval = 1000;
this.timer_Clock.Tick += new System.EventHandler(this.timer_Clock_Tick);
// ====================================================================
// pnl_TopBar — 顶部标题栏 (48px)
// ====================================================================
this.pnl_TopBar.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_TopBar.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_TopBar.Height = 48;
this.pnl_TopBar.Name = "pnl_TopBar";
this.pnl_TopBar.Controls.Add(this.lbl_SystemTitle);
this.pnl_TopBar.Controls.Add(this.pnl_TopBarRight);
// lbl_SystemTitle
this.lbl_SystemTitle.AutoSize = false;
this.lbl_SystemTitle.Font = new System.Drawing.Font("微软雅黑", 14F, System.Drawing.FontStyle.Bold);
this.lbl_SystemTitle.ForeColor = System.Drawing.Color.White;
this.lbl_SystemTitle.Location = new System.Drawing.Point(92, 0);
this.lbl_SystemTitle.Size = new System.Drawing.Size(400, 48);
this.lbl_SystemTitle.Name = "lbl_SystemTitle";
this.lbl_SystemTitle.Text = "⚖ 称重数据采集分析管理系统";
this.lbl_SystemTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
// ── pnl_TopBarRight — 右侧控件容器Dock=Right确保任何分辨率可见──
this.pnl_TopBarRight.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_TopBarRight.Dock = System.Windows.Forms.DockStyle.Right;
this.pnl_TopBarRight.Width = 420;
this.pnl_TopBarRight.Name = "pnl_TopBarRight";
this.pnl_TopBarRight.Controls.Add(this.pnl_SqlIndicator);
this.pnl_TopBarRight.Controls.Add(this.lbl_SqlStatus);
this.pnl_TopBarRight.Controls.Add(this.lbl_UserName);
this.pnl_TopBarRight.Controls.Add(this.lbl_DateTime);
// pnl_SqlIndicator — 数据库在线指示灯
this.pnl_SqlIndicator.BackColor = System.Drawing.Color.FromArgb(209, 213, 219);
this.pnl_SqlIndicator.Location = new System.Drawing.Point(10, 17);
this.pnl_SqlIndicator.Size = new System.Drawing.Size(14, 14);
this.pnl_SqlIndicator.Name = "pnl_SqlIndicator";
// lbl_SqlStatus — 数据库状态标签
this.lbl_SqlStatus.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_SqlStatus.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_SqlStatus.Location = new System.Drawing.Point(28, 0);
this.lbl_SqlStatus.Size = new System.Drawing.Size(55, 48);
this.lbl_SqlStatus.Name = "lbl_SqlStatus";
this.lbl_SqlStatus.Text = "数据库";
this.lbl_SqlStatus.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
// lbl_UserName
this.lbl_UserName.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_UserName.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_UserName.Location = new System.Drawing.Point(90, 0);
this.lbl_UserName.Size = new System.Drawing.Size(160, 48);
this.lbl_UserName.Name = "lbl_UserName";
this.lbl_UserName.Text = "操作员:";
this.lbl_UserName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// lbl_DateTime
this.lbl_DateTime.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateTime.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_DateTime.Location = new System.Drawing.Point(250, 0);
this.lbl_DateTime.Size = new System.Drawing.Size(165, 48);
this.lbl_DateTime.Name = "lbl_DateTime";
this.lbl_DateTime.Text = "2026-04-07 20:00:00";
this.lbl_DateTime.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
// ====================================================================
// pnl_Sidebar — 左侧导航栏 (80px, Navy背景)
// ====================================================================
this.pnl_Sidebar.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_Sidebar.Dock = System.Windows.Forms.DockStyle.Left;
this.pnl_Sidebar.Width = 80;
this.pnl_Sidebar.Name = "pnl_Sidebar";
// 全屏+退出 固定在侧栏底部
this.pnl_NavFullscreen.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pnl_NavFullscreen.Size = new System.Drawing.Size(80, 44);
this.pnl_NavClose.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pnl_NavClose.Size = new System.Drawing.Size(80, 44);
this.pnl_Sidebar.Controls.Add(this.pnl_SidebarScroll);
this.pnl_Sidebar.Controls.Add(this.pnl_NavFullscreen);
this.pnl_Sidebar.Controls.Add(this.pnl_NavClose);
// pnl_SidebarScroll — 内部滚动容器
this.pnl_SidebarScroll.AutoScroll = true;
this.pnl_SidebarScroll.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_SidebarScroll.Dock = System.Windows.Forms.DockStyle.Fill;
this.pnl_SidebarScroll.Name = "pnl_SidebarScroll";
// ── 导航项:称重作业 ──
this.pnl_NavWeighing.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavWeighing.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavWeighing.Location = new System.Drawing.Point(1, 8);
this.pnl_NavWeighing.Size = new System.Drawing.Size(78, 64);
this.pnl_NavWeighing.Name = "pnl_NavWeighing";
this.pnl_NavWeighing.Tag = "⚖\n称重作业";
this.lbl_NavWeighing.AutoSize = false;
this.lbl_NavWeighing.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavWeighing.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavWeighing.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavWeighing.Text = "⚖\n称重作业";
this.lbl_NavWeighing.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavWeighing.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavWeighing.Controls.Add(this.lbl_NavWeighing);
// ── 导航项:用户管理 ──
this.pnl_NavUserMgmt.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavUserMgmt.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavUserMgmt.Location = new System.Drawing.Point(1, 74);
this.pnl_NavUserMgmt.Size = new System.Drawing.Size(78, 64);
this.pnl_NavUserMgmt.Name = "pnl_NavUserMgmt";
this.pnl_NavUserMgmt.Tag = "👤\n用户管理";
this.lbl_NavUserMgmt.AutoSize = false;
this.lbl_NavUserMgmt.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavUserMgmt.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavUserMgmt.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavUserMgmt.Text = "👤\n用户管理";
this.lbl_NavUserMgmt.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavUserMgmt.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavUserMgmt.Controls.Add(this.lbl_NavUserMgmt);
// ── 导航项:工件管理 ──
this.pnl_NavWorkpiece.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavWorkpiece.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavWorkpiece.Location = new System.Drawing.Point(1, 140);
this.pnl_NavWorkpiece.Size = new System.Drawing.Size(78, 64);
this.pnl_NavWorkpiece.Name = "pnl_NavWorkpiece";
this.pnl_NavWorkpiece.Tag = "⚙\n工件管理";
this.lbl_NavWorkpiece.AutoSize = false;
this.lbl_NavWorkpiece.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavWorkpiece.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavWorkpiece.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavWorkpiece.Text = "⚙\n工件管理";
this.lbl_NavWorkpiece.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavWorkpiece.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavWorkpiece.Controls.Add(this.lbl_NavWorkpiece);
// ── 导航项:工具管理 ──
this.pnl_NavTool.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavTool.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavTool.Location = new System.Drawing.Point(1, 206);
this.pnl_NavTool.Size = new System.Drawing.Size(78, 64);
this.pnl_NavTool.Name = "pnl_NavTool";
this.pnl_NavTool.Tag = "🔧\n工具管理";
this.lbl_NavTool.AutoSize = false;
this.lbl_NavTool.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavTool.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavTool.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavTool.Text = "🔧\n工具管理";
this.lbl_NavTool.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavTool.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavTool.Controls.Add(this.lbl_NavTool);
// ── 导航项:报告查询 ──
this.pnl_NavReport.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavReport.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavReport.Location = new System.Drawing.Point(1, 272);
this.pnl_NavReport.Size = new System.Drawing.Size(78, 64);
this.pnl_NavReport.Name = "pnl_NavReport";
this.pnl_NavReport.Tag = "📋\n报告查询";
this.lbl_NavReport.AutoSize = false;
this.lbl_NavReport.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavReport.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavReport.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavReport.Text = "📋\n报告查询";
this.lbl_NavReport.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavReport.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavReport.Controls.Add(this.lbl_NavReport);
// ── 导航项:过站记录 ──
this.pnl_NavStation.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavStation.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavStation.Location = new System.Drawing.Point(1, 338);
this.pnl_NavStation.Size = new System.Drawing.Size(78, 64);
this.pnl_NavStation.Name = "pnl_NavStation";
this.pnl_NavStation.Tag = "📦\n过站记录";
this.lbl_NavStation.AutoSize = false;
this.lbl_NavStation.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavStation.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavStation.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavStation.Text = "📦\n过站记录";
this.lbl_NavStation.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavStation.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavStation.Controls.Add(this.lbl_NavStation);
// ── 导航项:统计分析 ──
this.pnl_NavStatistics.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavStatistics.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavStatistics.Location = new System.Drawing.Point(1, 404);
this.pnl_NavStatistics.Size = new System.Drawing.Size(78, 64);
this.pnl_NavStatistics.Name = "pnl_NavStatistics";
this.pnl_NavStatistics.Tag = "📊\n统计分析";
this.lbl_NavStatistics.AutoSize = false;
this.lbl_NavStatistics.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavStatistics.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavStatistics.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavStatistics.Text = "📊\n统计分析";
this.lbl_NavStatistics.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavStatistics.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavStatistics.Controls.Add(this.lbl_NavStatistics);
// ── 导航项:系统设置 ──
this.pnl_NavSettings.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavSettings.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavSettings.Location = new System.Drawing.Point(1, 470);
this.pnl_NavSettings.Size = new System.Drawing.Size(78, 64);
this.pnl_NavSettings.Name = "pnl_NavSettings";
this.pnl_NavSettings.Tag = "⚙\n系统设置";
this.lbl_NavSettings.AutoSize = false;
this.lbl_NavSettings.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavSettings.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavSettings.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavSettings.Text = "⚙\n系统设置";
this.lbl_NavSettings.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavSettings.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavSettings.Controls.Add(this.lbl_NavSettings);
// ── 导航项:日志查询 ──
this.pnl_NavLog.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavLog.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavLog.Location = new System.Drawing.Point(1, 536);
this.pnl_NavLog.Size = new System.Drawing.Size(78, 64);
this.pnl_NavLog.Name = "pnl_NavLog";
this.pnl_NavLog.Tag = "📝\n日志查询";
this.lbl_NavLog.AutoSize = false;
this.lbl_NavLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavLog.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavLog.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavLog.Text = "📝\n日志查询";
this.lbl_NavLog.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavLog.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavLog.Controls.Add(this.lbl_NavLog);
// ── 导航项PLC监控 ──
this.pnl_NavPLC.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavPLC.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavPLC.Location = new System.Drawing.Point(1, 602);
this.pnl_NavPLC.Size = new System.Drawing.Size(78, 64);
this.pnl_NavPLC.Name = "pnl_NavPLC";
this.pnl_NavPLC.Tag = "📡\nPLC监控";
this.lbl_NavPLC.AutoSize = false;
this.lbl_NavPLC.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavPLC.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavPLC.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavPLC.Text = "📡\nPLC监控";
this.lbl_NavPLC.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavPLC.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavPLC.Controls.Add(this.lbl_NavPLC);
// ── 全屏按钮(固定在侧栏底部,退出按钮上方) ──
this.pnl_NavFullscreen.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavFullscreen.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavFullscreen.Name = "pnl_NavFullscreen";
this.lbl_NavFullscreen.AutoSize = false;
this.lbl_NavFullscreen.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavFullscreen.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavFullscreen.ForeColor = System.Drawing.Color.FromArgb(148, 163, 184);
this.lbl_NavFullscreen.Text = "▣\n全屏";
this.lbl_NavFullscreen.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavFullscreen.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavFullscreen.Controls.Add(this.lbl_NavFullscreen);
// ── 退出按钮(固定在侧栏底部) ──
this.pnl_NavClose.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.pnl_NavClose.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavClose.Name = "pnl_NavClose";
this.pnl_NavClose.Tag = "✕\n退出";
this.lbl_NavClose.AutoSize = false;
this.lbl_NavClose.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbl_NavClose.Font = new System.Drawing.Font("微软雅黑", 9F);
this.lbl_NavClose.ForeColor = System.Drawing.Color.FromArgb(239, 68, 68);
this.lbl_NavClose.Text = "✕\n退出";
this.lbl_NavClose.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_NavClose.Cursor = System.Windows.Forms.Cursors.Hand;
this.pnl_NavClose.Controls.Add(this.lbl_NavClose);
// 导航Active指示条4px宽蓝色条初始隐藏后续代码定位
this.pnl_NavActiveIndicator.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.pnl_NavActiveIndicator.Location = new System.Drawing.Point(0, 8);
this.pnl_NavActiveIndicator.Size = new System.Drawing.Size(4, 64);
this.pnl_NavActiveIndicator.Name = "pnl_NavActiveIndicator";
// 添加导航项到滚动容器(从下往上加,确保绘制顺序)
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavActiveIndicator);
// pnl_NavClose 已从滚动容器移出到pnl_Sidebar底部
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavPLC);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavLog);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavSettings);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavStatistics);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavStation);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavReport);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavTool);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavWorkpiece);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavUserMgmt);
this.pnl_SidebarScroll.Controls.Add(this.pnl_NavWeighing);
// 绑定导航点击事件
this.pnl_NavWeighing.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavWeighing.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavUserMgmt.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavUserMgmt.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavWorkpiece.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavWorkpiece.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavTool.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavTool.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavReport.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavReport.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavStation.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavStation.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavStatistics.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavStatistics.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavSettings.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavSettings.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavLog.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavLog.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavPLC.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavPLC.Click += new System.EventHandler(this.Nav_Click);
this.pnl_NavFullscreen.Click += new System.EventHandler(this.btn_ToggleFullscreen_Click);
this.lbl_NavFullscreen.Click += new System.EventHandler(this.btn_ToggleFullscreen_Click);
this.pnl_NavClose.Click += new System.EventHandler(this.Nav_Click);
this.lbl_NavClose.Click += new System.EventHandler(this.Nav_Click);
// ====================================================================
// panel_Alarm — 底部报警栏 (32px)
// ====================================================================
this.panel_Alarm.BackColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.panel_Alarm.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel_Alarm.Height = 32;
this.panel_Alarm.Name = "panel_Alarm";
this.panel_Alarm.Controls.Add(this.label_Alarm);
this.panel_Alarm.Controls.Add(this.lbl_AlarmIcon);
// lbl_AlarmIcon
this.lbl_AlarmIcon.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_AlarmIcon.ForeColor = System.Drawing.Color.White;
this.lbl_AlarmIcon.Location = new System.Drawing.Point(4, 0);
this.lbl_AlarmIcon.Size = new System.Drawing.Size(80, 32);
this.lbl_AlarmIcon.Name = "lbl_AlarmIcon";
this.lbl_AlarmIcon.Text = "🔔 报警";
this.lbl_AlarmIcon.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
// label_Alarm — 保持原名以兼容 AppConfig.cs 中的引用
this.label_Alarm.Anchor = System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right | System.Windows.Forms.AnchorStyles.Top;
this.label_Alarm.Font = new System.Drawing.Font("微软雅黑", 10F);
this.label_Alarm.ForeColor = System.Drawing.Color.White;
this.label_Alarm.Location = new System.Drawing.Point(88, 0);
this.label_Alarm.Size = new System.Drawing.Size(1800, 32);
this.label_Alarm.Name = "label_Alarm";
this.label_Alarm.Text = "";
this.label_Alarm.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
// ====================================================================
// pnl_Content — 主内容区域 (动态加载UserControl)
// ====================================================================
this.pnl_Content.BackColor = System.Drawing.Color.FromArgb(245, 247, 250);
this.pnl_Content.Dock = System.Windows.Forms.DockStyle.Fill;
this.pnl_Content.Name = "pnl_Content";
this.pnl_Content.Padding = new System.Windows.Forms.Padding(8);
// ====================================================================
// MesWorkForm — 主窗口
// ====================================================================
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(1920, 1080);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this.Name = "MesWorkForm";
this.Text = "称重数据采集分析管理系统";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
// 添加顺序先Dock的后AddFill最后被添加但先占位
// 正确顺序Content(Fill) → Sidebar(Left) → Alarm(Bottom) → TopBar(Top)
this.Controls.Add(this.pnl_Content);
this.Controls.Add(this.pnl_Sidebar);
this.Controls.Add(this.panel_Alarm);
this.Controls.Add(this.pnl_TopBar);
this.Load += new System.EventHandler(this.MesWorkForm_Load);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MesWorkForm_FormClosing);
this.pnl_TopBar.ResumeLayout(false);
this.pnl_TopBarRight.ResumeLayout(false);
this.pnl_Sidebar.ResumeLayout(false);
this.pnl_SidebarScroll.ResumeLayout(false);
this.panel_Alarm.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
// ── Timers ──
private System.Windows.Forms.Timer timer_OnLine_Show;
private System.Windows.Forms.Timer timer_Clock;
// ── Top Bar ──
private System.Windows.Forms.Panel pnl_TopBar;
private System.Windows.Forms.Panel pnl_TopBarRight;
private System.Windows.Forms.Label lbl_SystemTitle;
private System.Windows.Forms.Label lbl_DateTime;
private System.Windows.Forms.Label lbl_UserName;
private System.Windows.Forms.Panel pnl_SqlIndicator;
private System.Windows.Forms.Label lbl_SqlStatus;
// ── Sidebar ──
private System.Windows.Forms.Panel pnl_Sidebar;
private System.Windows.Forms.Panel pnl_SidebarScroll;
private System.Windows.Forms.Panel pnl_NavWeighing;
private System.Windows.Forms.Label lbl_NavWeighing;
private System.Windows.Forms.Panel pnl_NavUserMgmt;
private System.Windows.Forms.Label lbl_NavUserMgmt;
private System.Windows.Forms.Panel pnl_NavWorkpiece;
private System.Windows.Forms.Label lbl_NavWorkpiece;
private System.Windows.Forms.Panel pnl_NavTool;
private System.Windows.Forms.Label lbl_NavTool;
private System.Windows.Forms.Panel pnl_NavReport;
private System.Windows.Forms.Label lbl_NavReport;
private System.Windows.Forms.Panel pnl_NavStation;
private System.Windows.Forms.Label lbl_NavStation;
private System.Windows.Forms.Panel pnl_NavStatistics;
private System.Windows.Forms.Label lbl_NavStatistics;
private System.Windows.Forms.Panel pnl_NavSettings;
private System.Windows.Forms.Label lbl_NavSettings;
private System.Windows.Forms.Panel pnl_NavLog;
private System.Windows.Forms.Label lbl_NavLog;
private System.Windows.Forms.Panel pnl_NavPLC;
private System.Windows.Forms.Label lbl_NavPLC;
private System.Windows.Forms.Panel pnl_NavFullscreen;
private System.Windows.Forms.Label lbl_NavFullscreen;
private System.Windows.Forms.Panel pnl_NavClose;
private System.Windows.Forms.Label lbl_NavClose;
private System.Windows.Forms.Panel pnl_NavActiveIndicator;
// ── Alarm Bar ──
private System.Windows.Forms.Panel panel_Alarm;
private System.Windows.Forms.Label label_Alarm;
private System.Windows.Forms.Label lbl_AlarmIcon;
// ── Content ──
private System.Windows.Forms.Panel pnl_Content;
}
}

322
SCADA/MAIN_PAGE.cs Normal file
View File

@@ -0,0 +1,322 @@
namespace MesWork
{
using DC_A95;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
/// <summary>
/// 主窗体 - 称重数据采集分析管理系统
/// </summary>
public partial class MesWorkForm
{
public static int Curr_AID = 0;
/// <summary>
/// 当前活跃的导航Panel
/// </summary>
private Panel _activeNavPanel = null;
public MesWorkForm()
{
Frm_Login frm_Login = new Frm_Login();
if (frm_Login.ShowDialog() == DialogResult.OK)
{
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)
{
BarcodeManager.Init();
BarcodeManager.OnBarcodeScanned = (opName, barcode) =>
{
// 在线程池中执行业务处理与PLC信号处理一致
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
try { Barcode_HandleScan(opName, barcode); }
catch { }
});
};
}
// 默认选中"称重作业"
SetActiveNav(pnl_NavWeighing);
}
else
{
Process.GetCurrentProcess().Kill();
}
}
// ====================================================================
// 导航系统
// ====================================================================
/// <summary>
/// 统一导航点击事件
/// </summary>
private void Nav_Click(object sender, EventArgs e)
{
// 获取真正的导航Panel可能点的是Label子控件
Panel navPanel;
if (sender is Label lbl)
navPanel = lbl.Parent as Panel;
else
navPanel = sender as Panel;
if (navPanel == null) return;
string navTag = navPanel.Tag?.ToString() ?? "";
// 退出按钮特殊处理
if (navTag.Contains("退出"))
{
DoClose();
return;
}
// PLC监控特殊处理弹出独立窗口
if (navTag.Contains("PLC"))
{
new OpcForm(IsDemoMesServer).Show(this);
return;
}
// 日志查询 — 内嵌页面
if (navTag.Contains("日志"))
{
SetActiveNav(navPanel);
return;
}
// 切换选中状态和内容
SetActiveNav(navPanel);
}
/// <summary>
/// 设置当前选中的导航项,高亮+加载对应内容
/// </summary>
private void SetActiveNav(Panel navPanel)
{
// 还原之前的选中项
if (_activeNavPanel != null)
{
_activeNavPanel.BackColor = Color.FromArgb(30, 58, 95);
foreach (Control c in _activeNavPanel.Controls)
if (c is Label l) l.ForeColor = Color.FromArgb(148, 163, 184);
}
// 高亮新选中项
_activeNavPanel = navPanel;
_activeNavPanel.BackColor = Color.FromArgb(42, 74, 111);
foreach (Control c in _activeNavPanel.Controls)
if (c is Label l) l.ForeColor = Color.White;
// 移动Active指示条
pnl_NavActiveIndicator.Top = navPanel.Top;
pnl_NavActiveIndicator.Visible = true;
// 加载对应内容
LoadContentByNav(navPanel.Tag?.ToString() ?? "");
}
/// <summary>
/// 页面实例缓存(避免重复创建,保持页面状态)
/// </summary>
private readonly System.Collections.Generic.Dictionary<string, UserControl> _pageCache
= new System.Collections.Generic.Dictionary<string, UserControl>();
/// <summary>
/// 根据导航Tag加载对应的UserControl
/// </summary>
private void LoadContentByNav(string navTag)
{
pnl_Content.Controls.Clear();
string pageKey = "";
UserControl uc = null;
if (navTag.Contains("称重"))
{
pageKey = "weighing";
if (!_pageCache.ContainsKey(pageKey))
{
var wp = new Pages.UC_Weighing();
_pageCache[pageKey] = wp;
MesWorkForm.WeighingPage = wp;
}
}
else if (navTag.Contains("用户"))
{
pageKey = "user";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_UserMgmt();
}
else if (navTag.Contains("工件"))
{
pageKey = "workpiece";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_WorkpieceMgmt();
}
else if (navTag.Contains("工具"))
{
pageKey = "tool";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_ToolMgmt();
}
else if (navTag.Contains("报告"))
{
pageKey = "report";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_Report();
}
else if (navTag.Contains("过站"))
{
pageKey = "station";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_StationRecord();
}
else if (navTag.Contains("统计"))
{
pageKey = "statistics";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_Statistics();
}
else if (navTag.Contains("设置"))
{
pageKey = "settings";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_SystemSettings();
}
else if (navTag.Contains("日志"))
{
pageKey = "log";
if (!_pageCache.ContainsKey(pageKey))
_pageCache[pageKey] = new Pages.UC_LogRecord();
}
if (!string.IsNullOrEmpty(pageKey) && _pageCache.ContainsKey(pageKey))
{
uc = _pageCache[pageKey];
uc.Dock = DockStyle.Fill;
pnl_Content.Controls.Add(uc);
}
}
// ====================================================================
// Timer Events
// ====================================================================
/// <summary>
/// 定时器SQL在线状态刷新 + PLC信号灯等
/// </summary>
private void timer_OnLine_Show_Tick(object sender, EventArgs e)
{
// 数据库在线状态指示(只变更指示灯颜色)
if (OnLine_Sql)
{
pnl_SqlIndicator.BackColor = Color.FromArgb(16, 185, 129); // 绿色
}
else
{
pnl_SqlIndicator.BackColor = Color.FromArgb(239, 68, 68); // 红色
}
}
/// <summary>
/// 定时器:时钟刷新
/// </summary>
private void timer_Clock_Tick(object sender, EventArgs e)
{
lbl_DateTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
// ====================================================================
// 窗口事件
// ====================================================================
private void MesWorkForm_Load(object sender, EventArgs e)
{
// 主界面加载完成
}
private void MesWorkForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
private void DoClose()
{
if (MessageBox.Show("是否确认退出程序?", "退出", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
{
timer_OnLine_Show.Enabled = false;
timer_Clock.Enabled = false;
CloseDeviceConn();
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);
Process.GetCurrentProcess().Kill();
}
}
// ====================================================================
// 全屏切换
// ====================================================================
private void btn_ToggleFullscreen_Click(object sender, EventArgs e)
{
if (this.FormBorderStyle == FormBorderStyle.None)
{
// 退出全屏 → 恢复普通最大化
this.FormBorderStyle = FormBorderStyle.Sizable;
this.WindowState = FormWindowState.Maximized;
lbl_NavFullscreen.Text = "▣\n全屏";
}
else
{
// 进入全屏 → 覆盖整个屏幕(含任务栏)
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.Bounds = Screen.PrimaryScreen.Bounds;
lbl_NavFullscreen.Text = "▣\n窗口";
}
}
// ====================================================================
// 窗口图标加载
// ====================================================================
/// <summary>
/// 加载窗口图标(多路径搜索 + exe回退
/// </summary>
private void LoadWindowIcon()
{
IconHelper.ApplyIcon(this);
}
}
}

61
SCADA/MAIN_PAGE.resx Normal file
View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<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">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,134 @@
using DC_A95;
using ExternalDataSync;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using SystemFramework;
using WebApi;
namespace MesWork
{
/// <summary>
/// 程式启动
/// </summary>
public partial class MesWorkForm
{
/// <summary>
/// 从这里启动
/// </summary>
/// <param name="e"></param>
protected override void OnShown(EventArgs e)
{
}
public void OnStart(params object[] o)
{
//【1】系统基本参数设定
ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);
Never_Know.Hello_You_All_Never_Know = Hello_You_All_Never_Know;
// 加载信号表(按设备类型对应表展开多工位)
var (dt_Tag_Mes, dt_Device) = BuildSignalTable(BasicDataTableFile);
// 保存工位名称列表及显示名映射供UC_Weighing标题使用
StationOpNames = new System.Collections.Generic.List<string>();
StationDisplayNames = new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase);
int stIdx = 1;
foreach (System.Data.DataRow row in dt_Device.Rows)
{
string opName = row["工位号"].ToString().Trim();
string dataSource = row["数据来源"].ToString().Trim(); // 称重位1, 称重位2
StationOpNames.Add(opName);
// 显示格式:"OP10-称重位1"
StationDisplayNames[opName] = $"{opName}-{dataSource}";
stIdx++;
}
LoadOPCData(dt_Tag_Mes, dt_Device, IsDemoMesServer, IsWriteMonitorLog, true, false, out string error);
if (error.Length > 0)
{
MessageBox.Show($"加载信号产生错误,程序不能启动!!错误:{error}");
Process.GetCurrentProcess().Kill();
}
Thread.Sleep(200);
StartServer();
Thread.Sleep(200);
First_TagMonitor();
var task1 = new Thread(new ThreadStart(Loop_SelectAlarmShow));
task1.IsBackground = true;
task1.Start();
// Loop_ReadRealPageShow 已移除PLC信号显示将在 UC_Weighing 中实现
// var task2 = new Thread(new ThreadStart(Loop_ReadRealPageShow));
// task2.IsBackground = true;
// task2.Start();
initServer = new InitServer(MesWorkForm.WebApi_Port);
B_DB_Opera.SaveLog_Request(Curr_Station, Log_Type.ZK_Login, "", Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"称重系统启动成功WebPort:[{MesWorkForm.WebApi_Port}]", out long AID);
}
/// <summary>
/// 构建信号表读取Excel模板按「设备类型对应表」的工位配置展开
/// 每个工位独立克隆一份模板TagID加工位号前缀如 OP10_TAG_0000001
/// 并填充对应的 工位号、数据来源、IP、DB
/// </summary>
/// <param name="excelFile">信号表Excel文件路径</param>
/// <returns>(展开后的信号表, 设备类型对应表)</returns>
private static (DataTable dtTagMes, DataTable dtDevice) BuildSignalTable(string excelFile)
{
// 读取设备类型对应表(所有启用的工位)
var dtDevice = NPOITest.ExeclHelper.ExcelToDataTable(excelFile, "设备类型对应表", true)
.Select("是否启用='1'").CopyToDataTable();
// 读取 MES_基本变量原始数据 模板(取全部启用行作为模板)
var dtTemplate = NPOITest.ExeclHelper.ExcelToDataTable(excelFile, "MES_基本变量原始数据", true)
.Select("是否启用='1'").CopyToDataTable();
// 按工位数量克隆模板
var dtTagMes = dtTemplate.Clone();
foreach (DataRow devRow in dtDevice.Rows)
{
string stationNo = devRow["工位号"].ToString().Trim(); // OP10, OP20
string dataSource = devRow["数据来源"].ToString().Trim(); // 称重位1, 称重位2
string deviceIP = devRow["设备IP"].ToString().Trim(); // 192.168.0.1
string deviceDB = devRow["DB"].ToString().Trim(); // DB1310, DB1311
// 提取纯DB号 ("DB1310" → "1310",若本身是数字则保持)
//string dbNumber = deviceDB.StartsWith("DB", StringComparison.OrdinalIgnoreCase)
// ? deviceDB.Substring(2) : deviceDB;
string dbNumber = deviceDB;
foreach (DataRow tplRow in dtTemplate.Rows)
{
var newRow = dtTagMes.NewRow();
for (int c = 0; c < dtTemplate.Columns.Count; c++)
newRow[c] = tplRow[c];
// 覆盖关键字段
newRow["TagID"] = $"{stationNo}_{tplRow["TagID"].ToString().Trim()}";
newRow["工位号"] = stationNo;
newRow["数据来源"] = dataSource;
newRow["IP"] = deviceIP;
newRow["DBName"] = dbNumber;
dtTagMes.Rows.Add(newRow);
}
}
return (dtTagMes, dtDevice);
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MesWork
{
/// <summary>
///
/// </summary>
public partial class Temp { }
/// <summary>
/// 延时刷新(框架通用方法)
/// </summary>
public partial class MesWorkForm
{
// timer_OnLine_Show_Tick 已移至 MAIN_PAGE.cs 中统一管理
/// <summary>
/// 值处理函数
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool ValueT(string value)
{
bool success = false;
if (value == "true" | value == "1")
{
success = true;
}
return success;
}
}
}

184
SCADA/Pages/CrudHelper.cs Normal file
View File

@@ -0,0 +1,184 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// CRUD页面通用辅助方法搜索框交互、弹窗控件创建、Excel导出
/// </summary>
public static class CrudHelper
{
// ====================================================================
// 搜索框占位提示交互
// ====================================================================
/// <summary>
/// 搜索框获得焦点时清除占位提示
/// </summary>
public static void SearchBox_GotFocus(TextBox txt, string hint)
{
if (txt.Text == hint) { txt.Text = ""; txt.ForeColor = Color.FromArgb(31, 41, 55); }
}
/// <summary>
/// 搜索框失去焦点时恢复占位提示
/// </summary>
public static void SearchBox_LostFocus(TextBox txt, string hint)
{
if (string.IsNullOrWhiteSpace(txt.Text)) { txt.Text = hint; txt.ForeColor = Color.FromArgb(156, 163, 175); }
}
/// <summary>
/// 搜索框回车触发搜索
/// </summary>
public static bool SearchBox_KeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) { e.SuppressKeyPress = true; return true; }
return false;
}
/// <summary>
/// 获取搜索框的实际搜索关键词(排除占位提示)
/// </summary>
public static string GetSearchKeyword(TextBox txt, string hint)
{
return txt.Text == hint ? "" : txt.Text.Trim();
}
// ====================================================================
// 编辑弹窗辅助
// ====================================================================
/// <summary>
/// 创建标准编辑弹窗框架(统一样式 + 确定/取消按钮)
/// </summary>
public static Form CreateEditDialog(string title, int width, int height)
{
var dlg = new Form
{
Text = title,
Size = new Size(width, height),
StartPosition = FormStartPosition.CenterParent,
FormBorderStyle = FormBorderStyle.FixedDialog,
MaximizeBox = false,
MinimizeBox = false,
BackColor = Color.White,
Font = new Font("微软雅黑", 11F)
};
return dlg;
}
/// <summary>
/// 向弹窗添加确定/取消按钮
/// </summary>
public static (Button btnOk, Button btnCancel) AddDialogButtons(Form dlg, ref int y)
{
y += 16;
var btnOk = new Button
{
Text = "确 定", BackColor = Color.FromArgb(74, 144, 217), ForeColor = Color.White,
FlatStyle = FlatStyle.Flat, Size = new Size(120, 36), Location = new Point(80, y),
DialogResult = DialogResult.OK
};
btnOk.FlatAppearance.BorderSize = 0;
var btnCancel = new Button
{
Text = "取 消", BackColor = Color.FromArgb(229, 231, 235), ForeColor = Color.FromArgb(55, 65, 81),
FlatStyle = FlatStyle.Flat, Size = new Size(120, 36), Location = new Point(220, y),
DialogResult = DialogResult.Cancel
};
btnCancel.FlatAppearance.BorderSize = 0;
dlg.Controls.Add(btnOk);
dlg.Controls.Add(btnCancel);
dlg.AcceptButton = btnOk;
dlg.CancelButton = btnCancel;
return (btnOk, btnCancel);
}
/// <summary>
/// 向弹窗添加文本输入字段
/// </summary>
public static TextBox AddTextField(Form dlg, string label, ref int y, string value, int labelX = 30, int inputX = 130, int inputWidth = 240, bool isPassword = false)
{
dlg.Controls.Add(new Label { Text = label, Location = new Point(labelX, y + 4), AutoSize = true });
var txt = new TextBox { Text = value ?? "", Location = new Point(inputX, y), Size = new Size(inputWidth, 28), BorderStyle = BorderStyle.FixedSingle };
if (isPassword) txt.PasswordChar = '*';
dlg.Controls.Add(txt);
y += 42;
return txt;
}
/// <summary>
/// 向弹窗添加下拉选择字段
/// </summary>
public static ComboBox AddComboField(Form dlg, string label, ref int y, string[] items, string selected, int labelX = 30, int inputX = 130, int inputWidth = 240)
{
dlg.Controls.Add(new Label { Text = label, Location = new Point(labelX, y + 4), AutoSize = true });
var cmb = new ComboBox { Location = new Point(inputX, y), Size = new Size(inputWidth, 28), DropDownStyle = ComboBoxStyle.DropDownList };
cmb.Items.AddRange(items);
cmb.Text = selected ?? items[0];
dlg.Controls.Add(cmb);
y += 42;
return cmb;
}
// ====================================================================
// 导出Excel通用方法
// ====================================================================
/// <summary>
/// 通用的Excel导出流程弹出保存对话框→查全量数据→NPOI导出→打开文件位置
/// </summary>
/// <param name="sheetName">Excel工作表名称如"工件管理"</param>
/// <param name="defaultFileName">默认文件名(如"工件管理",会自动加时间后缀)</param>
/// <param name="queryAllData">查询全量数据的委托返回DataTable</param>
/// <param name="hasData">当前是否有数据可导出</param>
public static void ExportToExcel(string sheetName, string defaultFileName, Func<DataTable> queryAllData, bool hasData)
{
if (!hasData)
{
MessageBox.Show("没有数据可导出,请先查询", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var sfd = new SaveFileDialog())
{
sfd.Filter = "Excel 文件|*.xlsx";
sfd.FileName = $"{defaultFileName}_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (sfd.ShowDialog() == DialogResult.OK)
{
try
{
DataTable dt = queryAllData();
if (dt != null && dt.Rows.Count > 0)
{
bool exported = NPOITest.ExeclHelper.DataTableToExcel(dt, sheetName, sfd.FileName);
if (exported)
{
MessageBox.Show($"导出成功!共 {dt.Rows.Count} 条记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{sfd.FileName}\"");
}
else
{
MessageBox.Show("导出失败,请检查文件是否被占用", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("查询全量数据失败,请重试", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
catch (Exception ex)
{
MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
}

177
SCADA/Pages/PagerBar.cs Normal file
View File

@@ -0,0 +1,177 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 通用分页状态栏控件 — 嵌入到任何页面底部
/// 包含:记录数标签 | 每页条数下拉 | 上一页 | 页码信息 | 下一页
/// </summary>
public class PagerBar : Panel
{
public event EventHandler PageChanged;
private Label lbl_RecordCount;
private ComboBox cmb_PageSize;
private Button btn_Prev;
private Label lbl_PageInfo;
private Button btn_Next;
private int _currentPage = 1;
private int _totalPage = 1;
private int _totalCount = 0;
private int _pageSize = 100;
public int CurrentPage => _currentPage;
public int PageSize => _pageSize;
public int TotalCount => _totalCount;
public PagerBar()
{
this.BackColor = Color.White;
this.Dock = DockStyle.Bottom;
this.Height = 40;
var tbl = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 1
};
tbl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tbl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tbl.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
this.Controls.Add(tbl);
// 左侧 — 记录数
lbl_RecordCount = new Label
{
AutoSize = true,
Anchor = AnchorStyles.Left,
Font = new Font("微软雅黑", 10F),
ForeColor = Color.FromArgb(55, 65, 81),
Margin = new Padding(16, 0, 0, 0),
Text = "共 0 条记录"
};
tbl.Controls.Add(lbl_RecordCount, 0, 0);
// 右侧 — 分页控件 (RightToLeft)
var flow = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.RightToLeft,
WrapContents = false
};
tbl.Controls.Add(flow, 1, 0);
// 下一页
btn_Next = CreatePageButton("下一页 ▶");
btn_Next.Click += (s, e) => { if (_currentPage < _totalPage) { _currentPage++; PageChanged?.Invoke(this, EventArgs.Empty); } };
flow.Controls.Add(btn_Next);
// 页码信息
lbl_PageInfo = new Label
{
AutoSize = true,
Font = new Font("微软雅黑", 10F),
ForeColor = Color.FromArgb(55, 65, 81),
Margin = new Padding(4, 10, 4, 0),
Text = "第 1 页 / 共 1 页"
};
flow.Controls.Add(lbl_PageInfo);
// 上一页
btn_Prev = CreatePageButton("◀ 上一页");
btn_Prev.Click += (s, e) => { if (_currentPage > 1) { _currentPage--; PageChanged?.Invoke(this, EventArgs.Empty); } };
flow.Controls.Add(btn_Prev);
// 每页条数下拉
cmb_PageSize = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Font = new Font("微软雅黑", 9F),
Size = new Size(70, 26),
Margin = new Padding(4, 7, 8, 0)
};
cmb_PageSize.Items.AddRange(new object[] { "50", "100", "200", "500", "1000" });
cmb_PageSize.SelectedItem = "100";
cmb_PageSize.SelectedIndexChanged += (s, e) =>
{
_pageSize = int.Parse(cmb_PageSize.Text);
_currentPage = 1;
PageChanged?.Invoke(this, EventArgs.Empty);
};
flow.Controls.Add(cmb_PageSize);
var lblSize = new Label
{
AutoSize = true,
Font = new Font("微软雅黑", 9F),
ForeColor = Color.FromArgb(100, 116, 139),
Margin = new Padding(4, 11, 0, 0),
Text = "每页"
};
flow.Controls.Add(lblSize);
}
/// <summary>
/// 重置到第1页查询按钮调用
/// </summary>
public void ResetPage() => _currentPage = 1;
/// <summary>
/// 更新分页状态SP调用后调用
/// </summary>
public void UpdateState(SqlParameter pageCountParam, SqlParameter itemCountParam)
{
_totalPage = pageCountParam.Value != DBNull.Value ? Convert.ToInt32(pageCountParam.Value) : 1;
_totalCount = itemCountParam.Value != DBNull.Value ? Convert.ToInt32(itemCountParam.Value) : 0;
if (_totalPage < 1) _totalPage = 1;
lbl_RecordCount.Text = $"共 {_totalCount} 条记录";
lbl_PageInfo.Text = $"第 {_currentPage} 页 / 共 {_totalPage} 页";
btn_Prev.Enabled = _currentPage > 1;
btn_Next.Enabled = _currentPage < _totalPage;
}
/// <summary>
/// 简易更新(直接传总条数,不分页的模块用)
/// </summary>
public void UpdateCount(int count)
{
lbl_RecordCount.Text = $"共 {count} 条记录";
}
/// <summary>
/// 创建分页 Output 参数对
/// </summary>
public static (SqlParameter pageCount, SqlParameter itemCount) CreateOutputParams()
{
return (
new SqlParameter("@PageCount", SqlDbType.Int) { Direction = ParameterDirection.Output },
new SqlParameter("@ItemCount", SqlDbType.Int) { Direction = ParameterDirection.Output }
);
}
private Button CreatePageButton(string text)
{
var btn = new Button
{
BackColor = Color.FromArgb(74, 144, 217),
Cursor = Cursors.Hand,
FlatStyle = FlatStyle.Flat,
Font = new Font("微软雅黑", 9F, FontStyle.Bold),
ForeColor = Color.White,
Margin = new Padding(4, 6, 4, 0),
Size = new Size(72, 28),
Text = text,
UseVisualStyleBackColor = false
};
btn.FlatAppearance.BorderSize = 0;
return btn;
}
}
}

262
SCADA/Pages/UC_LogRecord.Designer.cs generated Normal file
View File

@@ -0,0 +1,262 @@
namespace MesWork.Pages
{
partial class UC_LogRecord
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region
private void InitializeComponent()
{
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle10 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle11 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle12 = new System.Windows.Forms.DataGridViewCellStyle();
this.pnl_Toolbar = new System.Windows.Forms.Panel();
this.btn_Export = new System.Windows.Forms.Button();
this.btn_Refresh = new System.Windows.Forms.Button();
this.btn_Query = new System.Windows.Forms.Button();
this.txt_Keyword = new System.Windows.Forms.TextBox();
this.cmb_Type = new System.Windows.Forms.ComboBox();
this.dtp_End = new System.Windows.Forms.DateTimePicker();
this.lbl_To = new System.Windows.Forms.Label();
this.dtp_Start = new System.Windows.Forms.DateTimePicker();
this.dgv_Data = new System.Windows.Forms.DataGridView();
this.pnl_Footer = new System.Windows.Forms.Panel();
this.lbl_RecordCount = new System.Windows.Forms.Label();
this.pnl_Toolbar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).BeginInit();
this.pnl_Footer.SuspendLayout();
this.SuspendLayout();
//
// pnl_Toolbar
//
this.pnl_Toolbar.BackColor = System.Drawing.Color.White;
this.pnl_Toolbar.Controls.Add(this.btn_Export);
this.pnl_Toolbar.Controls.Add(this.btn_Refresh);
this.pnl_Toolbar.Controls.Add(this.btn_Query);
this.pnl_Toolbar.Controls.Add(this.txt_Keyword);
this.pnl_Toolbar.Controls.Add(this.cmb_Type);
this.pnl_Toolbar.Controls.Add(this.dtp_End);
this.pnl_Toolbar.Controls.Add(this.lbl_To);
this.pnl_Toolbar.Controls.Add(this.dtp_Start);
this.pnl_Toolbar.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_Toolbar.Location = new System.Drawing.Point(0, 0);
this.pnl_Toolbar.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.pnl_Toolbar.Name = "pnl_Toolbar";
this.pnl_Toolbar.Padding = new System.Windows.Forms.Padding(18, 12, 18, 12);
this.pnl_Toolbar.Size = new System.Drawing.Size(2730, 75);
this.pnl_Toolbar.TabIndex = 2;
//
// btn_Export
//
this.btn_Export.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(150)))), ((int)(((byte)(105)))));
this.btn_Export.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Export.FlatAppearance.BorderSize = 0;
this.btn_Export.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Export.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Export.ForeColor = System.Drawing.Color.White;
this.btn_Export.Location = new System.Drawing.Point(1305, 12);
this.btn_Export.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btn_Export.Name = "btn_Export";
this.btn_Export.Size = new System.Drawing.Size(180, 48);
this.btn_Export.TabIndex = 0;
this.btn_Export.Text = "📥 导出Excel";
this.btn_Export.UseVisualStyleBackColor = false;
this.btn_Export.Click += new System.EventHandler(this.btn_Export_Click);
//
// btn_Refresh
//
this.btn_Refresh.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(229)))), ((int)(((byte)(231)))), ((int)(((byte)(235)))));
this.btn_Refresh.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Refresh.FlatAppearance.BorderSize = 0;
this.btn_Refresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Refresh.Font = new System.Drawing.Font("微软雅黑", 10F);
this.btn_Refresh.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(55)))), ((int)(((byte)(65)))), ((int)(((byte)(81)))));
this.btn_Refresh.Location = new System.Drawing.Point(1170, 12);
this.btn_Refresh.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btn_Refresh.Name = "btn_Refresh";
this.btn_Refresh.Size = new System.Drawing.Size(120, 48);
this.btn_Refresh.TabIndex = 1;
this.btn_Refresh.Text = "↻ 刷新";
this.btn_Refresh.UseVisualStyleBackColor = false;
this.btn_Refresh.Click += new System.EventHandler(this.btn_Refresh_Click);
//
// btn_Query
//
this.btn_Query.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(144)))), ((int)(((byte)(217)))));
this.btn_Query.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Query.FlatAppearance.BorderSize = 0;
this.btn_Query.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Query.Font = new System.Drawing.Font("微软雅黑", 10F);
this.btn_Query.ForeColor = System.Drawing.Color.White;
this.btn_Query.Location = new System.Drawing.Point(1035, 12);
this.btn_Query.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btn_Query.Name = "btn_Query";
this.btn_Query.Size = new System.Drawing.Size(120, 48);
this.btn_Query.TabIndex = 2;
this.btn_Query.Text = "🔍 查询";
this.btn_Query.UseVisualStyleBackColor = false;
this.btn_Query.Click += new System.EventHandler(this.btn_Query_Click);
//
// txt_Keyword
//
this.txt_Keyword.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_Keyword.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_Keyword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(156)))), ((int)(((byte)(163)))), ((int)(((byte)(175)))));
this.txt_Keyword.Location = new System.Drawing.Point(712, 15);
this.txt_Keyword.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.txt_Keyword.Name = "txt_Keyword";
this.txt_Keyword.Size = new System.Drawing.Size(299, 34);
this.txt_Keyword.TabIndex = 3;
this.txt_Keyword.Text = "关键字搜索...";
//
// cmb_Type
//
this.cmb_Type.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmb_Type.Font = new System.Drawing.Font("微软雅黑", 10F);
this.cmb_Type.Location = new System.Drawing.Point(480, 15);
this.cmb_Type.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.cmb_Type.Name = "cmb_Type";
this.cmb_Type.Size = new System.Drawing.Size(208, 35);
this.cmb_Type.TabIndex = 4;
//
// dtp_End
//
this.dtp_End.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_End.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_End.Location = new System.Drawing.Point(262, 15);
this.dtp_End.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.dtp_End.Name = "dtp_End";
this.dtp_End.Size = new System.Drawing.Size(193, 34);
this.dtp_End.TabIndex = 5;
//
// lbl_To
//
this.lbl_To.AutoSize = true;
this.lbl_To.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_To.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(114)))), ((int)(((byte)(128)))));
this.lbl_To.Location = new System.Drawing.Point(222, 21);
this.lbl_To.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.lbl_To.Name = "lbl_To";
this.lbl_To.Size = new System.Drawing.Size(32, 27);
this.lbl_To.TabIndex = 6;
this.lbl_To.Text = "→";
//
// dtp_Start
//
this.dtp_Start.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_Start.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_Start.Location = new System.Drawing.Point(18, 15);
this.dtp_Start.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.dtp_Start.Name = "dtp_Start";
this.dtp_Start.Size = new System.Drawing.Size(193, 34);
this.dtp_Start.TabIndex = 7;
//
// dgv_Data
//
this.dgv_Data.AllowUserToAddRows = false;
this.dgv_Data.AllowUserToDeleteRows = false;
dataGridViewCellStyle10.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
this.dgv_Data.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle10;
this.dgv_Data.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dgv_Data.BackgroundColor = System.Drawing.Color.White;
this.dgv_Data.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgv_Data.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal;
dataGridViewCellStyle11.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
dataGridViewCellStyle11.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(232)))), ((int)(((byte)(237)))), ((int)(((byte)(242)))));
dataGridViewCellStyle11.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
dataGridViewCellStyle11.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(55)))), ((int)(((byte)(65)))), ((int)(((byte)(81)))));
dataGridViewCellStyle11.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(232)))), ((int)(((byte)(237)))), ((int)(((byte)(242)))));
dataGridViewCellStyle11.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle11.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.dgv_Data.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle11;
this.dgv_Data.ColumnHeadersHeight = 36;
dataGridViewCellStyle12.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle12.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle12.Font = new System.Drawing.Font("微软雅黑", 10F);
dataGridViewCellStyle12.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle12.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(219)))), ((int)(((byte)(234)))), ((int)(((byte)(254)))));
dataGridViewCellStyle12.SelectionForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(58)))), ((int)(((byte)(95)))));
dataGridViewCellStyle12.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dgv_Data.DefaultCellStyle = dataGridViewCellStyle12;
this.dgv_Data.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_Data.EnableHeadersVisualStyles = false;
this.dgv_Data.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dgv_Data.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(232)))), ((int)(((byte)(237)))), ((int)(((byte)(242)))));
this.dgv_Data.Location = new System.Drawing.Point(0, 75);
this.dgv_Data.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.dgv_Data.Name = "dgv_Data";
this.dgv_Data.ReadOnly = true;
this.dgv_Data.RowHeadersVisible = false;
this.dgv_Data.RowHeadersWidth = 62;
this.dgv_Data.RowTemplate.Height = 32;
this.dgv_Data.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_Data.Size = new System.Drawing.Size(2730, 1371);
this.dgv_Data.TabIndex = 0;
//
// pnl_Footer
//
this.pnl_Footer.BackColor = System.Drawing.Color.White;
this.pnl_Footer.Controls.Add(this.lbl_RecordCount);
this.pnl_Footer.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pnl_Footer.Location = new System.Drawing.Point(0, 1446);
this.pnl_Footer.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.pnl_Footer.Name = "pnl_Footer";
this.pnl_Footer.Size = new System.Drawing.Size(2730, 54);
this.pnl_Footer.TabIndex = 1;
//
// lbl_RecordCount
//
this.lbl_RecordCount.AutoSize = true;
this.lbl_RecordCount.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_RecordCount.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(114)))), ((int)(((byte)(128)))));
this.lbl_RecordCount.Location = new System.Drawing.Point(18, 9);
this.lbl_RecordCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.lbl_RecordCount.Name = "lbl_RecordCount";
this.lbl_RecordCount.Size = new System.Drawing.Size(116, 27);
this.lbl_RecordCount.TabIndex = 0;
this.lbl_RecordCount.Text = "共 0 条记录";
//
// UC_LogRecord
//
this.AutoScaleDimensions = new System.Drawing.SizeF(144F, 144F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(247)))), ((int)(((byte)(250)))));
this.Controls.Add(this.dgv_Data);
this.Controls.Add(this.pnl_Footer);
this.Controls.Add(this.pnl_Toolbar);
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.Name = "UC_LogRecord";
this.Size = new System.Drawing.Size(2730, 1500);
this.pnl_Toolbar.ResumeLayout(false);
this.pnl_Toolbar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).EndInit();
this.pnl_Footer.ResumeLayout(false);
this.pnl_Footer.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel pnl_Toolbar;
private System.Windows.Forms.DateTimePicker dtp_Start;
private System.Windows.Forms.Label lbl_To;
private System.Windows.Forms.DateTimePicker dtp_End;
private System.Windows.Forms.ComboBox cmb_Type;
private System.Windows.Forms.TextBox txt_Keyword;
private System.Windows.Forms.Button btn_Query;
private System.Windows.Forms.Button btn_Refresh;
private System.Windows.Forms.Button btn_Export;
private System.Windows.Forms.DataGridView dgv_Data;
private System.Windows.Forms.Panel pnl_Footer;
private System.Windows.Forms.Label lbl_RecordCount;
}
}

209
SCADA/Pages/UC_LogRecord.cs Normal file
View File

@@ -0,0 +1,209 @@
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 日志查询页面 — 现代化风格,统一 CRUD 模块设计
/// 直接查询 [记录_日志_项目运行日志] 表
/// </summary>
public partial class UC_LogRecord : UserControl
{
private const string KEYWORD_HINT = "关键字搜索...";
public UC_LogRecord()
{
InitializeComponent();
InitColumns();
InitFilterData();
// 绑定数据完成后自动着色
dgv_Data.DataBindingComplete += dgv_Data_DataBindingComplete;
}
private void InitColumns()
{
dgv_Data.AutoGenerateColumns = false;
dgv_Data.Columns.Clear();
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "AID", HeaderText = "AID", DataPropertyName = "AID", Visible = false });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "工位号", HeaderText = "工位号", DataPropertyName = "工位号", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "类型", HeaderText = "类型", DataPropertyName = "类型", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "请求时间", HeaderText = "请求时间", DataPropertyName = "请求时间", FillWeight = 100 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "请求内容", HeaderText = "请求内容", DataPropertyName = "请求内容", FillWeight = 200 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "响应时间", HeaderText = "响应时间", DataPropertyName = "响应时间", FillWeight = 100 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "响应内容", HeaderText = "响应内容", DataPropertyName = "响应内容", FillWeight = 200 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "CMD", HeaderText = "CMD", DataPropertyName = "CMD", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "FROM", HeaderText = "FROM", DataPropertyName = "FROM", FillWeight = 50 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "TO", HeaderText = "TO", DataPropertyName = "TO", FillWeight = 50 });
}
private void InitFilterData()
{
// 日志类型下拉
cmb_Type.Items.Add("全部");
foreach (var name in Enum.GetNames(typeof(Log_Type)))
cmb_Type.Items.Add(name);
cmb_Type.SelectedIndex = 0;
// 日期范围
dtp_End.Value = DateTime.Now.AddDays(1);
dtp_Start.Value = DateTime.Now.AddDays(-3);
// 搜索框事件
txt_Keyword.GotFocus += (s, e) => { if (txt_Keyword.Text == KEYWORD_HINT) { txt_Keyword.Text = ""; txt_Keyword.ForeColor = Color.FromArgb(31, 41, 55); } };
txt_Keyword.LostFocus += (s, e) => { if (string.IsNullOrWhiteSpace(txt_Keyword.Text)) { txt_Keyword.Text = KEYWORD_HINT; txt_Keyword.ForeColor = Color.FromArgb(156, 163, 175); } };
txt_Keyword.KeyDown += (s, e) => { if (e.KeyCode == Keys.Enter) { btn_Query_Click(s, e); e.SuppressKeyPress = true; } };
}
// ====================================================================
// 查询
// ====================================================================
private void btn_Query_Click(object sender, EventArgs e)
{
try
{
string sql = BuildQuerySql();
DataLinkMesWork.SQLCommon.ExecuteDataTable(sql, MesWorkForm.ConnectionString, out DataTable dt, out string err);
if (dt != null)
{
dgv_Data.DataSource = dt;
lbl_RecordCount.Text = $"共 {dt.Rows.Count} 条记录";
}
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 数据绑定完成后按日志类型着色(首次加载和每次查询都会触发)
/// </summary>
private void dgv_Data_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
for (int i = 0; i < dgv_Data.Rows.Count; i++)
{
string typeStr = dgv_Data.Rows[i].Cells["类型"]?.Value?.ToString() ?? "";
Color rowColor;
switch (typeStr)
{
case "ZK_Alarm":
case "ZK_Delete":
rowColor = Color.FromArgb(254, 226, 226); // 红底
break;
case "ZK_Login":
rowColor = Color.FromArgb(220, 252, 231); // 绿底
break;
case "ZK_Tip":
rowColor = Color.FromArgb(254, 249, 195); // 黄底
break;
default:
rowColor = (i % 2 == 0) ? Color.White : Color.FromArgb(248, 250, 252);
break;
}
dgv_Data.Rows[i].DefaultCellStyle.BackColor = rowColor;
}
dgv_Data.ClearSelection();
}
/// <summary>
/// 导出 Excel重新查询全量数据后导出
/// </summary>
private void btn_Export_Click(object sender, EventArgs e)
{
if (dgv_Data.Rows.Count == 0)
{
MessageBox.Show("没有数据可导出,请先查询", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var sfd = new SaveFileDialog())
{
sfd.Filter = "Excel 文件|*.xlsx";
sfd.FileName = $"运行日志_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (sfd.ShowDialog() == DialogResult.OK)
{
try
{
string sql = BuildQuerySql();
DataLinkMesWork.SQLCommon.ExecuteDataTable(sql, MesWorkForm.ConnectionString, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0)
{
bool exported = NPOITest.ExeclHelper.DataTableToExcel(dt, "运行日志", sfd.FileName);
if (exported)
{
MessageBox.Show($"导出成功!共 {dt.Rows.Count} 条记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{sfd.FileName}\"");
}
else
{
MessageBox.Show("导出失败,请检查文件是否被占用", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("查询数据失败,请重试", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
catch (Exception ex)
{
MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
/// <summary>
/// 根据当前筛选条件构建日志查询SQL查询和导出共用
/// </summary>
private string BuildQuerySql(bool includeAID = true)
{
var start = dtp_Start.Value;
var end = dtp_End.Value;
string keyword = txt_Keyword.Text == KEYWORD_HINT ? "" : txt_Keyword.Text.Trim();
string logType = cmb_Type.Text == "全部" ? "" : cmb_Type.Text;
string where = $" AND [请求时间] >= '{start:G}' AND [请求时间] < '{end:yyyy-MM-dd 23:59:59}'";
if (!string.IsNullOrEmpty(keyword))
where += $" AND ([请求内容] LIKE '%{keyword}%' OR [响应内容] LIKE '%{keyword}%' OR [工位号] LIKE '%{keyword}%')";
if (!string.IsNullOrEmpty(logType))
where += $" AND [日志类型] = '{logType}'";
string sql =
" SELECT [工位号] '工位号'" +
",[日志类型] '类型'" +
",[请求时间] '请求时间'" +
",[请求内容] '请求内容'" +
",[响应时间] '响应时间'" +
",[响应内容] '响应内容'" +
",[请求CMD] 'CMD'" +
",[请求FROM] 'FROM'" +
",[请求TO] 'TO'" +
(includeAID ? ",[AID] 'AID'" : "") +
" FROM [记录_日志_项目运行日志]" +
" WHERE 1=1 " + where +
" ORDER BY [请求时间] DESC";
return sql;
}
private void btn_Refresh_Click(object sender, EventArgs e)
{
cmb_Type.SelectedIndex = 0;
txt_Keyword.Text = KEYWORD_HINT;
txt_Keyword.ForeColor = Color.FromArgb(156, 163, 175);
dtp_Start.Value = DateTime.Now.AddDays(-3);
dtp_End.Value = DateTime.Now.AddDays(1);
btn_Query_Click(sender, e);
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (Visible && dgv_Data.DataSource == null)
btn_Query_Click(this, EventArgs.Empty);
}
}
}

View File

@@ -0,0 +1,120 @@
<?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">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

207
SCADA/Pages/UC_Report.Designer.cs generated Normal file
View File

@@ -0,0 +1,207 @@
namespace MesWork.Pages
{
partial class UC_Report
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region
private void InitializeComponent()
{
this.pnl_QueryBar = new System.Windows.Forms.Panel();
this.lbl_DateFrom = new System.Windows.Forms.Label();
this.dtp_Start = new System.Windows.Forms.DateTimePicker();
this.lbl_DateTo = new System.Windows.Forms.Label();
this.dtp_End = new System.Windows.Forms.DateTimePicker();
this.lbl_EngineNo = new System.Windows.Forms.Label();
this.txt_EngineNo = new System.Windows.Forms.TextBox();
this.lbl_ModelNo = new System.Windows.Forms.Label();
this.txt_ModelNo = new System.Windows.Forms.TextBox();
this.btn_Query = new System.Windows.Forms.Button();
this.btn_Export = new System.Windows.Forms.Button();
this.dgv_Data = new System.Windows.Forms.DataGridView();
this.pnl_StatusBar = new System.Windows.Forms.Panel();
this.pnl_QueryBar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).BeginInit();
this.SuspendLayout();
//
// pnl_QueryBar — 查询条件栏
//
this.pnl_QueryBar.BackColor = System.Drawing.Color.White;
this.pnl_QueryBar.Controls.Add(this.btn_Export);
this.pnl_QueryBar.Controls.Add(this.btn_Query);
this.pnl_QueryBar.Controls.Add(this.txt_ModelNo);
this.pnl_QueryBar.Controls.Add(this.lbl_ModelNo);
this.pnl_QueryBar.Controls.Add(this.txt_EngineNo);
this.pnl_QueryBar.Controls.Add(this.lbl_EngineNo);
this.pnl_QueryBar.Controls.Add(this.dtp_End);
this.pnl_QueryBar.Controls.Add(this.lbl_DateTo);
this.pnl_QueryBar.Controls.Add(this.dtp_Start);
this.pnl_QueryBar.Controls.Add(this.lbl_DateFrom);
this.pnl_QueryBar.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_QueryBar.Height = 56;
this.pnl_QueryBar.Name = "pnl_QueryBar";
this.pnl_QueryBar.Padding = new System.Windows.Forms.Padding(16, 12, 16, 12);
//
// 日期范围
//
this.lbl_DateFrom.AutoSize = true;
this.lbl_DateFrom.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateFrom.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_DateFrom.Location = new System.Drawing.Point(16, 17);
this.lbl_DateFrom.Name = "lbl_DateFrom";
this.lbl_DateFrom.Text = "从";
//
this.dtp_Start.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_Start.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_Start.Location = new System.Drawing.Point(40, 13);
this.dtp_Start.Name = "dtp_Start";
this.dtp_Start.Size = new System.Drawing.Size(130, 25);
//
this.lbl_DateTo.AutoSize = true;
this.lbl_DateTo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateTo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_DateTo.Location = new System.Drawing.Point(178, 17);
this.lbl_DateTo.Name = "lbl_DateTo";
this.lbl_DateTo.Text = "到";
//
this.dtp_End.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_End.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_End.Location = new System.Drawing.Point(200, 13);
this.dtp_End.Name = "dtp_End";
this.dtp_End.Size = new System.Drawing.Size(130, 25);
//
// 发动机号
//
this.lbl_EngineNo.AutoSize = true;
this.lbl_EngineNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_EngineNo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_EngineNo.Location = new System.Drawing.Point(360, 17);
this.lbl_EngineNo.Name = "lbl_EngineNo";
this.lbl_EngineNo.Text = "发动机号";
this.txt_EngineNo.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_EngineNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_EngineNo.Location = new System.Drawing.Point(430, 13);
this.txt_EngineNo.Name = "txt_EngineNo";
this.txt_EngineNo.Size = new System.Drawing.Size(160, 25);
//
// 机型号
//
this.lbl_ModelNo.AutoSize = true;
this.lbl_ModelNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_ModelNo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_ModelNo.Location = new System.Drawing.Point(610, 17);
this.lbl_ModelNo.Name = "lbl_ModelNo";
this.lbl_ModelNo.Text = "机型号";
this.txt_ModelNo.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_ModelNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_ModelNo.Location = new System.Drawing.Point(666, 13);
this.txt_ModelNo.Name = "txt_ModelNo";
this.txt_ModelNo.Size = new System.Drawing.Size(160, 25);
//
// btn_Query — 查询
//
this.btn_Query.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.btn_Query.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Query.FlatAppearance.BorderSize = 0;
this.btn_Query.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Query.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Query.ForeColor = System.Drawing.Color.White;
this.btn_Query.Location = new System.Drawing.Point(850, 12);
this.btn_Query.Name = "btn_Query";
this.btn_Query.Size = new System.Drawing.Size(88, 30);
this.btn_Query.Text = "🔍 查询";
this.btn_Query.UseVisualStyleBackColor = false;
this.btn_Query.Click += new System.EventHandler(this.btn_Query_Click);
//
// btn_Export — 导出Excel
//
this.btn_Export.BackColor = System.Drawing.Color.FromArgb(5, 150, 105);
this.btn_Export.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Export.FlatAppearance.BorderSize = 0;
this.btn_Export.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Export.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Export.ForeColor = System.Drawing.Color.White;
this.btn_Export.Location = new System.Drawing.Point(950, 12);
this.btn_Export.Name = "btn_Export";
this.btn_Export.Size = new System.Drawing.Size(104, 30);
this.btn_Export.Text = "📥 导出 Excel";
this.btn_Export.UseVisualStyleBackColor = false;
this.btn_Export.Click += new System.EventHandler(this.btn_Export_Click);
//
// dgv_Data — 数据表格
//
this.dgv_Data.AllowUserToAddRows = false;
this.dgv_Data.AllowUserToDeleteRows = false;
this.dgv_Data.AllowUserToResizeRows = false;
this.dgv_Data.BackgroundColor = System.Drawing.Color.White;
this.dgv_Data.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgv_Data.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal;
this.dgv_Data.ColumnHeadersHeight = 42;
this.dgv_Data.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
this.dgv_Data.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(232, 237, 242);
this.dgv_Data.ColumnHeadersDefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Bold);
this.dgv_Data.DefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(31, 41, 55);
this.dgv_Data.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 11F);
this.dgv_Data.DefaultCellStyle.SelectionBackColor = System.Drawing.Color.FromArgb(219, 234, 254);
this.dgv_Data.DefaultCellStyle.SelectionForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.AlternatingRowsDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(248, 250, 252);
this.dgv_Data.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_Data.EnableHeadersVisualStyles = false;
this.dgv_Data.GridColor = System.Drawing.Color.FromArgb(229, 231, 235);
this.dgv_Data.MultiSelect = false;
this.dgv_Data.Name = "dgv_Data";
this.dgv_Data.ReadOnly = true;
this.dgv_Data.RowHeadersVisible = false;
this.dgv_Data.RowTemplate.Height = 38;
this.dgv_Data.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_Data.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
//
// pnl_StatusBar — 占位构造函数中会被PagerBar替换
//
this.pnl_StatusBar.BackColor = System.Drawing.Color.White;
this.pnl_StatusBar.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pnl_StatusBar.Height = 40;
this.pnl_StatusBar.Name = "pnl_StatusBar";
//
// UC_Report
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(245, 247, 250);
this.Controls.Add(this.dgv_Data);
this.Controls.Add(this.pnl_StatusBar);
this.Controls.Add(this.pnl_QueryBar);
this.Name = "UC_Report";
this.Size = new System.Drawing.Size(1820, 1000);
this.pnl_QueryBar.ResumeLayout(false);
this.pnl_QueryBar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel pnl_QueryBar;
private System.Windows.Forms.Label lbl_DateFrom;
private System.Windows.Forms.DateTimePicker dtp_Start;
private System.Windows.Forms.Label lbl_DateTo;
private System.Windows.Forms.DateTimePicker dtp_End;
private System.Windows.Forms.Label lbl_EngineNo;
private System.Windows.Forms.TextBox txt_EngineNo;
private System.Windows.Forms.Label lbl_ModelNo;
private System.Windows.Forms.TextBox txt_ModelNo;
private System.Windows.Forms.Button btn_Query;
private System.Windows.Forms.Button btn_Export;
private System.Windows.Forms.DataGridView dgv_Data;
private System.Windows.Forms.Panel pnl_StatusBar;
}
}

153
SCADA/Pages/UC_Report.cs Normal file
View File

@@ -0,0 +1,153 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 报告查询页面 — 历史称重数据检索与 Excel 导出
/// 对应存储过程称重记录_分页查询使用 sp_xt_pagesplit 真分页)
/// </summary>
public partial class UC_Report : UserControl
{
private PagerBar _pager;
public UC_Report()
{
InitializeComponent();
InitColumns();
dtp_Start.Value = DateTime.Today.AddDays(-7);
dtp_End.Value = DateTime.Today;
// 用PagerBar替换原始pnl_StatusBar
_pager = new PagerBar();
_pager.PageChanged += (s, e) => LoadData();
this.Controls.Remove(pnl_StatusBar);
this.Controls.Add(_pager);
this.Controls.SetChildIndex(_pager, this.Controls.Count - 2);
}
/// <summary>
/// 预设表格列
/// </summary>
private void InitColumns()
{
dgv_Data.AutoGenerateColumns = false;
dgv_Data.Columns.Clear();
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "ID", HeaderText = "ID", DataPropertyName = "ID", Visible = false });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "发动机号", HeaderText = "发动机号", DataPropertyName = "发动机号", FillWeight = 100 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "机型号", HeaderText = "机型号", DataPropertyName = "机型号", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "订单号", HeaderText = "订单号", DataPropertyName = "订单号", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "称重类型", HeaderText = "称重类型", DataPropertyName = "称重类型", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "托盘号", HeaderText = "托盘号", DataPropertyName = "托盘号", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "进站重量", HeaderText = "进站重量(KG)", DataPropertyName = "进站重量", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "离站重量", HeaderText = "离站重量(KG)", DataPropertyName = "离站重量", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "加油量", HeaderText = "加油量", DataPropertyName = "加油量", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "抽油量", HeaderText = "抽油量", DataPropertyName = "抽油量", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "放油量", HeaderText = "放油量", DataPropertyName = "放油量", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "残留量", HeaderText = "残留量", DataPropertyName = "残留量", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "油密度", HeaderText = "油密度", DataPropertyName = "油密度", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "合格标志", HeaderText = "合格", DataPropertyName = "合格标志", FillWeight = 50 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "操作员", HeaderText = "操作员", DataPropertyName = "操作员", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "进站时间", HeaderText = "检测时间", DataPropertyName = "进站时间", FillWeight = 100 });
}
/// <summary>
/// 查询按钮重置到第1页后查询
/// </summary>
private void btn_Query_Click(object sender, EventArgs e)
{
_pager.ResetPage();
LoadData();
}
/// <summary>
/// 核心查询方法
/// </summary>
private void LoadData()
{
try
{
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", txt_EngineNo.Text.Trim()),
new SqlParameter("@机型号", txt_ModelNo.Text.Trim()),
new SqlParameter("@开始时间", dtp_Start.Value.ToString("yyyy-MM-dd")),
new SqlParameter("@结束时间", dtp_End.Value.AddDays(1).ToString("yyyy-MM-dd")),
new SqlParameter("@PageCurrent", _pager.CurrentPage),
new SqlParameter("@PageSize", _pager.PageSize),
pageCountParam,
itemCountParam
};
SqlOperation.ExecuteStoredProcedure("称重记录_分页查询", parms, out DataTable dt, out string err);
if (dt != null) dgv_Data.DataSource = dt;
_pager.UpdateState(pageCountParam, itemCountParam);
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 导出 Excel重新查询全量数据后导出
/// </summary>
private void btn_Export_Click(object sender, EventArgs e)
{
if (dgv_Data.Rows.Count == 0)
{
MessageBox.Show("没有数据可导出,请先查询", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var sfd = new SaveFileDialog())
{
sfd.Filter = "Excel 文件|*.xlsx";
sfd.FileName = $"称重报告_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (sfd.ShowDialog() == DialogResult.OK)
{
try
{
// 用当前查询条件重新查询全量数据(不受分页限制)
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", txt_EngineNo.Text.Trim()),
new SqlParameter("@机型号", txt_ModelNo.Text.Trim()),
new SqlParameter("@开始时间", dtp_Start.Value.ToString("yyyy-MM-dd")),
new SqlParameter("@结束时间", dtp_End.Value.AddDays(1).ToString("yyyy-MM-dd")),
new SqlParameter("@PageCurrent", 1),
new SqlParameter("@PageSize", 9999999),
pageCountParam,
itemCountParam
};
SqlOperation.ExecuteStoredProcedure("称重记录_分页查询", parms, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0)
{
bool exported = NPOITest.ExeclHelper.DataTableToExcel(dt, "称重记录", sfd.FileName);
if (exported)
{
MessageBox.Show($"导出成功!共 {dt.Rows.Count} 条记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{sfd.FileName}\"");
}
else
{
MessageBox.Show("导出失败,请检查文件是否被占用", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("查询全量数据失败,请重试", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
catch (Exception ex)
{
MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<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">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata"><xsd:complexType><xsd:sequence><xsd:element name="value" type="xsd:string" minOccurs="0" /></xsd:sequence><xsd:attribute name="name" use="required" type="xsd:string" /><xsd:attribute name="type" type="xsd:string" /><xsd:attribute name="mimetype" type="xsd:string" /><xsd:attribute ref="xml:space" /></xsd:complexType></xsd:element>
<xsd:element name="assembly"><xsd:complexType><xsd:attribute name="alias" type="xsd:string" /><xsd:attribute name="name" type="xsd:string" /></xsd:complexType></xsd:element>
<xsd:element name="data"><xsd:complexType><xsd:sequence><xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /><xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /></xsd:sequence><xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /><xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /><xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /><xsd:attribute ref="xml:space" /></xsd:complexType></xsd:element>
<xsd:element name="resheader"><xsd:complexType><xsd:sequence><xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /></xsd:sequence><xsd:attribute name="name" type="xsd:string" use="required" /></xsd:complexType></xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader>
<resheader name="version"><value>2.0</value></resheader>
<resheader name="reader"><value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader>
<resheader name="writer"><value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader>
</root>

186
SCADA/Pages/UC_StationRecord.Designer.cs generated Normal file
View File

@@ -0,0 +1,186 @@
namespace MesWork.Pages
{
partial class UC_StationRecord
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region
private void InitializeComponent()
{
this.pnl_QueryBar = new System.Windows.Forms.Panel();
this.lbl_DateFrom = new System.Windows.Forms.Label();
this.dtp_Start = new System.Windows.Forms.DateTimePicker();
this.lbl_DateTo = new System.Windows.Forms.Label();
this.dtp_End = new System.Windows.Forms.DateTimePicker();
this.lbl_EngineNo = new System.Windows.Forms.Label();
this.txt_EngineNo = new System.Windows.Forms.TextBox();
this.lbl_ModelNo = new System.Windows.Forms.Label();
this.txt_ModelNo = new System.Windows.Forms.TextBox();
this.btn_Query = new System.Windows.Forms.Button();
this.btn_Export = new System.Windows.Forms.Button();
this.dgv_Data = new System.Windows.Forms.DataGridView();
this.pnl_StatusBar = new System.Windows.Forms.Panel();
this.pnl_QueryBar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).BeginInit();
this.SuspendLayout();
// pnl_QueryBar
this.pnl_QueryBar.BackColor = System.Drawing.Color.White;
this.pnl_QueryBar.Controls.Add(this.btn_Export);
this.pnl_QueryBar.Controls.Add(this.btn_Query);
this.pnl_QueryBar.Controls.Add(this.txt_ModelNo);
this.pnl_QueryBar.Controls.Add(this.lbl_ModelNo);
this.pnl_QueryBar.Controls.Add(this.txt_EngineNo);
this.pnl_QueryBar.Controls.Add(this.lbl_EngineNo);
this.pnl_QueryBar.Controls.Add(this.dtp_End);
this.pnl_QueryBar.Controls.Add(this.lbl_DateTo);
this.pnl_QueryBar.Controls.Add(this.dtp_Start);
this.pnl_QueryBar.Controls.Add(this.lbl_DateFrom);
this.pnl_QueryBar.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_QueryBar.Height = 56;
this.pnl_QueryBar.Name = "pnl_QueryBar";
this.pnl_QueryBar.Padding = new System.Windows.Forms.Padding(16, 12, 16, 12);
// 日期范围
this.lbl_DateFrom.AutoSize = true;
this.lbl_DateFrom.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateFrom.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_DateFrom.Location = new System.Drawing.Point(16, 17);
this.lbl_DateFrom.Name = "lbl_DateFrom";
this.lbl_DateFrom.Text = "从";
this.dtp_Start.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_Start.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_Start.Location = new System.Drawing.Point(40, 13);
this.dtp_Start.Name = "dtp_Start";
this.dtp_Start.Size = new System.Drawing.Size(130, 25);
this.lbl_DateTo.AutoSize = true;
this.lbl_DateTo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateTo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_DateTo.Location = new System.Drawing.Point(178, 17);
this.lbl_DateTo.Name = "lbl_DateTo";
this.lbl_DateTo.Text = "到";
this.dtp_End.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_End.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_End.Location = new System.Drawing.Point(200, 13);
this.dtp_End.Name = "dtp_End";
this.dtp_End.Size = new System.Drawing.Size(130, 25);
// 发动机号
this.lbl_EngineNo.AutoSize = true;
this.lbl_EngineNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_EngineNo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_EngineNo.Location = new System.Drawing.Point(360, 17);
this.lbl_EngineNo.Name = "lbl_EngineNo";
this.lbl_EngineNo.Text = "发动机号";
this.txt_EngineNo.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_EngineNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_EngineNo.Location = new System.Drawing.Point(430, 13);
this.txt_EngineNo.Name = "txt_EngineNo";
this.txt_EngineNo.Size = new System.Drawing.Size(160, 25);
// 机型号
this.lbl_ModelNo.AutoSize = true;
this.lbl_ModelNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_ModelNo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_ModelNo.Location = new System.Drawing.Point(610, 17);
this.lbl_ModelNo.Name = "lbl_ModelNo";
this.lbl_ModelNo.Text = "机型号";
this.txt_ModelNo.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_ModelNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_ModelNo.Location = new System.Drawing.Point(666, 13);
this.txt_ModelNo.Name = "txt_ModelNo";
this.txt_ModelNo.Size = new System.Drawing.Size(160, 25);
// btn_Query
this.btn_Query.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.btn_Query.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Query.FlatAppearance.BorderSize = 0;
this.btn_Query.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Query.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Query.ForeColor = System.Drawing.Color.White;
this.btn_Query.Location = new System.Drawing.Point(850, 12);
this.btn_Query.Name = "btn_Query";
this.btn_Query.Size = new System.Drawing.Size(88, 30);
this.btn_Query.Text = "🔍 查询";
this.btn_Query.UseVisualStyleBackColor = false;
this.btn_Query.Click += new System.EventHandler(this.btn_Query_Click);
// btn_Export — 导出Excel
this.btn_Export.BackColor = System.Drawing.Color.FromArgb(5, 150, 105);
this.btn_Export.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Export.FlatAppearance.BorderSize = 0;
this.btn_Export.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Export.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Export.ForeColor = System.Drawing.Color.White;
this.btn_Export.Location = new System.Drawing.Point(950, 12);
this.btn_Export.Name = "btn_Export";
this.btn_Export.Size = new System.Drawing.Size(104, 30);
this.btn_Export.Text = "📥 导出 Excel";
this.btn_Export.UseVisualStyleBackColor = false;
this.btn_Export.Click += new System.EventHandler(this.btn_Export_Click);
// dgv_Data
this.dgv_Data.AllowUserToAddRows = false;
this.dgv_Data.AllowUserToDeleteRows = false;
this.dgv_Data.AllowUserToResizeRows = false;
this.dgv_Data.BackgroundColor = System.Drawing.Color.White;
this.dgv_Data.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgv_Data.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal;
this.dgv_Data.ColumnHeadersHeight = 42;
this.dgv_Data.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
this.dgv_Data.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(232, 237, 242);
this.dgv_Data.ColumnHeadersDefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Bold);
this.dgv_Data.DefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(31, 41, 55);
this.dgv_Data.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 11F);
this.dgv_Data.DefaultCellStyle.SelectionBackColor = System.Drawing.Color.FromArgb(219, 234, 254);
this.dgv_Data.DefaultCellStyle.SelectionForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.AlternatingRowsDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(248, 250, 252);
this.dgv_Data.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_Data.EnableHeadersVisualStyles = false;
this.dgv_Data.GridColor = System.Drawing.Color.FromArgb(229, 231, 235);
this.dgv_Data.MultiSelect = false;
this.dgv_Data.Name = "dgv_Data";
this.dgv_Data.ReadOnly = true;
this.dgv_Data.RowHeadersVisible = false;
this.dgv_Data.RowTemplate.Height = 38;
this.dgv_Data.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_Data.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
// pnl_StatusBar
this.pnl_StatusBar.BackColor = System.Drawing.Color.White;
this.pnl_StatusBar.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pnl_StatusBar.Height = 40;
this.pnl_StatusBar.Name = "pnl_StatusBar";
// UC_StationRecord
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(245, 247, 250);
this.Controls.Add(this.dgv_Data);
this.Controls.Add(this.pnl_StatusBar);
this.Controls.Add(this.pnl_QueryBar);
this.Name = "UC_StationRecord";
this.Size = new System.Drawing.Size(1820, 1000);
this.pnl_QueryBar.ResumeLayout(false);
this.pnl_QueryBar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel pnl_QueryBar;
private System.Windows.Forms.Label lbl_DateFrom;
private System.Windows.Forms.DateTimePicker dtp_Start;
private System.Windows.Forms.Label lbl_DateTo;
private System.Windows.Forms.DateTimePicker dtp_End;
private System.Windows.Forms.Label lbl_EngineNo;
private System.Windows.Forms.TextBox txt_EngineNo;
private System.Windows.Forms.Label lbl_ModelNo;
private System.Windows.Forms.TextBox txt_ModelNo;
private System.Windows.Forms.Button btn_Query;
private System.Windows.Forms.Button btn_Export;
private System.Windows.Forms.DataGridView dgv_Data;
private System.Windows.Forms.Panel pnl_StatusBar;
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 过站记录查询页面 — 每个产品经过每个工位的明细记录
/// 对应存储过程过站记录_分页查询
/// </summary>
public partial class UC_StationRecord : UserControl
{
private PagerBar _pager;
public UC_StationRecord()
{
InitializeComponent();
InitColumns();
dtp_Start.Value = DateTime.Today.AddDays(-7);
dtp_End.Value = DateTime.Today;
_pager = new PagerBar();
_pager.PageChanged += (s, e) => LoadData();
this.Controls.Remove(pnl_StatusBar);
this.Controls.Add(_pager);
this.Controls.SetChildIndex(_pager, this.Controls.Count - 2);
}
private void InitColumns()
{
dgv_Data.AutoGenerateColumns = false;
dgv_Data.Columns.Clear();
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "ID", HeaderText = "ID", DataPropertyName = "ID", Visible = false });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "发动机号", HeaderText = "发动机号", DataPropertyName = "发动机号", FillWeight = 100 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "机型号", HeaderText = "机型号", DataPropertyName = "机型号", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "工单号", HeaderText = "订单号", DataPropertyName = "工单号", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "工位号", HeaderText = "工位号", DataPropertyName = "工位号", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "工位名称", HeaderText = "工位名称", DataPropertyName = "工位名称", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "到站时间", HeaderText = "到达时间", DataPropertyName = "到站时间", FillWeight = 110,
DefaultCellStyle = new DataGridViewCellStyle { Format = "yyyy/MM/dd HH:mm:ss" } });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "离站时间", HeaderText = "离开时间", DataPropertyName = "离站时间", FillWeight = 110,
DefaultCellStyle = new DataGridViewCellStyle { Format = "yyyy/MM/dd HH:mm:ss" } });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "停站时间", HeaderText = "在位(秒)", DataPropertyName = "停站时间", FillWeight = 55 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "称重重量", HeaderText = "称重重量(kg)", DataPropertyName = "称重重量", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "加油量", HeaderText = "加油量", DataPropertyName = "加油量", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "抽油量", HeaderText = "抽油量", DataPropertyName = "抽油量", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "油密度", HeaderText = "油密度", DataPropertyName = "油密度", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "操作者", HeaderText = "操作员", DataPropertyName = "操作者", FillWeight = 60 });
}
private void btn_Query_Click(object sender, EventArgs e)
{
_pager.ResetPage();
LoadData();
}
private void LoadData()
{
try
{
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", txt_EngineNo.Text.Trim()),
new SqlParameter("@机型号", txt_ModelNo.Text.Trim()),
new SqlParameter("@开始时间", dtp_Start.Value.ToString("yyyy-MM-dd")),
new SqlParameter("@结束时间", dtp_End.Value.AddDays(1).ToString("yyyy-MM-dd")),
new SqlParameter("@PageCurrent", _pager.CurrentPage),
new SqlParameter("@PageSize", _pager.PageSize),
pageCountParam,
itemCountParam
};
SqlOperation.ExecuteStoredProcedure("过站记录_分页查询", parms, out DataTable dt, out string err);
if (dt != null) dgv_Data.DataSource = dt;
_pager.UpdateState(pageCountParam, itemCountParam);
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btn_Export_Click(object sender, EventArgs e)
{
if (dgv_Data.Rows.Count == 0)
{
MessageBox.Show("没有数据可导出,请先查询", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var sfd = new SaveFileDialog())
{
sfd.Filter = "Excel 文件|*.xlsx";
sfd.FileName = $"过站记录_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (sfd.ShowDialog() == DialogResult.OK)
{
try
{
// 用当前查询条件重新查询全量数据(不受分页限制)
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", txt_EngineNo.Text.Trim()),
new SqlParameter("@机型号", txt_ModelNo.Text.Trim()),
new SqlParameter("@开始时间", dtp_Start.Value.ToString("yyyy-MM-dd")),
new SqlParameter("@结束时间", dtp_End.Value.AddDays(1).ToString("yyyy-MM-dd")),
new SqlParameter("@PageCurrent", 1),
new SqlParameter("@PageSize", 9999999),
pageCountParam,
itemCountParam
};
SqlOperation.ExecuteStoredProcedure("过站记录_分页查询", parms, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0)
{
bool exported = NPOITest.ExeclHelper.DataTableToExcel(dt, "过站记录", sfd.FileName);
if (exported)
{
MessageBox.Show($"导出成功!共 {dt.Rows.Count} 条记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{sfd.FileName}\"");
}
else
{
MessageBox.Show("导出失败,请检查文件是否被占用", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("查询全量数据失败,请重试", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
catch (Exception ex)
{
MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
}

310
SCADA/Pages/UC_Statistics.Designer.cs generated Normal file
View File

@@ -0,0 +1,310 @@
namespace MesWork.Pages
{
partial class UC_Statistics
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region
private void InitializeComponent()
{
this.pnl_QueryBar = new System.Windows.Forms.Panel();
this.lbl_DateFrom = new System.Windows.Forms.Label();
this.dtp_Start = new System.Windows.Forms.DateTimePicker();
this.lbl_DateTo = new System.Windows.Forms.Label();
this.dtp_End = new System.Windows.Forms.DateTimePicker();
this.txt_ModelNo = new System.Windows.Forms.TextBox();
this.txt_OrderNo = new System.Windows.Forms.TextBox();
this.btn_Analyze = new System.Windows.Forms.Button();
this.btn_Export = new System.Windows.Forms.Button();
this.pnl_ChartArea = new System.Windows.Forms.Panel();
this.pnl_Chart = new System.Windows.Forms.Panel();
this.lbl_ChartTitle = new System.Windows.Forms.Label();
this.pnl_StatsCard = new System.Windows.Forms.Panel();
this.lbl_StatsTitle = new System.Windows.Forms.Label();
this.lbl_StatMax = new System.Windows.Forms.Label();
this.lbl_StatMin = new System.Windows.Forms.Label();
this.lbl_StatAvg = new System.Windows.Forms.Label();
this.lbl_StatMedian = new System.Windows.Forms.Label();
this.lbl_StatRange = new System.Windows.Forms.Label();
this.lbl_StatVariance = new System.Windows.Forms.Label();
this.lbl_StatStdDev = new System.Windows.Forms.Label();
this.lbl_StatCount = new System.Windows.Forms.Label();
this.dgv_Data = new System.Windows.Forms.DataGridView();
this.pnl_QueryBar.SuspendLayout();
this.pnl_ChartArea.SuspendLayout();
this.pnl_Chart.SuspendLayout();
this.pnl_StatsCard.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).BeginInit();
this.SuspendLayout();
//
// pnl_QueryBar — 查询栏(增高,两行布局)
//
this.pnl_QueryBar.BackColor = System.Drawing.Color.White;
this.pnl_QueryBar.Controls.Add(this.btn_Export);
this.pnl_QueryBar.Controls.Add(this.btn_Analyze);
this.pnl_QueryBar.Controls.Add(this.txt_OrderNo);
this.pnl_QueryBar.Controls.Add(this.txt_ModelNo);
this.pnl_QueryBar.Controls.Add(this.dtp_End);
this.pnl_QueryBar.Controls.Add(this.lbl_DateTo);
this.pnl_QueryBar.Controls.Add(this.dtp_Start);
this.pnl_QueryBar.Controls.Add(this.lbl_DateFrom);
this.pnl_QueryBar.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_QueryBar.Height = 56;
this.pnl_QueryBar.Name = "pnl_QueryBar";
this.pnl_QueryBar.Padding = new System.Windows.Forms.Padding(16, 0, 16, 0);
//
// lbl_DateFrom
//
this.lbl_DateFrom.AutoSize = true;
this.lbl_DateFrom.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateFrom.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_DateFrom.Location = new System.Drawing.Point(16, 17);
this.lbl_DateFrom.Name = "lbl_DateFrom";
this.lbl_DateFrom.Text = "从";
//
// dtp_Start
//
this.dtp_Start.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_Start.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_Start.Location = new System.Drawing.Point(40, 13);
this.dtp_Start.Name = "dtp_Start";
this.dtp_Start.Size = new System.Drawing.Size(130, 25);
//
// lbl_DateTo
//
this.lbl_DateTo.AutoSize = true;
this.lbl_DateTo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_DateTo.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_DateTo.Location = new System.Drawing.Point(178, 17);
this.lbl_DateTo.Name = "lbl_DateTo";
this.lbl_DateTo.Text = "到";
//
// dtp_End
//
this.dtp_End.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dtp_End.Format = System.Windows.Forms.DateTimePickerFormat.Short;
this.dtp_End.Location = new System.Drawing.Point(200, 13);
this.dtp_End.Name = "dtp_End";
this.dtp_End.Size = new System.Drawing.Size(130, 25);
//
// txt_ModelNo — 机型号筛选
//
this.txt_ModelNo.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_ModelNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_ModelNo.Location = new System.Drawing.Point(350, 13);
this.txt_ModelNo.Name = "txt_ModelNo";
this.txt_ModelNo.Size = new System.Drawing.Size(140, 27);
//
// txt_OrderNo — 订单号筛选
//
this.txt_OrderNo.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_OrderNo.Font = new System.Drawing.Font("微软雅黑", 10F);
this.txt_OrderNo.Location = new System.Drawing.Point(500, 13);
this.txt_OrderNo.Name = "txt_OrderNo";
this.txt_OrderNo.Size = new System.Drawing.Size(140, 27);
//
// btn_Analyze — 统计分析按钮
//
this.btn_Analyze.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.btn_Analyze.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Analyze.FlatAppearance.BorderSize = 0;
this.btn_Analyze.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Analyze.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Analyze.ForeColor = System.Drawing.Color.White;
this.btn_Analyze.Location = new System.Drawing.Point(660, 12);
this.btn_Analyze.Name = "btn_Analyze";
this.btn_Analyze.Size = new System.Drawing.Size(110, 30);
this.btn_Analyze.Text = "📊 统计分析";
this.btn_Analyze.UseVisualStyleBackColor = false;
this.btn_Analyze.Click += new System.EventHandler(this.btn_Analyze_Click);
//
// btn_Export — 导出数据按钮
//
this.btn_Export.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
this.btn_Export.BackColor = System.Drawing.Color.FromArgb(5, 150, 105);
this.btn_Export.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Export.FlatAppearance.BorderSize = 0;
this.btn_Export.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Export.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Export.ForeColor = System.Drawing.Color.White;
this.btn_Export.Location = new System.Drawing.Point(1700, 12);
this.btn_Export.Name = "btn_Export";
this.btn_Export.Size = new System.Drawing.Size(104, 30);
this.btn_Export.Text = "📥 导出数据";
this.btn_Export.UseVisualStyleBackColor = false;
this.btn_Export.Click += new System.EventHandler(this.btn_Export_Click);
//
// pnl_ChartArea — 图表+统计卡片区域
//
this.pnl_ChartArea.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_ChartArea.Height = 400;
this.pnl_ChartArea.Name = "pnl_ChartArea";
this.pnl_ChartArea.Padding = new System.Windows.Forms.Padding(12, 8, 12, 8);
this.pnl_ChartArea.Controls.Add(this.pnl_Chart);
this.pnl_ChartArea.Controls.Add(this.pnl_StatsCard);
//
// pnl_Chart — 图表区域
//
this.pnl_Chart.BackColor = System.Drawing.Color.White;
this.pnl_Chart.Dock = System.Windows.Forms.DockStyle.Fill;
this.pnl_Chart.Name = "pnl_Chart";
this.pnl_Chart.Padding = new System.Windows.Forms.Padding(16, 8, 16, 8);
this.pnl_Chart.Controls.Add(this.lbl_ChartTitle);
//
// lbl_ChartTitle
//
this.lbl_ChartTitle.Dock = System.Windows.Forms.DockStyle.Top;
this.lbl_ChartTitle.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold);
this.lbl_ChartTitle.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.lbl_ChartTitle.Height = 32;
this.lbl_ChartTitle.Name = "lbl_ChartTitle";
this.lbl_ChartTitle.Text = "📈 机油残留量趋势分析";
this.lbl_ChartTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// pnl_StatsCard — 右侧统计指标卡片
//
this.pnl_StatsCard.BackColor = System.Drawing.Color.FromArgb(248, 250, 252);
this.pnl_StatsCard.Dock = System.Windows.Forms.DockStyle.Right;
this.pnl_StatsCard.Width = 260;
this.pnl_StatsCard.Name = "pnl_StatsCard";
this.pnl_StatsCard.Padding = new System.Windows.Forms.Padding(20, 8, 20, 8);
this.pnl_StatsCard.Controls.Add(this.lbl_StatCount);
this.pnl_StatsCard.Controls.Add(this.lbl_StatStdDev);
this.pnl_StatsCard.Controls.Add(this.lbl_StatVariance);
this.pnl_StatsCard.Controls.Add(this.lbl_StatRange);
this.pnl_StatsCard.Controls.Add(this.lbl_StatMedian);
this.pnl_StatsCard.Controls.Add(this.lbl_StatAvg);
this.pnl_StatsCard.Controls.Add(this.lbl_StatMin);
this.pnl_StatsCard.Controls.Add(this.lbl_StatMax);
this.pnl_StatsCard.Controls.Add(this.lbl_StatsTitle);
//
// lbl_StatsTitle
//
this.lbl_StatsTitle.Dock = System.Windows.Forms.DockStyle.Top;
this.lbl_StatsTitle.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold);
this.lbl_StatsTitle.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.lbl_StatsTitle.Height = 36;
this.lbl_StatsTitle.Name = "lbl_StatsTitle";
this.lbl_StatsTitle.Text = "📋 统计指标";
this.lbl_StatsTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// 统计数值标签
//
this.lbl_StatMax.AutoSize = true; this.lbl_StatMax.Font = new System.Drawing.Font("微软雅黑", 10.5F);
this.lbl_StatMax.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_StatMax.Location = new System.Drawing.Point(20, 50); this.lbl_StatMax.Name = "lbl_StatMax"; this.lbl_StatMax.Text = "最大值 —";
this.lbl_StatMin.AutoSize = true; this.lbl_StatMin.Font = new System.Drawing.Font("微软雅黑", 10.5F);
this.lbl_StatMin.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_StatMin.Location = new System.Drawing.Point(20, 82); this.lbl_StatMin.Name = "lbl_StatMin"; this.lbl_StatMin.Text = "最小值 —";
this.lbl_StatAvg.AutoSize = true; this.lbl_StatAvg.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Bold);
this.lbl_StatAvg.ForeColor = System.Drawing.Color.FromArgb(245, 158, 11);
this.lbl_StatAvg.Location = new System.Drawing.Point(20, 114); this.lbl_StatAvg.Name = "lbl_StatAvg"; this.lbl_StatAvg.Text = "平均值 —";
this.lbl_StatMedian.AutoSize = true; this.lbl_StatMedian.Font = new System.Drawing.Font("微软雅黑", 10.5F);
this.lbl_StatMedian.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_StatMedian.Location = new System.Drawing.Point(20, 146); this.lbl_StatMedian.Name = "lbl_StatMedian"; this.lbl_StatMedian.Text = "中位数 —";
this.lbl_StatRange.AutoSize = true; this.lbl_StatRange.Font = new System.Drawing.Font("微软雅黑", 10.5F);
this.lbl_StatRange.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_StatRange.Location = new System.Drawing.Point(20, 178); this.lbl_StatRange.Name = "lbl_StatRange"; this.lbl_StatRange.Text = "极差 —";
this.lbl_StatVariance.AutoSize = true; this.lbl_StatVariance.Font = new System.Drawing.Font("微软雅黑", 10.5F);
this.lbl_StatVariance.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_StatVariance.Location = new System.Drawing.Point(20, 210); this.lbl_StatVariance.Name = "lbl_StatVariance"; this.lbl_StatVariance.Text = "方差 —";
this.lbl_StatStdDev.AutoSize = true; this.lbl_StatStdDev.Font = new System.Drawing.Font("微软雅黑", 10.5F);
this.lbl_StatStdDev.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_StatStdDev.Location = new System.Drawing.Point(20, 242); this.lbl_StatStdDev.Name = "lbl_StatStdDev"; this.lbl_StatStdDev.Text = "标准差 —";
this.lbl_StatCount.AutoSize = true; this.lbl_StatCount.Font = new System.Drawing.Font("微软雅黑", 10.5F, System.Drawing.FontStyle.Bold);
this.lbl_StatCount.ForeColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.lbl_StatCount.Location = new System.Drawing.Point(20, 282); this.lbl_StatCount.Name = "lbl_StatCount"; this.lbl_StatCount.Text = "样本数 0";
//
// dgv_Data — 明细表
//
this.dgv_Data.AllowUserToAddRows = false;
this.dgv_Data.AllowUserToDeleteRows = false;
this.dgv_Data.AllowUserToResizeRows = false;
this.dgv_Data.BackgroundColor = System.Drawing.Color.White;
this.dgv_Data.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgv_Data.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal;
this.dgv_Data.ColumnHeadersHeight = 40;
this.dgv_Data.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
this.dgv_Data.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(232, 237, 242);
this.dgv_Data.ColumnHeadersDefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.dgv_Data.ColumnHeadersDefaultCellStyle.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
this.dgv_Data.DefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(31, 41, 55);
this.dgv_Data.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 10F);
this.dgv_Data.DefaultCellStyle.SelectionBackColor = System.Drawing.Color.FromArgb(219, 234, 254);
this.dgv_Data.DefaultCellStyle.SelectionForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.DefaultCellStyle.Padding = new System.Windows.Forms.Padding(6, 0, 6, 0);
this.dgv_Data.AlternatingRowsDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(248, 250, 252);
this.dgv_Data.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_Data.EnableHeadersVisualStyles = false;
this.dgv_Data.GridColor = System.Drawing.Color.FromArgb(229, 231, 235);
this.dgv_Data.MultiSelect = false;
this.dgv_Data.Name = "dgv_Data";
this.dgv_Data.ReadOnly = true;
this.dgv_Data.RowHeadersVisible = false;
this.dgv_Data.RowTemplate.Height = 36;
this.dgv_Data.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_Data.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
//
// UC_Statistics
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(245, 247, 250);
this.Controls.Add(this.dgv_Data);
this.Controls.Add(this.pnl_ChartArea);
this.Controls.Add(this.pnl_QueryBar);
this.Name = "UC_Statistics";
this.Size = new System.Drawing.Size(1820, 1000);
this.pnl_QueryBar.ResumeLayout(false);
this.pnl_QueryBar.PerformLayout();
this.pnl_ChartArea.ResumeLayout(false);
this.pnl_Chart.ResumeLayout(false);
this.pnl_StatsCard.ResumeLayout(false);
this.pnl_StatsCard.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel pnl_QueryBar;
private System.Windows.Forms.Label lbl_DateFrom;
private System.Windows.Forms.DateTimePicker dtp_Start;
private System.Windows.Forms.Label lbl_DateTo;
private System.Windows.Forms.DateTimePicker dtp_End;
private System.Windows.Forms.TextBox txt_ModelNo;
private System.Windows.Forms.TextBox txt_OrderNo;
private System.Windows.Forms.Button btn_Analyze;
private System.Windows.Forms.Button btn_Export;
private System.Windows.Forms.Panel pnl_ChartArea;
private System.Windows.Forms.Panel pnl_Chart;
private System.Windows.Forms.Label lbl_ChartTitle;
private System.Windows.Forms.Panel pnl_StatsCard;
private System.Windows.Forms.Label lbl_StatsTitle;
private System.Windows.Forms.Label lbl_StatMax;
private System.Windows.Forms.Label lbl_StatMin;
private System.Windows.Forms.Label lbl_StatAvg;
private System.Windows.Forms.Label lbl_StatMedian;
private System.Windows.Forms.Label lbl_StatRange;
private System.Windows.Forms.Label lbl_StatVariance;
private System.Windows.Forms.Label lbl_StatStdDev;
private System.Windows.Forms.Label lbl_StatCount;
private System.Windows.Forms.DataGridView dgv_Data;
}
}

View File

@@ -0,0 +1,471 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 统计分析页面 — 残留量趋势分析 + 统计指标计算 + 明细数据
/// 使用 GDI+ 绘制趋势折线图(含平均线 + 数据标签)
/// </summary>
public partial class UC_Statistics : UserControl
{
/// <summary>图表数据缓存</summary>
private List<(DateTime time, double value, string label)> _chartData = new List<(DateTime, double, string)>();
/// <summary>平均值(用于绘制参考线)</summary>
private double _avgValue = 0;
// ─── 配色常量 ───
private static readonly Color CLR_PRIMARY = Color.FromArgb(74, 144, 217);
private static readonly Color CLR_PRIMARY_DARK = Color.FromArgb(30, 58, 95);
private static readonly Color CLR_AVG_LINE = Color.FromArgb(245, 158, 11); // 橙色平均线
private static readonly Color CLR_GRID = Color.FromArgb(236, 239, 243);
private static readonly Color CLR_TEXT = Color.FromArgb(55, 65, 81);
private static readonly Color CLR_TEXT_LIGHT = Color.FromArgb(107, 114, 128);
private static readonly Color CLR_DOT_FILL = Color.FromArgb(74, 144, 217);
private static readonly Color CLR_CARD_BG = Color.FromArgb(248, 250, 252);
public UC_Statistics()
{
InitializeComponent();
InitColumns();
dtp_Start.Value = DateTime.Today.AddDays(-30);
dtp_End.Value = DateTime.Today;
// 订阅图表区域绘制
pnl_Chart.Paint += pnl_Chart_Paint;
pnl_Chart.Resize += (s, ev) => pnl_Chart.Invalidate();
// 为筛选框加占位提示
SetHint(txt_ModelNo, "机型号筛选...");
SetHint(txt_OrderNo, "订单号筛选...");
}
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 = CLR_PRIMARY_DARK; } };
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();
}
/// <summary>
/// 预设明细表格列
/// </summary>
private void InitColumns()
{
dgv_Data.AutoGenerateColumns = false;
dgv_Data.Columns.Clear();
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "序号", HeaderText = "序号", Width = 50, FillWeight = 30 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "ID", HeaderText = "ID", DataPropertyName = "ID", Visible = false });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "测试时间", HeaderText = "检测时间", DataPropertyName = "测试时间", FillWeight = 100 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "发动机号", HeaderText = "工件编号", DataPropertyName = "发动机号", FillWeight = 100 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "订单号", HeaderText = "订单号", DataPropertyName = "订单号", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "机型号", HeaderText = "机型号", DataPropertyName = "机型号", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "残留量", HeaderText = "机油残留(mg)", DataPropertyName = "残留量", FillWeight = 80 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "设备编号", HeaderText = "设备编号", DataPropertyName = "设备编号", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "操作员", HeaderText = "操作员", DataPropertyName = "操作员", FillWeight = 60 });
}
/// <summary>
/// 统计分析按钮
/// </summary>
private void btn_Analyze_Click(object sender, EventArgs e)
{
try
{
var pageCountParam = new SqlParameter("@PageCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
var itemCountParam = new SqlParameter("@ItemCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", ""),
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")),
new SqlParameter("@PageCurrent", 1),
new SqlParameter("@PageSize", 9999),
pageCountParam,
itemCountParam
};
SqlOperation.ExecuteStoredProcedure("称重记录_分页查询", parms, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0)
{
dgv_Data.DataSource = dt;
FillRowNumbers();
CalculateStatistics(dt);
BuildChartData(dt);
pnl_Chart.Invalidate();
}
else
{
_chartData.Clear();
ClearStatLabels();
pnl_Chart.Invalidate();
MessageBox.Show("查询范围内无数据", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 填充表格序号列(与图表数据点编号对应)
/// 图表按时间升序排列表格按ID降序序号在数据排序后重新映射
/// </summary>
private void FillRowNumbers()
{
// 表格数据按ID降序排列最新在前序号直接按行号赋值
for (int i = 0; i < dgv_Data.Rows.Count; i++)
{
dgv_Data.Rows[i].Cells["序号"].Value = i + 1;
}
}
/// <summary>
/// 从DataTable构建图表数据确保所有行都被处理
/// </summary>
private void BuildChartData(DataTable dt)
{
_chartData.Clear();
// 查找时间列和数值列
string timeCol = dt.Columns.Contains("进站时间") ? "进站时间" :
dt.Columns.Contains("测试时间") ? "测试时间" : null;
string valCol = dt.Columns.Contains("残油量") ? "残油量" :
dt.Columns.Contains("残留量") ? "残留量" :
dt.Columns.Contains("放油量") ? "放油量" : null;
string labelCol = dt.Columns.Contains("发动机号") ? "发动机号" : null;
if (timeCol == null || valCol == null) return;
foreach (DataRow row in dt.Rows)
{
DateTime t;
double v;
string lbl = labelCol != null ? row[labelCol]?.ToString() ?? "" : "";
// 时间解析:如果失败则使用当前时间递增
if (!DateTime.TryParse(row[timeCol]?.ToString(), out t))
t = DateTime.Now;
// 数值解析如果失败或为空默认为0也是有效数据点
object rawVal = row[valCol];
if (rawVal == null || rawVal == DBNull.Value || !double.TryParse(rawVal.ToString(), out v))
v = 0;
_chartData.Add((t, v, lbl));
}
// 不排序保持DataTable原始行顺序与表格序号一致
}
/// <summary>
/// GDI+ 绘制折线图(增强版)
/// </summary>
private void pnl_Chart_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
var rect = pnl_Chart.ClientRectangle;
int padL = 80, padR = 40, padT = 50, padB = 60;
int chartW = rect.Width - padL - padR;
int chartH = rect.Height - padT - padB;
if (chartW < 50 || chartH < 50) return;
// 绘制背景
using (var bgBrush = new SolidBrush(Color.White))
g.FillRectangle(bgBrush, rect);
// 空数据提示
if (_chartData.Count == 0)
{
using (var font = new Font("微软雅黑", 14F))
using (var brush = new SolidBrush(Color.FromArgb(156, 163, 175)))
{
var msg = "📊 设置筛选条件后点击「统计分析」查看趋势图";
var sz = g.MeasureString(msg, font);
g.DrawString(msg, font, brush, (rect.Width - sz.Width) / 2, (rect.Height - sz.Height) / 2);
}
return;
}
// 计算数据范围
double minVal = _chartData.Min(d => d.value);
double maxVal = _chartData.Max(d => d.value);
double range = maxVal - minVal;
if (range < 0.001) { range = Math.Max(Math.Abs(maxVal) * 0.2, 1); }
minVal -= range * 0.12;
maxVal += range * 0.12;
if (minVal < 0 && _chartData.Min(d => d.value) >= 0) minVal = 0; // 不让Y轴负数如果原始数据非负
int totalPts = _chartData.Count;
// ─── 绘制网格线和Y轴标签 ───
using (var gridPen = new Pen(CLR_GRID, 1) { DashStyle = DashStyle.Dot })
using (var axisFont = new Font("微软雅黑", 9F))
using (var axisBrush = new SolidBrush(CLR_TEXT_LIGHT))
using (var axisPen = new Pen(Color.FromArgb(200, 210, 220), 1))
{
// Y轴线
g.DrawLine(axisPen, padL, padT, padL, padT + chartH);
// X轴线
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);
double val = minVal + (maxVal - minVal) * i / 5.0;
string valStr = val.ToString("F2");
var sz = g.MeasureString(valStr, axisFont);
g.DrawString(valStr, axisFont, axisBrush, padL - sz.Width - 6, y - sz.Height / 2);
}
// X轴标签 — 序号均匀排布1, 2, 3...
// 计算合适的标签间隔,避免过于密集
int maxLabels = Math.Max(1, chartW / 40); // 每个标签至少40px空间
int labelStep = Math.Max(1, (totalPts - 1) / Math.Max(maxLabels - 1, 1));
// 如果数据点少于maxLabels每个都显示
if (totalPts <= maxLabels) labelStep = 1;
for (int i = 0; i < totalPts; i += labelStep)
{
float xPos = totalPts == 1 ? padL + chartW / 2f
: padL + (float)chartW * i / (totalPts - 1);
string label = (i + 1).ToString();
var sz = g.MeasureString(label, axisFont);
g.DrawString(label, axisFont, axisBrush, xPos - sz.Width / 2, padT + chartH + 8);
g.DrawLine(axisPen, (int)xPos, padT + chartH, (int)xPos, padT + chartH + 5);
}
// 确保最后一个序号显示
if (totalPts > 1 && (totalPts - 1) % labelStep != 0)
{
float lastX = padL + chartW;
string lastLabel = totalPts.ToString();
var lastSz = g.MeasureString(lastLabel, axisFont);
g.DrawString(lastLabel, axisFont, axisBrush, lastX - lastSz.Width / 2, padT + chartH + 8);
g.DrawLine(axisPen, (int)lastX, padT + chartH, (int)lastX, padT + chartH + 5);
}
}
// ─── 计算所有数据点坐标X轴按序号均匀排布───
var points = new PointF[totalPts];
for (int i = 0; i < totalPts; i++)
{
float x = totalPts == 1 ? padL + chartW / 2f
: padL + (float)chartW * i / (totalPts - 1);
float y = padT + chartH - (float)(chartH * ((_chartData[i].value - minVal) / (maxVal - minVal)));
points[i] = new PointF(x, y);
}
// ─── 渐变填充区域 ───
if (points.Length >= 2)
{
using (var fillPath = new GraphicsPath())
{
fillPath.AddLines(points);
fillPath.AddLine(points.Last(), new PointF(points.Last().X, padT + chartH));
fillPath.AddLine(new PointF(points.Last().X, padT + chartH), new PointF(points.First().X, padT + chartH));
fillPath.CloseFigure();
using (var fillBrush = new LinearGradientBrush(
new Point(0, padT), new Point(0, padT + chartH),
Color.FromArgb(50, 74, 144, 217), Color.FromArgb(3, 74, 144, 217)))
{
g.FillPath(fillBrush, fillPath);
}
}
// 折线(粗线+阴影效果)
using (var shadowPen = new Pen(Color.FromArgb(30, 74, 144, 217), 5f))
{
shadowPen.LineJoin = LineJoin.Round;
g.DrawLines(shadowPen, points);
}
using (var linePen = new Pen(CLR_PRIMARY, 2.5f))
{
linePen.LineJoin = LineJoin.Round;
g.DrawLines(linePen, points);
}
}
// ─── 平均线(红色虚线 + 箭头 + 标签)───
if (_chartData.Count > 0)
{
float avgY = padT + chartH - (float)(chartH * ((_avgValue - minVal) / (maxVal - minVal)));
if (avgY >= padT && avgY <= padT + chartH)
{
using (var avgPen = new Pen(CLR_AVG_LINE, 1.8f) { DashStyle = DashStyle.Dash })
{
g.DrawLine(avgPen, padL, avgY, padL + chartW, avgY);
}
// 平均线标签(右侧带箭头标识)
using (var avgFont = new Font("微软雅黑", 9F, FontStyle.Bold))
using (var avgBrush = new SolidBrush(CLR_AVG_LINE))
{
string avgText = $"▶ 平均 {_avgValue:F2}";
var sz = g.MeasureString(avgText, avgFont);
// 背景
float lblX = padL + chartW - sz.Width - 4;
float lblY = avgY - sz.Height - 3;
using (var bgBrush = new SolidBrush(Color.FromArgb(220, 255, 255, 255)))
g.FillRectangle(bgBrush, lblX - 3, lblY, sz.Width + 6, sz.Height + 2);
g.DrawString(avgText, avgFont, avgBrush, lblX, lblY);
// 小三角箭头指向线
var arrowPts = new PointF[] {
new PointF(padL + chartW + 2, avgY),
new PointF(padL + chartW + 10, avgY - 5),
new PointF(padL + chartW + 10, avgY + 5)
};
g.FillPolygon(avgBrush, arrowPts);
}
}
}
// ─── 数据点(带光晕效果)───
for (int i = 0; i < points.Length; i++)
{
var pt = points[i];
// 光晕
using (var glowBrush = new SolidBrush(Color.FromArgb(40, 74, 144, 217)))
g.FillEllipse(glowBrush, pt.X - 8, pt.Y - 8, 16, 16);
// 白色底
g.FillEllipse(Brushes.White, pt.X - 5, pt.Y - 5, 10, 10);
// 蓝色圆点
using (var dotBrush = new SolidBrush(CLR_DOT_FILL))
g.FillEllipse(dotBrush, pt.X - 4, pt.Y - 4, 8, 8);
// 数值标签(只有当数据点不太密时显示)
if (_chartData.Count <= 15)
{
using (var valFont = new Font("微软雅黑", 8F))
using (var valBrush = new SolidBrush(CLR_PRIMARY_DARK))
{
string valText = _chartData[i].value.ToString("F1");
var sz = g.MeasureString(valText, valFont);
g.DrawString(valText, valFont, valBrush, pt.X - sz.Width / 2, pt.Y - sz.Height - 6);
}
}
}
// ─── 图表右下角水印 ───
using (var wmFont = new Font("微软雅黑", 8F))
using (var wmBrush = new SolidBrush(Color.FromArgb(80, 156, 163, 175)))
{
string wm = $"共 {_chartData.Count} 个样本";
var sz = g.MeasureString(wm, wmFont);
g.DrawString(wm, wmFont, wmBrush, padL + chartW - sz.Width, padT + 4);
}
}
/// <summary>
/// 计算统计指标(基于残留量列)
/// </summary>
private void CalculateStatistics(DataTable dt)
{
var values = new List<double>();
string colName = dt.Columns.Contains("残油量") ? "残油量" :
dt.Columns.Contains("残留量") ? "残留量" : null;
if (colName == null)
{
foreach (DataColumn col in dt.Columns)
{
if (col.ColumnName.Contains("残留") || col.ColumnName.Contains("残油") || col.ColumnName.Contains("重量"))
{
colName = col.ColumnName;
break;
}
}
}
if (colName != null)
{
foreach (DataRow row in dt.Rows)
{
object rawVal = row[colName];
if (rawVal != null && rawVal != DBNull.Value && double.TryParse(rawVal.ToString(), out double v))
values.Add(v);
else
values.Add(0); // 空值也算0确保样本数一致
}
}
if (values.Count == 0)
{
ClearStatLabels();
return;
}
values.Sort();
double max = values.Max();
double min = values.Min();
double avg = values.Average();
_avgValue = avg; // 保存平均值用于绘图
double median = values.Count % 2 == 0
? (values[values.Count / 2 - 1] + values[values.Count / 2]) / 2.0
: values[values.Count / 2];
double rangeVal = max - min;
double variance = values.Sum(v => Math.Pow(v - avg, 2)) / values.Count;
double stdDev = Math.Sqrt(variance);
// 更新统计指标标签
lbl_StatMax.Text = $"最大值 {max:F4}";
lbl_StatMin.Text = $"最小值 {min:F4}";
lbl_StatAvg.Text = $"平均值 {avg:F4}";
lbl_StatMedian.Text = $"中位数 {median:F4}";
lbl_StatRange.Text = $"极差 {rangeVal:F4}";
lbl_StatVariance.Text = $"方差 {variance:F4}";
lbl_StatStdDev.Text = $"标准差 {stdDev:F4}";
lbl_StatCount.Text = $"样本数 {values.Count}";
}
private void ClearStatLabels()
{
lbl_StatMax.Text = "最大值 —";
lbl_StatMin.Text = "最小值 —";
lbl_StatAvg.Text = "平均值 —";
lbl_StatMedian.Text = "中位数 —";
lbl_StatRange.Text = "极差 —";
lbl_StatVariance.Text = "方差 —";
lbl_StatStdDev.Text = "标准差 —";
lbl_StatCount.Text = "样本数 0";
}
/// <summary>
/// 导出数据
/// </summary>
private void btn_Export_Click(object sender, EventArgs e)
{
CrudHelper.ExportToExcel("统计分析", "统计分析", () =>
{
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@发动机号", ""),
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")),
new SqlParameter("@PageCurrent", 1),
new SqlParameter("@PageSize", 9999999),
pageCountParam, itemCountParam
};
SqlOperation.ExecuteStoredProcedure("称重记录_分页查询", parms, out DataTable dt, out string err);
return dt;
}, dgv_Data.Rows.Count > 0);
}
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<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">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata"><xsd:complexType><xsd:sequence><xsd:element name="value" type="xsd:string" minOccurs="0" /></xsd:sequence><xsd:attribute name="name" use="required" type="xsd:string" /><xsd:attribute name="type" type="xsd:string" /><xsd:attribute name="mimetype" type="xsd:string" /><xsd:attribute ref="xml:space" /></xsd:complexType></xsd:element>
<xsd:element name="assembly"><xsd:complexType><xsd:attribute name="alias" type="xsd:string" /><xsd:attribute name="name" type="xsd:string" /></xsd:complexType></xsd:element>
<xsd:element name="data"><xsd:complexType><xsd:sequence><xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /><xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /></xsd:sequence><xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /><xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /><xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /><xsd:attribute ref="xml:space" /></xsd:complexType></xsd:element>
<xsd:element name="resheader"><xsd:complexType><xsd:sequence><xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /></xsd:sequence><xsd:attribute name="name" type="xsd:string" use="required" /></xsd:complexType></xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader>
<resheader name="version"><value>2.0</value></resheader>
<resheader name="reader"><value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader>
<resheader name="writer"><value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader>
</root>

View File

@@ -0,0 +1,93 @@
namespace MesWork.Pages
{
partial class UC_SystemSettings
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.pnl_Header = new System.Windows.Forms.Panel();
this.lbl_Title = new System.Windows.Forms.Label();
this.grp_Scanner1 = new System.Windows.Forms.GroupBox();
this.grp_Scanner2 = new System.Windows.Forms.GroupBox();
this.timer_Refresh = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// pnl_Header
//
this.pnl_Header.BackColor = System.Drawing.Color.White;
this.pnl_Header.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_Header.Height = 56;
this.pnl_Header.Padding = new System.Windows.Forms.Padding(24, 0, 0, 0);
this.pnl_Header.Name = "pnl_Header";
this.pnl_Header.Controls.Add(this.lbl_Title);
//
// lbl_Title
//
this.lbl_Title.AutoSize = true;
this.lbl_Title.Font = new System.Drawing.Font("微软雅黑", 14F, System.Drawing.FontStyle.Bold);
this.lbl_Title.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.lbl_Title.Location = new System.Drawing.Point(24, 14);
this.lbl_Title.Name = "lbl_Title";
this.lbl_Title.Text = "系统设置";
//
// grp_Scanner1
//
this.grp_Scanner1.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Bold);
this.grp_Scanner1.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.grp_Scanner1.Location = new System.Drawing.Point(30, 76);
this.grp_Scanner1.Name = "grp_Scanner1";
this.grp_Scanner1.Size = new System.Drawing.Size(700, 200);
this.grp_Scanner1.Text = " OP10 扫码枪 ";
//
// grp_Scanner2
//
this.grp_Scanner2.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Bold);
this.grp_Scanner2.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.grp_Scanner2.Location = new System.Drawing.Point(30, 296);
this.grp_Scanner2.Name = "grp_Scanner2";
this.grp_Scanner2.Size = new System.Drawing.Size(700, 200);
this.grp_Scanner2.Text = " OP20 扫码枪 ";
//
// timer_Refresh
//
this.timer_Refresh.Interval = 1000;
this.timer_Refresh.Tick += new System.EventHandler(this.timer_Refresh_Tick);
//
// UC_SystemSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(245, 247, 250);
this.Controls.Add(this.grp_Scanner1);
this.Controls.Add(this.grp_Scanner2);
this.Controls.Add(this.pnl_Header);
this.Name = "UC_SystemSettings";
this.Size = new System.Drawing.Size(1820, 1000);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Panel pnl_Header;
private System.Windows.Forms.Label lbl_Title;
private System.Windows.Forms.GroupBox grp_Scanner1;
private System.Windows.Forms.GroupBox grp_Scanner2;
private System.Windows.Forms.Timer timer_Refresh;
}
}

View File

@@ -0,0 +1,238 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 系统设置页面 — 扫码枪连接管理
/// </summary>
public partial class UC_SystemSettings : UserControl
{
// OP10 控件
private ComboBox cmb_Com1;
private Button btn_Connect1, btn_Disconnect1, btn_ManualSend1;
private Label lbl_Status1, lbl_ScanTime1;
private TextBox txt_Barcode1;
// OP20 控件
private ComboBox cmb_Com2;
private Button btn_Connect2, btn_Disconnect2, btn_ManualSend2;
private Label lbl_Status2, lbl_ScanTime2;
private TextBox txt_Barcode2;
public UC_SystemSettings()
{
InitializeComponent();
if (BarcodeManager.IsEnabled)
{
BuildScannerPanel(grp_Scanner1, "OP10", ref cmb_Com1, ref lbl_Status1, ref txt_Barcode1, ref lbl_ScanTime1,
ref btn_Connect1, ref btn_Disconnect1, ref btn_ManualSend1);
BuildScannerPanel(grp_Scanner2, "OP20", ref cmb_Com2, ref lbl_Status2, ref txt_Barcode2, ref lbl_ScanTime2,
ref btn_Connect2, ref btn_Disconnect2, ref btn_ManualSend2);
timer_Refresh.Start();
}
else
{
BuildScannerPanel(grp_Scanner1, "OP10", ref cmb_Com1, ref lbl_Status1, ref txt_Barcode1, ref lbl_ScanTime1,
ref btn_Connect1, ref btn_Disconnect1, ref btn_ManualSend1);
BuildScannerPanel(grp_Scanner2, "OP20", ref cmb_Com2, ref lbl_Status2, ref txt_Barcode2, ref lbl_ScanTime2,
ref btn_Connect2, ref btn_Disconnect2, ref btn_ManualSend2);
grp_Scanner1.Enabled = false;
grp_Scanner2.Enabled = false;
}
}
/// <summary>
/// 动态构建单个扫码枪面板的控件
/// </summary>
private void BuildScannerPanel(GroupBox grp, string opName,
ref ComboBox cmb, ref Label lblStatus, ref TextBox txtBar, ref Label lblTime,
ref Button btnConn, ref Button btnDisc, ref Button btnSend)
{
var normalFont = new Font("微软雅黑", 10F);
var boldFont = new Font("微软雅黑", 10F, FontStyle.Bold);
int y1 = 35, y2 = 75, y3 = 115;
// Row 1: COM口选择 + 连接/断开
var lblCom = new Label { Text = "COM口:", Font = normalFont, ForeColor = Color.FromArgb(55, 65, 81),
Location = new Point(20, y1 + 4), AutoSize = true };
grp.Controls.Add(lblCom);
cmb = new ComboBox { Font = normalFont, Location = new Point(90, y1), Size = new Size(110, 28),
DropDownStyle = ComboBoxStyle.DropDownList };
RefreshComPorts(cmb, opName);
grp.Controls.Add(cmb);
// 局部变量捕获ref参数不能在lambda中使用
var localCmb = cmb;
var localTxtBar = txtBar;
btnConn = new Button
{
Text = "🔗 连接", Font = boldFont, Size = new Size(90, 30), Location = new Point(215, y1 - 2),
BackColor = Color.FromArgb(74, 144, 217), ForeColor = Color.White,
FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand
};
btnConn.FlatAppearance.BorderSize = 0;
btnConn.Click += (s, e) => DoConnect(opName, localCmb);
grp.Controls.Add(btnConn);
btnDisc = new Button
{
Text = "⛔ 断开", Font = boldFont, Size = new Size(90, 30), Location = new Point(315, y1 - 2),
BackColor = Color.FromArgb(239, 68, 68), ForeColor = Color.White,
FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand
};
btnDisc.FlatAppearance.BorderSize = 0;
btnDisc.Click += (s, e) => DoDisconnect(opName);
grp.Controls.Add(btnDisc);
// 刷新COM口列表按钮
var btnRefresh = new Button
{
Text = "🔄", Font = normalFont, Size = new Size(36, 30), Location = new Point(415, y1 - 2),
FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand,
BackColor = Color.FromArgb(229, 231, 235)
};
btnRefresh.FlatAppearance.BorderSize = 0;
btnRefresh.Click += (s, e) => RefreshComPorts(localCmb, opName);
grp.Controls.Add(btnRefresh);
lblStatus = new Label { Text = "● 未连接", Font = boldFont, ForeColor = Color.FromArgb(156, 163, 175),
Location = new Point(470, y1 + 4), AutoSize = true };
grp.Controls.Add(lblStatus);
// Row 2: 最近码值 + 手动发送
var lblBar = new Label { Text = "码值:", Font = normalFont, ForeColor = Color.FromArgb(55, 65, 81),
Location = new Point(20, y2 + 4), AutoSize = true };
grp.Controls.Add(lblBar);
txtBar = new TextBox { Font = normalFont, Location = new Point(90, y2), Size = new Size(300, 28),
BorderStyle = BorderStyle.FixedSingle };
localTxtBar = txtBar; // 更新局部变量
grp.Controls.Add(txtBar);
btnSend = new Button
{
Text = "📤 手动发送", Font = boldFont, Size = new Size(120, 30), Location = new Point(405, y2 - 2),
BackColor = Color.FromArgb(5, 150, 105), ForeColor = Color.White,
FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand
};
btnSend.FlatAppearance.BorderSize = 0;
btnSend.Click += (s, e) => DoManualSend(opName, localTxtBar);
grp.Controls.Add(btnSend);
// Row 3: 扫码时间
var lblTimeLbl = new Label { Text = "扫码时间:", Font = normalFont, ForeColor = Color.FromArgb(55, 65, 81),
Location = new Point(20, y3 + 4), AutoSize = true };
grp.Controls.Add(lblTimeLbl);
lblTime = new Label { Text = "--", Font = normalFont, ForeColor = Color.FromArgb(31, 41, 55),
Location = new Point(110, y3 + 4), AutoSize = true };
grp.Controls.Add(lblTime);
}
// ── 操作方法 ──
private void DoConnect(string opName, ComboBox cmb)
{
string com = cmb.SelectedItem?.ToString();
if (string.IsNullOrEmpty(com))
{
MessageBox.Show("请选择COM口", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
BarcodeScanner scanner;
if (!BarcodeManager.Scanners.TryGetValue(opName, out scanner))
{
scanner = new BarcodeScanner(opName, MesWorkForm.BarcodeBaudRate);
scanner.BarcodeReceived += (op, code) => BarcodeManager.ManualSend(op, code);
BarcodeManager.Scanners[opName] = scanner;
}
bool ok = scanner.Connect(com);
MessageBox.Show(ok ? $"{opName} 扫码枪已连接 ({com})" : $"{opName} 连接失败请检查COM口",
"连接结果", MessageBoxButtons.OK, ok ? MessageBoxIcon.Information : MessageBoxIcon.Warning);
}
private void DoDisconnect(string opName)
{
if (BarcodeManager.Scanners.TryGetValue(opName, out var scanner))
{
scanner.Disconnect();
MessageBox.Show($"{opName} 扫码枪已断开", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void DoManualSend(string opName, TextBox txtBar)
{
string barcode = txtBar.Text.Trim();
if (string.IsNullOrEmpty(barcode))
{
MessageBox.Show("请输入条码值", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
BarcodeManager.ManualSend(opName, barcode);
MessageBox.Show($"已手动发送条码:{barcode}\n工位{opName}", "发送成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void RefreshComPorts(ComboBox cmb, string opName)
{
cmb.Items.Clear();
var ports = BarcodeManager.GetAvailablePorts();
foreach (var p in ports)
cmb.Items.Add(p);
// 自动选中当前配置的COM口或已连接的COM口
if (BarcodeManager.Scanners.TryGetValue(opName, out var scanner) && !string.IsNullOrEmpty(scanner.ComPort))
{
int idx = cmb.Items.IndexOf(scanner.ComPort);
if (idx >= 0) cmb.SelectedIndex = idx;
}
}
// ── 定时刷新状态 ──
private void timer_Refresh_Tick(object sender, EventArgs e)
{
RefreshScannerStatus("OP10", lbl_Status1, txt_Barcode1, lbl_ScanTime1);
RefreshScannerStatus("OP20", lbl_Status2, txt_Barcode2, lbl_ScanTime2);
}
private void RefreshScannerStatus(string opName, Label lblStatus, TextBox txtBar, Label lblTime)
{
if (lblStatus == null) return;
if (BarcodeManager.Scanners.TryGetValue(opName, out var scanner))
{
if (scanner.IsConnected)
{
lblStatus.Text = $"● 已连接 ({scanner.ComPort})";
lblStatus.ForeColor = Color.FromArgb(16, 185, 129); // 绿色
}
else
{
lblStatus.Text = $"● 未连接 ({scanner.ComPort})";
lblStatus.ForeColor = Color.FromArgb(239, 68, 68); // 红色
}
// 仅在有新扫码时更新(避免覆盖用户手动输入)
if (!string.IsNullOrEmpty(scanner.LastBarcode) && txtBar.Text != scanner.LastBarcode && !txtBar.Focused)
txtBar.Text = scanner.LastBarcode;
lblTime.Text = scanner.LastScanTime.HasValue
? scanner.LastScanTime.Value.ToString("yyyy-MM-dd HH:mm:ss")
: "--";
}
else
{
lblStatus.Text = "● 未配置";
lblStatus.ForeColor = Color.FromArgb(156, 163, 175);
lblTime.Text = "--";
}
}
}
}

253
SCADA/Pages/UC_ToolMgmt.Designer.cs generated Normal file
View File

@@ -0,0 +1,253 @@
namespace MesWork.Pages
{
partial class UC_ToolMgmt
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
#region
private void InitializeComponent()
{
this.pnl_Toolbar = new System.Windows.Forms.Panel();
this.txt_Search = new System.Windows.Forms.TextBox();
this.btn_Search = new System.Windows.Forms.Button();
this.btn_Add = new System.Windows.Forms.Button();
this.btn_Edit = new System.Windows.Forms.Button();
this.btn_Delete = new System.Windows.Forms.Button();
this.btn_Refresh = new System.Windows.Forms.Button();
this.btn_Export = new System.Windows.Forms.Button();
this.dgv_Data = new System.Windows.Forms.DataGridView();
this.pnl_StatusBar = new System.Windows.Forms.Panel();
this.lbl_RecordCount = new System.Windows.Forms.Label();
this.pnl_Toolbar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).BeginInit();
this.pnl_StatusBar.SuspendLayout();
this.SuspendLayout();
//
// pnl_Toolbar — 顶部工具栏
//
this.pnl_Toolbar.BackColor = System.Drawing.Color.White;
this.pnl_Toolbar.Controls.Add(this.btn_Export);
this.pnl_Toolbar.Controls.Add(this.btn_Refresh);
this.pnl_Toolbar.Controls.Add(this.btn_Delete);
this.pnl_Toolbar.Controls.Add(this.btn_Edit);
this.pnl_Toolbar.Controls.Add(this.btn_Add);
this.pnl_Toolbar.Controls.Add(this.btn_Search);
this.pnl_Toolbar.Controls.Add(this.txt_Search);
this.pnl_Toolbar.Dock = System.Windows.Forms.DockStyle.Top;
this.pnl_Toolbar.Location = new System.Drawing.Point(0, 0);
this.pnl_Toolbar.Name = "pnl_Toolbar";
this.pnl_Toolbar.Padding = new System.Windows.Forms.Padding(16, 12, 16, 12);
this.pnl_Toolbar.Size = new System.Drawing.Size(1820, 56);
this.pnl_Toolbar.TabIndex = 0;
//
// txt_Search — 搜索框
//
this.txt_Search.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txt_Search.Font = new System.Drawing.Font("微软雅黑", 11F);
this.txt_Search.ForeColor = System.Drawing.Color.FromArgb(156, 163, 175);
this.txt_Search.Location = new System.Drawing.Point(16, 13);
this.txt_Search.Name = "txt_Search";
this.txt_Search.Size = new System.Drawing.Size(280, 27);
this.txt_Search.TabIndex = 0;
this.txt_Search.Text = "输入工具名称搜索...";
this.txt_Search.GotFocus += new System.EventHandler(this.txt_Search_GotFocus);
this.txt_Search.LostFocus += new System.EventHandler(this.txt_Search_LostFocus);
this.txt_Search.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_Search_KeyDown);
//
// btn_Search
//
this.btn_Search.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.btn_Search.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Search.FlatAppearance.BorderSize = 0;
this.btn_Search.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Search.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Search.ForeColor = System.Drawing.Color.White;
this.btn_Search.Location = new System.Drawing.Point(306, 12);
this.btn_Search.Name = "btn_Search";
this.btn_Search.Size = new System.Drawing.Size(80, 30);
this.btn_Search.TabIndex = 1;
this.btn_Search.Text = "🔍 搜索";
this.btn_Search.UseVisualStyleBackColor = false;
this.btn_Search.Click += new System.EventHandler(this.btn_Search_Click);
//
// btn_Add
//
this.btn_Add.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
this.btn_Add.BackColor = System.Drawing.Color.FromArgb(74, 144, 217);
this.btn_Add.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Add.FlatAppearance.BorderSize = 0;
this.btn_Add.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Add.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Add.ForeColor = System.Drawing.Color.White;
this.btn_Add.Location = new System.Drawing.Point(1334, 12);
this.btn_Add.Name = "btn_Add";
this.btn_Add.Size = new System.Drawing.Size(88, 30);
this.btn_Add.TabIndex = 2;
this.btn_Add.Text = " 新增";
this.btn_Add.UseVisualStyleBackColor = false;
this.btn_Add.Click += new System.EventHandler(this.btn_Add_Click);
//
// btn_Edit
//
this.btn_Edit.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
this.btn_Edit.BackColor = System.Drawing.Color.FromArgb(245, 158, 11);
this.btn_Edit.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Edit.FlatAppearance.BorderSize = 0;
this.btn_Edit.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Edit.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Edit.ForeColor = System.Drawing.Color.White;
this.btn_Edit.Location = new System.Drawing.Point(1430, 12);
this.btn_Edit.Name = "btn_Edit";
this.btn_Edit.Size = new System.Drawing.Size(88, 30);
this.btn_Edit.TabIndex = 3;
this.btn_Edit.Text = "✏ 修改";
this.btn_Edit.UseVisualStyleBackColor = false;
this.btn_Edit.Click += new System.EventHandler(this.btn_Edit_Click);
//
// btn_Delete
//
this.btn_Delete.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
this.btn_Delete.BackColor = System.Drawing.Color.FromArgb(239, 68, 68);
this.btn_Delete.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Delete.FlatAppearance.BorderSize = 0;
this.btn_Delete.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Delete.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Delete.ForeColor = System.Drawing.Color.White;
this.btn_Delete.Location = new System.Drawing.Point(1526, 12);
this.btn_Delete.Name = "btn_Delete";
this.btn_Delete.Size = new System.Drawing.Size(88, 30);
this.btn_Delete.TabIndex = 4;
this.btn_Delete.Text = "✕ 删除";
this.btn_Delete.UseVisualStyleBackColor = false;
this.btn_Delete.Click += new System.EventHandler(this.btn_Delete_Click);
//
// btn_Refresh
//
this.btn_Refresh.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
this.btn_Refresh.BackColor = System.Drawing.Color.FromArgb(107, 114, 128);
this.btn_Refresh.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Refresh.FlatAppearance.BorderSize = 0;
this.btn_Refresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Refresh.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Refresh.ForeColor = System.Drawing.Color.White;
this.btn_Refresh.Location = new System.Drawing.Point(1718, 12);
this.btn_Refresh.Name = "btn_Refresh";
this.btn_Refresh.Size = new System.Drawing.Size(88, 30);
this.btn_Refresh.TabIndex = 5;
this.btn_Refresh.Text = "↻ 刷新";
this.btn_Refresh.UseVisualStyleBackColor = false;
this.btn_Refresh.Click += new System.EventHandler(this.btn_Refresh_Click);
//
// btn_Export
//
this.btn_Export.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
this.btn_Export.BackColor = System.Drawing.Color.FromArgb(5, 150, 105);
this.btn_Export.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_Export.FlatAppearance.BorderSize = 0;
this.btn_Export.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_Export.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Bold);
this.btn_Export.ForeColor = System.Drawing.Color.White;
this.btn_Export.Location = new System.Drawing.Point(1622, 12);
this.btn_Export.Name = "btn_Export";
this.btn_Export.Size = new System.Drawing.Size(88, 30);
this.btn_Export.TabIndex = 6;
this.btn_Export.Text = "📥 导出";
this.btn_Export.UseVisualStyleBackColor = false;
this.btn_Export.Click += new System.EventHandler(this.btn_Export_Click);
//
// dgv_Data — 数据表格
//
this.dgv_Data.AllowUserToAddRows = false;
this.dgv_Data.AllowUserToDeleteRows = false;
this.dgv_Data.AllowUserToResizeRows = false;
this.dgv_Data.BackgroundColor = System.Drawing.Color.White;
this.dgv_Data.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgv_Data.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal;
this.dgv_Data.ColumnHeadersHeight = 42;
this.dgv_Data.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
this.dgv_Data.ColumnHeadersDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(232, 237, 242);
this.dgv_Data.ColumnHeadersDefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Bold);
this.dgv_Data.ColumnHeadersDefaultCellStyle.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
this.dgv_Data.DefaultCellStyle.ForeColor = System.Drawing.Color.FromArgb(31, 41, 55);
this.dgv_Data.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 11F);
this.dgv_Data.DefaultCellStyle.SelectionBackColor = System.Drawing.Color.FromArgb(219, 234, 254);
this.dgv_Data.DefaultCellStyle.SelectionForeColor = System.Drawing.Color.FromArgb(30, 58, 95);
this.dgv_Data.DefaultCellStyle.Padding = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.dgv_Data.AlternatingRowsDefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(248, 250, 252);
this.dgv_Data.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgv_Data.EnableHeadersVisualStyles = false;
this.dgv_Data.GridColor = System.Drawing.Color.FromArgb(229, 231, 235);
this.dgv_Data.MultiSelect = false;
this.dgv_Data.Name = "dgv_Data";
this.dgv_Data.ReadOnly = true;
this.dgv_Data.RowHeadersVisible = false;
this.dgv_Data.RowTemplate.Height = 38;
this.dgv_Data.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgv_Data.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dgv_Data.TabIndex = 1;
this.dgv_Data.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgv_Data_CellDoubleClick);
//
// pnl_StatusBar — 底部状态栏
//
this.pnl_StatusBar.BackColor = System.Drawing.Color.White;
this.pnl_StatusBar.Controls.Add(this.lbl_RecordCount);
this.pnl_StatusBar.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pnl_StatusBar.Location = new System.Drawing.Point(0, 964);
this.pnl_StatusBar.Name = "pnl_StatusBar";
this.pnl_StatusBar.Padding = new System.Windows.Forms.Padding(16, 6, 16, 6);
this.pnl_StatusBar.Size = new System.Drawing.Size(1820, 36);
this.pnl_StatusBar.TabIndex = 2;
//
// lbl_RecordCount
//
this.lbl_RecordCount.AutoSize = true;
this.lbl_RecordCount.Font = new System.Drawing.Font("微软雅黑", 10F);
this.lbl_RecordCount.ForeColor = System.Drawing.Color.FromArgb(55, 65, 81);
this.lbl_RecordCount.Location = new System.Drawing.Point(16, 8);
this.lbl_RecordCount.Name = "lbl_RecordCount";
this.lbl_RecordCount.Size = new System.Drawing.Size(82, 20);
this.lbl_RecordCount.TabIndex = 0;
this.lbl_RecordCount.Text = "共 0 条记录";
//
// UC_ToolMgmt
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.FromArgb(245, 247, 250);
this.Controls.Add(this.dgv_Data);
this.Controls.Add(this.pnl_StatusBar);
this.Controls.Add(this.pnl_Toolbar);
this.Name = "UC_ToolMgmt";
this.Size = new System.Drawing.Size(1820, 1000);
this.pnl_Toolbar.ResumeLayout(false);
this.pnl_Toolbar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgv_Data)).EndInit();
this.pnl_StatusBar.ResumeLayout(false);
this.pnl_StatusBar.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel pnl_Toolbar;
private System.Windows.Forms.TextBox txt_Search;
private System.Windows.Forms.Button btn_Search;
private System.Windows.Forms.Button btn_Add;
private System.Windows.Forms.Button btn_Edit;
private System.Windows.Forms.Button btn_Delete;
private System.Windows.Forms.Button btn_Refresh;
private System.Windows.Forms.Button btn_Export;
private System.Windows.Forms.DataGridView dgv_Data;
private System.Windows.Forms.Panel pnl_StatusBar;
private System.Windows.Forms.Label lbl_RecordCount;
}
}

159
SCADA/Pages/UC_ToolMgmt.cs Normal file
View File

@@ -0,0 +1,159 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
namespace MesWork.Pages
{
/// <summary>
/// 工具管理页面 — 传感器/工具校准和使用管理
/// 对应存储过程工具管理_分页查询、工具管理_新增、工具管理_修改、工具管理_删除
/// </summary>
public partial class UC_ToolMgmt : UserControl
{
private const string SEARCH_HINT = "输入工具名称搜索...";
private PagerBar _pager;
public UC_ToolMgmt()
{
InitializeComponent();
InitColumns();
_pager = new PagerBar();
_pager.PageChanged += (s, e) => LoadData(txt_Search.Text == SEARCH_HINT ? "" : txt_Search.Text.Trim());
this.Controls.Remove(pnl_StatusBar);
this.Controls.Add(_pager);
this.Controls.SetChildIndex(_pager, this.Controls.Count - 2);
}
/// <summary>
/// 预设表格列(确保无数据时也能看到表头结构)
/// </summary>
private void InitColumns()
{
dgv_Data.AutoGenerateColumns = false;
dgv_Data.Columns.Clear();
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "ID", HeaderText = "ID", DataPropertyName = "ID", Visible = false });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "工具名称", HeaderText = "工具名称", DataPropertyName = "工具名称", FillWeight = 110 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "说明", HeaderText = "说明", DataPropertyName = "说明", FillWeight = 120 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "校准值", HeaderText = "校准值", DataPropertyName = "校准值", FillWeight = 70 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "校准时间", HeaderText = "校准时间", DataPropertyName = "校准时间", FillWeight = 90 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "使用次数", HeaderText = "使用次数", DataPropertyName = "使用次数", FillWeight = 60 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "使用次数变更时间", HeaderText = "次数变更时间", DataPropertyName = "使用次数变更时间", FillWeight = 90 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "创建时间", HeaderText = "创建时间", DataPropertyName = "创建时间", FillWeight = 90 });
dgv_Data.Columns.Add(new DataGridViewTextBoxColumn { Name = "修改时间", HeaderText = "修改时间", DataPropertyName = "修改时间", FillWeight = 90 });
}
public void LoadData(string keyword = "")
{
try
{
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@工具名称", keyword ?? ""),
new SqlParameter("@PageCurrent", _pager.CurrentPage),
new SqlParameter("@PageSize", _pager.PageSize),
pageCountParam,
itemCountParam
};
SqlOperation.ExecuteStoredProcedure("工具管理_分页查询", parms, out DataTable dt, out string err);
if (dt != null) dgv_Data.DataSource = dt;
_pager.UpdateState(pageCountParam, itemCountParam);
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// ── 搜索框交互 ──
private void txt_Search_GotFocus(object sender, EventArgs e) { CrudHelper.SearchBox_GotFocus(txt_Search, SEARCH_HINT); }
private void txt_Search_LostFocus(object sender, EventArgs e) { CrudHelper.SearchBox_LostFocus(txt_Search, SEARCH_HINT); }
private void txt_Search_KeyDown(object sender, KeyEventArgs e) { if (CrudHelper.SearchBox_KeyDown(e)) btn_Search_Click(sender, e); }
// ── 按钮事件 ──
private void btn_Search_Click(object sender, EventArgs e) { _pager.ResetPage(); LoadData(txt_Search.Text == SEARCH_HINT ? "" : txt_Search.Text.Trim()); }
private void btn_Add_Click(object sender, EventArgs e) => ShowEditDialog(null);
private void btn_Edit_Click(object sender, EventArgs e)
{
if (dgv_Data.CurrentRow != null) ShowEditDialog(dgv_Data.CurrentRow);
else MessageBox.Show("请先选择一条记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void btn_Delete_Click(object sender, EventArgs e)
{
if (dgv_Data.CurrentRow == null) { MessageBox.Show("请先选择一条记录", "提示"); return; }
string name = dgv_Data.CurrentRow.Cells["工具名称"]?.Value?.ToString() ?? "";
if (MessageBox.Show($"确认删除工具「{name}」?", "确认删除", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
try
{
var parms = new SqlParameter[] { new SqlParameter("@ID", int.Parse(dgv_Data.CurrentRow.Cells["ID"]?.Value?.ToString() ?? "0")) };
SqlOperation.ExecuteStoredProcedure("工具管理_删除", parms, out string err);
LoadData();
}
catch (Exception ex) { MessageBox.Show($"删除失败:{ex.Message}"); }
}
}
private void btn_Refresh_Click(object sender, EventArgs e) { _pager.ResetPage(); LoadData(""); }
private void dgv_Data_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex >= 0) btn_Edit_Click(sender, e); }
/// <summary>
/// 新增/修改弹窗
/// </summary>
private void ShowEditDialog(DataGridViewRow row)
{
bool isEdit = row != null;
using (var dlg = CrudHelper.CreateEditDialog(isEdit ? "修改工具" : "新增工具", 420, 380))
{
int y = 20;
var txtName = CrudHelper.AddTextField(dlg, "工具名称:", ref y, isEdit ? row.Cells["工具名称"]?.Value?.ToString() : "");
var txtDesc = CrudHelper.AddTextField(dlg, "说明:", ref y, isEdit ? row.Cells["说明"]?.Value?.ToString() : "");
var txtCalVal = CrudHelper.AddTextField(dlg, "校准值:", ref y, isEdit ? row.Cells["校准值"]?.Value?.ToString() : "0");
var txtCalTime = CrudHelper.AddTextField(dlg, "校准时间:", ref y, isEdit ? row.Cells["校准时间"]?.Value?.ToString() : DateTime.Now.ToString("yyyy-MM-dd"));
var txtUseCnt = CrudHelper.AddTextField(dlg, "使用次数:", ref y, isEdit ? row.Cells["使用次数"]?.Value?.ToString() : "0");
CrudHelper.AddDialogButtons(dlg, ref y);
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
string sp = isEdit ? "工具管理_编辑" : "工具管理_增加";
var pList = new System.Collections.Generic.List<SqlParameter>
{
new SqlParameter("@工具名称", txtName.Text.Trim()),
new SqlParameter("@说明", txtDesc.Text.Trim()),
new SqlParameter("@校准值", txtCalVal.Text.Trim()),
new SqlParameter("@校准时间", DateTime.Parse(txtCalTime.Text)),
new SqlParameter("@使用次数", int.Parse(txtUseCnt.Text))
};
if (isEdit) pList.Insert(0, new SqlParameter("@ID", int.Parse(row.Cells["ID"]?.Value?.ToString() ?? "0")));
SqlOperation.ExecuteStoredProcedure(sp, pList.ToArray(), out string err);
LoadData();
}
catch (Exception ex) { MessageBox.Show($"保存失败:{ex.Message}"); }
}
}
}
private void btn_Export_Click(object sender, EventArgs e)
{
CrudHelper.ExportToExcel("工具管理", "工具管理", () =>
{
string keyword = CrudHelper.GetSearchKeyword(txt_Search, SEARCH_HINT);
var (pageCountParam, itemCountParam) = PagerBar.CreateOutputParams();
var parms = new SqlParameter[]
{
new SqlParameter("@工具名称", keyword),
new SqlParameter("@PageCurrent", 1),
new SqlParameter("@PageSize", 9999999),
pageCountParam, itemCountParam
};
SqlOperation.ExecuteStoredProcedure("工具管理_分页查询", parms, out DataTable dt, out string err);
return dt;
}, dgv_Data.Rows.Count > 0);
}
protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); if (Visible && dgv_Data.DataSource == null) LoadData(); }
}
}

Some files were not shown because too many files have changed in this diff Show More