Files
WC-CZGKJ/SCADA/Pages/UC_SystemSettings.cs
2026-05-16 14:50:22 +08:00

556 lines
21 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Security;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
namespace MesWork.Pages
{
/// <summary>
/// 系统设置页面 — 扫码枪连接管理
/// </summary>
public partial class UC_SystemSettings : UserControl
{
private const string AutoStartKeyName = "WC_GKJ_OIL";
private const string AutoStartRunKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
private const string AutoStartTaskName = "WC_GKJ_OIL_AutoStart";
private bool _isLoadingAutoStart;
private CheckBox chk_AutoStart;
private Label lbl_AutoStartStatus;
// 工位1控件
private ComboBox cmb_Com1;
private Button btn_Connect1, btn_Disconnect1, btn_ManualSend1;
private Label lbl_Status1, lbl_ScanTime1;
private TextBox txt_Barcode1;
// 工位2控件
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();
string station1 = MesWorkForm.Station1OpName;
string station2 = MesWorkForm.Station2OpName;
grp_Station1.Text = $" 工位1{station1} ";
grp_Station2.Text = $" 工位2{station2} ";
grp_Scanner1.Text = $" {station1} 扫码枪 ";
grp_Scanner2.Text = $" {station2} 扫码枪 ";
BuildAutoStartPanel();
if (BarcodeManager.IsEnabled)
{
BuildScannerPanel(grp_Scanner1, station1, ref cmb_Com1, ref lbl_Status1, ref txt_Barcode1, ref lbl_ScanTime1,
ref btn_Connect1, ref btn_Disconnect1, ref btn_ManualSend1);
BuildScannerPanel(grp_Scanner2, station2, 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, station1, ref cmb_Com1, ref lbl_Status1, ref txt_Barcode1, ref lbl_ScanTime1,
ref btn_Connect1, ref btn_Disconnect1, ref btn_ManualSend1);
BuildScannerPanel(grp_Scanner2, station2, 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>
/// 构建开机自启设置区域状态直接读取Windows当前用户启动项。
/// </summary>
private void BuildAutoStartPanel()
{
var grpAutoStart = new GroupBox
{
Text = " 开机自启 ",
Font = new Font("微软雅黑", 12F, FontStyle.Bold),
ForeColor = Color.FromArgb(30, 58, 95),
Location = new Point(30, 410),
Size = new Size(840, 120)
};
chk_AutoStart = new CheckBox
{
Text = "启用开机自启",
Font = new Font("微软雅黑", 11F, FontStyle.Bold),
ForeColor = Color.FromArgb(31, 41, 55),
Location = new Point(24, 36),
AutoSize = true,
Cursor = Cursors.Hand
};
chk_AutoStart.CheckedChanged += chk_AutoStart_CheckedChanged;
grpAutoStart.Controls.Add(chk_AutoStart);
lbl_AutoStartStatus = new Label
{
Text = "--",
Font = new Font("微软雅黑", 10F),
ForeColor = Color.FromArgb(75, 85, 99),
Location = new Point(24, 72),
AutoSize = true
};
grpAutoStart.Controls.Add(lbl_AutoStartStatus);
Controls.Add(grpAutoStart);
RefreshAutoStartState();
}
/// <summary>
/// 动态构建单个扫码枪面板的控件
/// </summary>
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 string GetAutoStartCommand()
{
return Application.ExecutablePath;
}
private bool RunSchtasks(string arguments, out string output)
{
output = "";
try
{
var psi = new ProcessStartInfo
{
FileName = "schtasks.exe",
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (Process process = Process.Start(psi))
{
output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd();
process.WaitForExit();
return process.ExitCode == 0;
}
}
catch (Exception ex)
{
output = ex.Message;
return false;
}
}
private bool TryGetAutoStartTaskXml(out string taskXml, out string errMsg)
{
taskXml = "";
errMsg = "";
if (RunSchtasks($"/Query /TN \"{AutoStartTaskName}\" /XML", out string output))
{
taskXml = output;
return true;
}
if (IsTaskNotFoundOutput(output))
{
return true;
}
errMsg = output;
return false;
}
private bool IsTaskNotFoundOutput(string output)
{
return output.IndexOf("cannot find", StringComparison.OrdinalIgnoreCase) >= 0 ||
output.Contains("找不到") ||
output.Contains("不存在");
}
private void DeleteLegacyRegistryAutoStart()
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(AutoStartRunKeyPath, true))
{
key?.DeleteValue(AutoStartKeyName, false);
}
}
catch
{
}
}
private bool TryCreateAutoStartTask(out string errMsg)
{
errMsg = "";
string xmlPath = Path.Combine(Path.GetTempPath(), $"{AutoStartTaskName}.xml");
string exePath = GetAutoStartCommand();
string workDir = Path.GetDirectoryName(exePath);
string taskXml =
$@"<?xml version=""1.0"" encoding=""UTF-16""?>
<Task version=""1.2"" xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task"">
<RegistrationInfo>
<Description>潍柴称重工控机程序开机自启</Description>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
<Delay>PT30S</Delay>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id=""Author"">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>false</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context=""Author"">
<Exec>
<Command>{SecurityElement.Escape(exePath)}</Command>
<WorkingDirectory>{SecurityElement.Escape(workDir)}</WorkingDirectory>
</Exec>
</Actions>
</Task>";
try
{
File.WriteAllText(xmlPath, taskXml, System.Text.Encoding.Unicode);
if (RunSchtasks($"/Create /TN \"{AutoStartTaskName}\" /XML \"{xmlPath}\" /F", out string output))
{
DeleteLegacyRegistryAutoStart();
return true;
}
errMsg = output;
return false;
}
catch (Exception ex)
{
errMsg = ex.Message;
return false;
}
finally
{
try { if (File.Exists(xmlPath)) File.Delete(xmlPath); } catch { }
}
}
private bool IsCurrentProgramAutoStart(string taskXml)
{
return !string.IsNullOrWhiteSpace(taskXml) &&
taskXml.IndexOf(GetAutoStartCommand(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private bool TrySetAutoStart(bool enabled, out string errMsg)
{
errMsg = "";
if (enabled)
{
return TryCreateAutoStartTask(out errMsg);
}
DeleteLegacyRegistryAutoStart();
if (RunSchtasks($"/Delete /TN \"{AutoStartTaskName}\" /F", out string output))
{
return true;
}
if (IsTaskNotFoundOutput(output))
{
return true;
}
errMsg = output;
return false;
}
private void RefreshAutoStartState()
{
if (chk_AutoStart == null || lbl_AutoStartStatus == null) return;
_isLoadingAutoStart = true;
try
{
if (!TryGetAutoStartTaskXml(out string taskXml, out string errMsg))
{
chk_AutoStart.Checked = false;
lbl_AutoStartStatus.Text = $"读取任务计划失败:{errMsg}";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(239, 68, 68);
return;
}
bool isCurrentProgramAutoStart = IsCurrentProgramAutoStart(taskXml);
chk_AutoStart.Checked = isCurrentProgramAutoStart;
if (isCurrentProgramAutoStart)
{
lbl_AutoStartStatus.Text = $"已启用任务计划:{Application.ExecutablePath}";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(16, 185, 129);
}
else if (string.IsNullOrWhiteSpace(taskXml))
{
lbl_AutoStartStatus.Text = "未启用";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(107, 114, 128);
}
else
{
lbl_AutoStartStatus.Text = "任务计划存在但路径不是当前程序,请重新勾选修正";
lbl_AutoStartStatus.ForeColor = Color.FromArgb(245, 158, 11);
}
}
finally
{
_isLoadingAutoStart = false;
}
}
private void chk_AutoStart_CheckedChanged(object sender, EventArgs e)
{
if (_isLoadingAutoStart) return;
bool enabled = chk_AutoStart.Checked;
if (!TrySetAutoStart(enabled, out string errMsg))
{
MessageBox.Show($"开机自启设置失败:{errMsg}", "设置失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
RefreshAutoStartState();
}
private void DoConnect(string opName, ComboBox cmb)
{
string com = cmb.SelectedItem?.ToString();
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 async void btn_WeightClear1_Click(object sender, EventArgs e)
{
await DoWeightClearAsync(MesWorkForm.Station1OpName, btn_WeightClear1);
}
private async void btn_WeightClear2_Click(object sender, EventArgs e)
{
await DoWeightClearAsync(MesWorkForm.Station2OpName, btn_WeightClear2);
}
private async Task DoWeightClearAsync(string opName, Button btn)
{
try
{
btn.Enabled = false;
MesWorkForm.WritePLC_IF(100017, opName, 1);
await Task.Delay(1000);
MesWorkForm.WritePLC_IF(100017, opName, 0);
MessageBox.Show($"{opName} 称重清零信号已发送", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MesWorkForm.WriteLog_Info("系统设置", $"{opName} 称重清零失败:{ex.Message}", ex);
MessageBox.Show($"{opName} 称重清零失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
btn.Enabled = true;
}
}
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(MesWorkForm.Station1OpName, lbl_Status1, txt_Barcode1, lbl_ScanTime1);
RefreshScannerStatus(MesWorkForm.Station2OpName, 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 = "--";
}
}
}
}