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 { /// /// 系统设置页面 — 扫码枪连接管理 /// 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; } } /// /// 构建开机自启设置区域,状态直接读取Windows当前用户启动项。 /// 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(); } /// /// 动态构建单个扫码枪面板的控件 /// 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 = $@" 潍柴称重工控机程序开机自启 true PT30S InteractiveToken HighestAvailable IgnoreNew false false true true false false false true true false false false PT0S 7 {SecurityElement.Escape(exePath)} {SecurityElement.Escape(workDir)} "; 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 = "--"; } } } }