using FATrace.App.Model; using FATrace.Model; using NLog; using System.Data; using System.Net.NetworkInformation; using System.Text; namespace FATrace.App { public partial class frmMain : Form { public frmMain() { InitializeComponent(); //禁止编译器对跨线程访问做检查 Control.CheckForIllegalCrossThreadCalls = false; // 切换到“历史数据”页时,确保下拉框数据已绑定 TabControlMain.SelectedIndexChanged += (s, e) => { if (TabControlMain.SelectedTab == tabPage1) EnsureSearchRawNameDataBound(); }; // 窗口首次显示后再绑定一次,避免某些情况下 Load 阶段控件状态未准备好 this.Shown += (s, e) => { EnsureSearchRawNameDataBound(); }; } //日志的实例化 private static Logger logger = LogManager.GetCurrentClassLogger(); // TouchSocket 版称重客户端 private TScalTcp? _scaleTcp; // 打印机连接参数(用于状态检测) private string _printerIp = "192.0.1.21"; private int _printerPort = 9100; // 状态缓存,避免频繁重复写日志 private bool? _lastScaleOk = null; private bool? _lastServerOk = null; private bool? _lastPrinterOk = null; // 统一更新状态栏文本与颜色(线程安全) private void UpdateStatusLabel(ToolStripStatusLabel label, string text, Color backColor, Color foreColor) { void Apply() { label.Text = text; label.BackColor = backColor; label.ForeColor = foreColor; } if (InvokeRequired) BeginInvoke((Action)Apply); else Apply(); } private void SetScaleStatusOk(string text) { UpdateStatusLabel(tslWeightState, text, Color.Green, Color.White); _lastScaleOk = true; } private void SetScaleStatusFail(string text) { UpdateStatusLabel(tslWeightState, text, Color.Tomato, Color.White); _lastScaleOk = false; } private void SetServerStatusOk(string text) { UpdateStatusLabel(tslServerState, text, Color.Green, Color.White); _lastServerOk = true; } private void SetServerStatusFail(string text) { UpdateStatusLabel(tslServerState, text, Color.Tomato, Color.White); _lastServerOk = false; } private void SetPrinterStatusOk(string text) { UpdateStatusLabel(tslPrintState, text, Color.Green, Color.White); _lastPrinterOk = true; } private void SetPrinterStatusFail(string text) { UpdateStatusLabel(tslPrintState, text, Color.Tomato, Color.White); _lastPrinterOk = false; } // 使用 ICMP Ping 检测主机可达,避免占用打印机 9100 端口而影响正常打印 private async Task PingAsync(string ip, int timeoutMs) { try { using var ping = new Ping(); var reply = await ping.SendPingAsync(ip, timeoutMs); return reply.Status == IPStatus.Success; } catch { return false; } } private async Task CheckDbStatusAsync() { try { var ok = await Task.Run(() => { try { // 简单心跳 var obj = FSqlContext.FDb.Ado.ExecuteScalar("SELECT 1"); if (obj == null) return false; // 兼容不同数据库返回类型(可能是 long/decimal/string) var v = Convert.ToInt32(obj); return v == 1; } catch { return false; } }); if (_lastServerOk != ok) { if (ok) { SetServerStatusOk("服务器正常"); logger.Info("数据库服务器心跳正常"); } else { SetServerStatusFail("服务器异常"); logger.Error("数据库服务器心跳失败"); } } else { // 状态未变化,但仍在首次加载时给出视觉反馈 if (ok && _lastServerOk == null) SetServerStatusOk("服务器正常"); } } catch (Exception ex) { SetServerStatusFail($"服务器异常:{ex.Message}"); logger.Error(ex, "数据库服务器状态检测异常"); } } private async Task CheckPrinterStatusAsync() { try { // 使用 Ping 检测打印机在线状态,不占用打印端口 bool ok = await PingAsync(_printerIp, 2000); if (_lastPrinterOk != ok) { if (ok) { SetPrinterStatusOk("打印机在线"); logger.Info($"打印机({_printerIp}:{_printerPort})在线"); } else { SetPrinterStatusFail("打印机离线"); logger.Error($"打印机({_printerIp}:{_printerPort})离线"); } } else { if (ok && _lastPrinterOk == null) SetPrinterStatusOk("打印机在线"); } } catch (Exception ex) { SetPrinterStatusFail($"打印机异常:{ex.Message}"); logger.Error(ex, "打印机状态检测异常"); } } /// /// 当前确认者用户名 /// public string CurrentCheckUserNo { get; set; } = string.Empty; /// /// 当前操作者用户名 /// public string CurrentOpUserNo { get; set; } = string.Empty; public string CurrentOperationNoUserLevel { get; set; } = string.Empty; /// /// 计算扫描 Task /// private static Task CycleTask { get; set; } /// /// 日信息 /// 防止跨日的生产的产量的清零 /// private DaySgl CurDaySgl { get; set; } /// /// 线程的启用 /// private bool ThreadEnable { get; set; } = true; private async void frmMain_Load(object sender, EventArgs e) // 将加载事件改为 async 以便等待连接 { this.TabControlMain.SelectedIndex = 1; // 初始化打印机对象 CurZebraPrint = new ZebraPrint(_printerIp, _printerPort); // 下面初始化称重服务(请按需修改 IP 与端口为仪表的实际地址) var scaleIp = "192.168.0.80"; // 仪表的 TCP Server IP(示例值,实际请替换) var scalePort = 10251; // 仪表的 TCP Server 端口(示例值,实际请替换) //日产量初始获取信息 CurDayCount = GetDayCount(); ListRawCtrInfo = new List() { new RawCtrInfo(){ RawName="瑞士乳杆菌GCL1815", RawCode="DYG05030013", BtnControlName="btnRawName1", RawSource=RawSource.China }, new RawCtrInfo(){ RawName="抗性糊精", RawCode="YG03031004", BtnControlName="btnRawName3", RawSource=RawSource.China }, new RawCtrInfo(){ RawName="白砂糖", RawCode="YG03010001", BtnControlName="btnRawName2", RawSource=RawSource.China }, }; // 初始化历史查询下拉框数据 EnsureSearchRawNameDataBound(); CurDaySgl = new DaySgl(); CurDaySgl.DaySglEvent += DaySgl_DaySglEvent; CycleScan(); try // 捕获连接过程中的异常,便于界面提示 { // 使用 TouchSocket 版本的称重客户端 _scaleTcp = new TScalTcp(scaleIp, scalePort); _scaleTcp.StableWeightReceived += ScaleTcp_StableWeightReceived; // 稳定值事件 _scaleTcp.CommunicationError += ScaleTcp_CommunicationError; // 通信异常事件 await _scaleTcp.StartAsync(); // 启动并连接 SetScaleStatusOk($"称重已连接 {scaleIp}:{scalePort}"); logger.Info($"称重连接成功:{scaleIp}:{scalePort}"); // 如需切回 SuperSocket 方案,请注释上方并取消下方注释 // _weightService = new TScalWeightServices(scaleIp, scalePort); // _weightService.StableWeightReceived += WeightService_StableWeightReceived; // _weightService.CommunicationError += WeightService_CommunicationError; // await _weightService.StartAsync(); } catch (Exception ex) // 连接失败时弹出提示 { SetScaleStatusFail($"称重连接失败:{ex.Message}"); logger.Error(ex, "称重连接失败"); MessageBox.Show($"称重连接失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); // 显示错误 } // 启动后台状态检测(DB与打印机) _ = Task.Run(async () => { await CheckDbStatusAsync(); await CheckPrinterStatusAsync(); }); } /// /// 跨日的生产的产量的清零 /// /// /// private void DaySgl_DaySglEvent(object? sender, string e) { CurDayCount = GetDayCount(); } /// /// 周期扫描 /// private void CycleScan() { CycleTask = Task.Run(async () => { int loop = 0; while (ThreadEnable) { await Task.Delay(3000); // 称重连接状态轮询(防止某些厂商设备断线未触发回调) bool scaleOk = _scaleTcp?.IsConnected ?? false; if (_lastScaleOk != scaleOk) { if (scaleOk) { SetScaleStatusOk("称重已连接"); logger.Info("称重连接恢复"); } else { SetScaleStatusFail("称重未连接"); logger.Error("称重连接断开"); } } // 每30秒检测一次服务器与打印机 loop = (loop + 1) % 1000000; if (loop % 10 == 0) await CheckDbStatusAsync(); if (loop % 10 == 5) await CheckPrinterStatusAsync(); } }); } private void btnTest_Click(object sender, EventArgs e) { //new ZebraPrint("192.168.0.40", 9100).PrintWeight("添加剂21", 80.8, "20251021", 3, "02", 2583); } ///// ///// 当前选择的原料名称 ///// //private string CurSelectedRawProInput.RawName { get; set; } /// /// 当前重量数据 /// private double CurWeight { get; set; } = 80.8; ///// ///// 当前剩余数据 ///// //private double CurRemainWeight { get; set; } /// /// 当前选中的生产原料数据 /// private RawProInput CurSelectedRawProInput { get; set; } /// /// 当前打印的驱动数据 /// private ZebraPrint CurZebraPrint { get; set; } /// /// 当天的产量信息 /// private DayCount CurDayCount { get; set; } /// /// 原料控制集合 /// private List ListRawCtrInfo { get; set; } /// /// 选中的原料控件信息 /// private RawCtrInfo SelectedRawCtrInfo { get; set; } #region 原料名称按钮控制 /// /// 高亮当前选中的原料按钮,并还原其他按钮样式 /// private void HighlightRawButton(Button active) { var all = new[] { btnRawName1, btnRawName2, btnRawName3 }; foreach (var b in all) { if (b == null) continue; if (ReferenceEquals(b, active)) { b.UseVisualStyleBackColor = false; b.BackColor = Color.DeepSkyBlue; b.ForeColor = Color.White; } else { b.UseVisualStyleBackColor = true; b.BackColor = SystemColors.Control; b.ForeColor = SystemColors.ControlText; } } } /// /// 统一处理原料按钮点击的选择与高亮 /// private void OnRawNameButtonClicked(Button btn) { //CurSelectedRawName = btn.Text?.Trim(); HighlightRawButton(btn); foreach (var RawCtrInfo in ListRawCtrInfo) { if (RawCtrInfo.BtnControlName == btn.Name) { SelectedRawCtrInfo = RawCtrInfo; break; } } var RawInfoData = CheckRawInfo(SelectedRawCtrInfo.RawName!); if (RawInfoData.result) { //只取最新的第一个原料信息 CurSelectedRawProInput = RawInfoData.data!; txtWeight.Text = CurSelectedRawProInput.Weight.ToString(); txtBatch.Text = CurSelectedRawProInput.Batch!; txtShelfLife.Text = CurSelectedRawProInput.ShelfLife.ToString(); txtRemainWeight.Text = CurSelectedRawProInput.RemainWeight.ToString(); Task.Run(() => { lblRawBeforeInfo.BeginInvoke(new Action(() => { lblRawBeforeInfo.Visible = true; })); Thread.Sleep(3000); lblRawBeforeInfo.BeginInvoke(new Action(() => { lblRawBeforeInfo.Visible = false; })); }); } else { //可能第一次使用的时候,需要初始化数据,把控件的信息给选中的模型 CurSelectedRawProInput = new RawProInput(); CurSelectedRawProInput.RawSource = SelectedRawCtrInfo.RawSource; CurSelectedRawProInput.RawName = SelectedRawCtrInfo.RawName!; CurSelectedRawProInput.RawCode = SelectedRawCtrInfo.RawCode!; //清空分拆的数据 ClearInput(); } } /// /// 检查原料信息 /// /// private (bool result, RawProInput data) CheckRawInfo(string RawName) { //理论上如果存在的话,只有一个信息,因为一个原料没有用完的情况下,是不可以用下一个的产品的,否则提示 var ListRawProInput = FSqlContext.FDb.Select().Where(a => a.RawName == RawName && a.RawState == RawSplitState.Splitting).OrderByDescending(a => a.CreateTime).ToList(); if (ListRawProInput.Count() > 0) { return (true, ListRawProInput.FirstOrDefault()!); } else { //不存在的话,说明是新的原料,可以直接使用 return (false, new RawProInput()); } } private void btnRawName1_Click(object sender, EventArgs e) { if (sender is Button btn) { OnRawNameButtonClicked(btn); } } private void btnRawName3_Click(object sender, EventArgs e) { if (sender is Button btn) { OnRawNameButtonClicked(btn); } } private void btnRawName2_Click(object sender, EventArgs e) { if (sender is Button btn) { OnRawNameButtonClicked(btn); } } private void btnRawNameCommon_Click(object? sender, EventArgs e) { if (sender is Button btn) { OnRawNameButtonClicked(btn); } } #endregion /// /// 确认输入数据 /// 大包装的产线数据 /// /// /// private void btnTrueProInput_Click(object sender, EventArgs e) { try { // 1) 校验已选择的原料名称 if (CurSelectedRawProInput == null || string.IsNullOrWhiteSpace(CurSelectedRawProInput.RawName)) { MessageBox.Show("请先在上方选择原料名称。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // 2) 读取并校验输入框 var weightText = (txtWeight.Text ?? string.Empty).Trim(); var batchText = (txtBatch.Text ?? string.Empty).Trim(); var shelfLifeText = (txtShelfLife.Text ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(weightText) || !double.TryParse(weightText.Replace(',', '.').Replace('。', '.'), out var weight) || weight <= 0) { MessageBox.Show("请输入有效的入库重量(Kg),必须为正数。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); txtWeight.Focus(); txtWeight.SelectAll(); return; } if (string.IsNullOrWhiteSpace(batchText)) { MessageBox.Show("请输入批号。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); txtBatch.Focus(); return; } if (batchText.Length > 20) { MessageBox.Show("批号长度不能超过20个字符。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); txtBatch.Focus(); return; } if (string.IsNullOrWhiteSpace(shelfLifeText) || !int.TryParse(shelfLifeText.Replace(',', '.').Replace('。', '.'), out var shelfLife) || shelfLife <= 0) { MessageBox.Show("请输入有效的保质期(月),必须为正数。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); txtShelfLife.Focus(); txtShelfLife.SelectAll(); return; } // 3) 组装实体 var entity = new RawProInput { RawName = CurSelectedRawProInput!.RawName, RawCode = CurSelectedRawProInput.RawCode, Weight = Math.Round(weight, 2), // 保留3位小数,避免浮点抖动 Batch = batchText, ShelfLife = shelfLife, RawSource = CurSelectedRawProInput.RawSource, RemainWeight = Math.Round(weight, 2), RawState = RawSplitState.Splitting }; // 4) 写入数据库(FreeSql) var affRowsData = FSqlContext.FDb.Insert(entity).ExecuteInserted(); if (affRowsData.Count <= 0) { MessageBox.Show("保存失败:未能写入数据库。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } else { //插入成功后,更新当前选中的产品 CurSelectedRawProInput = affRowsData.FirstOrDefault()!; // 5) 成功提示并清空输入 MessageBox.Show("保存成功。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } } catch (Exception ex) { MessageBox.Show($"保存失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 清零当前的原料 /// /// /// private void btnClearRaw_Click(object sender, EventArgs e) { DialogResult result = MessageBox.Show("您确定清零当前的原料?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.No) { return; } if (CurSelectedRawProInput == null) { MessageBox.Show("请选中原料后再操作。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } var UpdatedData = FSqlContext.FDb.Update() .Set(a => a.RawState, RawSplitState.SplitComplete) .Where(a => a.Id == CurSelectedRawProInput.Id) .ExecuteUpdated(); if (UpdatedData.Count() > 0) { ClearInput(); } } /// /// 称重打印数据 /// /// /// private void btnWeightPrint_Click(object sender, EventArgs e) { if (CurSelectedRawProInput == null) { MessageBox.Show("请先选择要称重的产品", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (string.IsNullOrEmpty(CurSelectedRawProInput.RawName) || string.IsNullOrEmpty(CurSelectedRawProInput.Batch) || string.IsNullOrEmpty(CurSelectedRawProInput.RawCode)) { MessageBox.Show("选择的产品信息无数据,请信息输入后再操作", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } //新的剩余重量 Kg var NewRemainWeight = CurSelectedRawProInput.RemainWeight - CurWeight / 1000; //当前产品的剩余重量 txtRemainWeight.Text = NewRemainWeight.ToString(); //二维码 var Code = GenCode(CurSelectedRawProInput.RawCode!, CurWeight, CurSelectedRawProInput.Batch!, CurSelectedRawProInput.ShelfLife, CurSelectedRawProInput.RawSource, CurDayCount.Count); // 执行打印 try { UpdateStatusLabel(tslPrintState, "正在打印...", Color.Goldenrod, Color.White); CurZebraPrint.PrintWeight(Code, CurSelectedRawProInput.RawName!, CurWeight, CurSelectedRawProInput.Batch!, CurSelectedRawProInput.ShelfLife ); SetPrinterStatusOk("打印成功"); logger.Info($"打印成功:{CurSelectedRawProInput.RawName} {CurWeight}g 批号{CurSelectedRawProInput.Batch}"); } catch (Exception ex) { SetPrinterStatusFail($"打印失败:{ex.Message}"); logger.Error(ex, "打印失败"); MessageBox.Show($"打印失败:{ex.Message}", "打印机错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } //内包条码更新 txtCode.Text = Code; // 写入称重使用记录 var Result = FSqlContext.FDb.Insert(new RawProUse() { Batch = CurSelectedRawProInput.Batch, InBagCode = Code, BoxCode = Code + ",A", OpUser = CurrentOpUserNo, CheckUser = CurrentCheckUserNo, RawCode = CurSelectedRawProInput.RawCode, ShelfLife = CurSelectedRawProInput.ShelfLife, RawName = CurSelectedRawProInput.RawName, RemainWeight = NewRemainWeight, Weight = CurWeight, DeliveryDate= DateTime.Now.ToString("yyyy-MM-dd"), WeightTime = DateTime.Now, StockWeight = CurSelectedRawProInput.Weight, }).ExecuteAffrows(); if (Result > 0) { //结束后这个重量 CurSelectedRawProInput.RemainWeight = NewRemainWeight; txtRemainWeight.Text = CurSelectedRawProInput.RemainWeight.ToString(); //称重检查,如果剩余重量为0,进行提示,当剩余的重量小于称重的重量,进行提示 if (CurSelectedRawProInput.RemainWeight < CurWeight / 1000.0) { //剩余的重量小于称重的重量,进行提示,强制进行清零 Task.Run(() => { lblRawUseStateTip.BeginInvoke(new Action(() => { lblRawUseStateTip.Visible = true; })); Thread.Sleep(3000); lblRawUseStateTip.BeginInvoke(new Action(() => { lblRawUseStateTip.Visible = false; })); }); //更新数据,完成 FSqlContext.FDb.Update() .Set(a => a.RemainWeight, NewRemainWeight) .Set(a => a.RawState, RawSplitState.SplitComplete) .Where(a => a.Id == CurSelectedRawProInput.Id) .ExecuteAffrows(); //当前的产品信息清零 CurSelectedRawProInput = null; //清空分拆的数据 ClearInput(); //恢复btnRawName1,2,3的默认样式 SetDefaultStyle(panel1); } else { //当前分拆的产品的剩余重量更新数据 FSqlContext.FDb.Update() .Set(a => a.RemainWeight, NewRemainWeight) .Where(a => a.Id == CurSelectedRawProInput.Id) .ExecuteAffrows(); } // 产量 +1 CurDayCount.Count = CurDayCount.Count + 1; UpdateDayCount(CurDayCount); } } /// /// 获取当前的日产量信息 /// /// private DayCount GetDayCount() { var CurDayCountInfo = FSqlContext.FDb.Select().Where(a => a.DayInfo == DateTime.Now.ToString("yyyy-MM-dd")).First(); if (CurDayCountInfo == null) { //当日的日产量信息不存在,第一次的话就新建信息 var ReturnData = FSqlContext.FDb.Insert(new DayCount() { Count = 1, DayInfo = DateTime.Now.ToString("yyyy-MM-dd"), }).ExecuteInserted(); return ReturnData.FirstOrDefault()!; } else { //否则的话,获取当前的日产量信息 return CurDayCountInfo; } } /// /// 更新日产量信息 /// private bool UpdateDayCount(DayCount dayCount) { var Data = FSqlContext.FDb.Update() .Set(a => a.Count, dayCount.Count) .Set(a => a.UpdateTime, DateTime.Now) .Where(a => a.Id == dayCount.Id) .ExecuteAffrows(); if (Data > 0) { return true; } return false; } /// /// 清空输入 /// private void ClearInput() { txtWeight.Text = ""; txtBatch.Text = ""; txtShelfLife.Text = ""; txtRemainWeight.Text = ""; } /// /// 恢复panel1内按钮默认样式 /// /// private void SetDefaultStyle(Panel panel) { foreach (Control control in panel.Controls) { if (control is Button btn) { btn.BackColor = Color.Transparent; btn.ForeColor = Color.Black; } } } /// /// 生成条码信息 /// /// public string GenCode(string RawCode, double Weight, string Batch, int ShelfLife, RawSource Source, int DayCount) { //二维码数据 var Code = new StringBuilder(); Code.Append(RawCode); Code.Append(','); Code.Append(Batch); Code.Append(','); Code.Append(Weight.ToString().Replace(".", "")); Code.Append(','); Code.Append(ShelfLife.ToString()); Code.Append(','); Code.Append(GetSourceInf(Source)); Code.Append(','); Code.Append(DayCount.ToString()); return Code.ToString(); } /// /// 获取枚举的信息 /// /// public string GetSourceInf(RawSource rawSource) { switch (rawSource) { case RawSource.China: return "01"; case RawSource.Japan: return "02"; default: return "01"; } } // TouchSocket 版本稳定数据事件处理 private void ScaleTcp_StableWeightReceived(object? sender, TScalTcp.StableWeightEventArgs e) { void UpdateUi() { CurWeight = (double)e.Value; txtRtWeight.Text = e.Value.ToString("0.###"); // 数据收发正常 if (_lastScaleOk != true) { SetScaleStatusOk("称重通讯正常"); } } if (InvokeRequired) BeginInvoke((Action)UpdateUi); else UpdateUi(); } // TouchSocket 版本通信错误处理 private void ScaleTcp_CommunicationError(object? sender, string message) { void ShowError() { MessageBox.Show(message, "称重通信错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); } // 更新状态与日志 SetScaleStatusFail(message); logger.Error($"称重通信错误:{message}"); if (InvokeRequired) BeginInvoke((Action)ShowError); else ShowError(); } // 窗体关闭时,停止称重服务,释放网络连接 protected override void OnFormClosed(FormClosedEventArgs e) { try { _scaleTcp?.StopAsync().GetAwaiter().GetResult(); } catch { } base.OnFormClosed(e); } /// private void btnLogin_Click(object sender, EventArgs e) { try { if (string.IsNullOrEmpty(txtCheckUserName.Text)) { MessageBox.Show("请输入确认者用户名称", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (string.IsNullOrEmpty(txtOpName.Text)) { MessageBox.Show("请输入操作者用户名称", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (string.IsNullOrEmpty(txtPassword.Text)) { MessageBox.Show("请输入密码", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } var ListUser = FSqlContext.FDb.Select().Where(a => a.UserName == txtCheckUserName.Text.Trim()).ToList(); if (ListUser != null && ListUser.Count() > 0) { if (ListUser.FirstOrDefault().Password == txtPassword.Text.Trim()) { MessageBox.Show("登录成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); Main_PopUserNameEvent(txtCheckUserName.Text.Trim(), txtOpName.Text.Trim(), ListUser.FirstOrDefault().AccessLevel); this.TabControlMain.SelectedIndex = 0; txtCheckUserName.Text = ""; txtPassword.Text = ""; //PopUserNameEvent(txtUserName.Text.Trim()); //this.Close(); } else { MessageBox.Show("密码错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } else { MessageBox.Show("当前用户不存在!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } catch (Exception ex) { logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); //insertLogToDBDelegate.BeginInvoke(1, "UpdateUIMethod异常", ex.Message.ToString() + ex.StackTrace.Substring(ex.StackTrace.Length - 40, 40), null, null); } } /// /// 用户登录的事件发布方法 /// /// private void Main_PopUserNameEvent(string CheckUserName, string OpUserName, string UserLevel) { try { CurrentCheckUserNo = CheckUserName; CurrentOpUserNo = OpUserName; CurrentOperationNoUserLevel = UserLevel; Invoke(new Action(() => { tslCurrentUser.Text = "确认者用户:" + CurrentCheckUserNo; tslCurrentUser.BackColor = Color.Green; tslCurrentUser.ForeColor = Color.White; })); } catch (Exception ex) { logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); //insertLogToDBDelegate.BeginInvoke(1, "UpdateUIMethod异常", ex.Message.ToString() + ex.StackTrace.Substring(ex.StackTrace.Length - 40, 40), null, null); } } #region 系统按钮的操作 private void bntFrmLogin_Click(object sender, EventArgs e) { TabControlMain.SelectedIndex = 1; } private void btnSysConfig_Click(object sender, EventArgs e) { if (!string.IsNullOrEmpty(CurrentCheckUserNo)) { } } private void btnExit_Click(object sender, EventArgs e) { try { if (DialogResult.OK == MessageBox.Show("你确定要关闭程序系统吗?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning)) { ThreadEnable = false; // 断开连接 //_MelsecMcNet?.ConnectClose(); // 断开连接 this.Close(); System.Environment.Exit(System.Environment.ExitCode); //Application.ExitThread(); //Application.Exit(); } else { } } catch (Exception ex) { logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); //ScanSerialPort.Close(); //HandSerialPort.Close(); //_MelsecMcNet?.ConnectClose(); //insertLogToDBDelegate.BeginInvoke(1, "UpdateUIMethod异常", ex.Message.ToString() + ex.StackTrace.Substring(ex.StackTrace.Length - 40, 40), null, null); } } private void btnMain_Click(object sender, EventArgs e) { if (!string.IsNullOrEmpty(CurrentCheckUserNo)) { TabControlMain.SelectedIndex = 0; } else { MessageBox.Show("请登录后再操作!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } private void btnHistoryData_Click(object sender, EventArgs e) { try { TabControlMain.SelectedIndex = 2; // 切换到历史数据页 EnsureSearchRawNameDataBound(); // 确保下拉框有数据 } catch { } } #endregion #region 历史数据搜索 private void btnSearchHistoryData_Click(object sender, EventArgs e) { try { // 初始化下拉框数据源(仅在首次/为空时绑定) EnsureSearchRawNameDataBound(); // 时间范围:按日期选择,自动扩大为整日区间 var startDate = dtpSearchStartTime.Value.Date; // 00:00:00 var endDate = dtpSearchEndTime.Value.Date.AddDays(1).AddTicks(-1); // 23:59:59.9999999 if (startDate > endDate) { MessageBox.Show("开始时间不能晚于结束时间。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // 原料名称/编码关键字:允许为空(为空则不过滤)。 var rawKeyword = (cbxSearchRawName.Text ?? string.Empty).Trim(); // 批号:精确匹配 var batchKeyword = (txtSearchBatch.Text ?? string.Empty).Trim(); // 构建 FreeSql 查询 var select = FSqlContext.FDb.Select() .Where(a => a.WeightTime >= startDate && a.WeightTime <= endDate); if (!string.IsNullOrWhiteSpace(rawKeyword)) // 名称与编码均支持模糊匹配 select = select.Where(a => (a.RawName != null && a.RawName.Contains(rawKeyword)) || (a.RawCode != null && a.RawCode.Contains(rawKeyword))); if (!string.IsNullOrWhiteSpace(batchKeyword)) select = select.Where(a => a.Batch.Contains(batchKeyword)); var result = select.OrderByDescending(a => a.WeightTime).ToList(); // 绑定到 DataGridView dataGridView1.AutoGenerateColumns = true; dataGridView1.DataSource = result; ApplyChineseColumnHeaders(); } catch (Exception ex) { MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 确保搜索条件中的“原料名称”下拉框已从 ListRawCtrInfo 绑定数据。 /// private void EnsureSearchRawNameDataBound() { // 优先使用界面配置的原料集合;若为空则回退到数据库历史记录中的原料名称 string[] names = Array.Empty(); if (ListRawCtrInfo != null && ListRawCtrInfo.Count > 0) { names = ListRawCtrInfo .Where(x => !string.IsNullOrWhiteSpace(x.RawName)) .Select(x => x.RawName!.Trim()) .Distinct() .ToArray(); } else { try { names = FSqlContext.FDb.Select() .Where(a => a.RawName != null && a.RawName != "") .OrderBy(a => a.RawName) .ToList(a => a.RawName!) .Where(s => !string.IsNullOrWhiteSpace(s)) .Distinct() .ToArray(); } catch { /* 忽略绑定期的数据库异常,保持界面可用 */ } } cbxSearchRawName.BeginUpdate(); try { cbxSearchRawName.DataSource = null; cbxSearchRawName.Items.Clear(); if (names.Length > 0) cbxSearchRawName.Items.AddRange(names); // 允许直接输入(支持编码模糊输入),并打开联想补全 cbxSearchRawName.DropDownStyle = ComboBoxStyle.DropDown; cbxSearchRawName.AutoCompleteMode = AutoCompleteMode.SuggestAppend; // 使用自定义自动完成源,加入“名称”和“编码”,便于输入编码模糊匹配 var ac = new AutoCompleteStringCollection(); try { if (ListRawCtrInfo != null) { var codes = ListRawCtrInfo .Where(x => !string.IsNullOrWhiteSpace(x.RawCode)) .Select(x => x.RawCode!.Trim()); var texts = names.Concat(codes).Distinct().ToArray(); ac.AddRange(texts); } else { ac.AddRange(names); } } catch { ac.AddRange(names); } cbxSearchRawName.AutoCompleteCustomSource = ac; cbxSearchRawName.AutoCompleteSource = AutoCompleteSource.CustomSource; cbxSearchRawName.SelectedIndex = -1; // 默认不选中 } finally { cbxSearchRawName.EndUpdate(); } } /// /// 将 DataGridView 列头设置为中文显示。 /// private void ApplyChineseColumnHeaders() { // 隐藏行头(左侧灰条) dataGridView1.RowHeadersVisible = false; void SetHeader(string name, string text) { var col = dataGridView1.Columns[name]; if (col != null) col.HeaderText = text; } // 移除编号列 var idCol = dataGridView1.Columns["Id"]; if (idCol != null) dataGridView1.Columns.Remove(idCol); SetHeader("RawCode", "原料编号"); SetHeader("RawName", "原料名称"); SetHeader("InBagCode", "内袋二维码"); SetHeader("BoxCode", "外箱二维码"); SetHeader("Batch", "原料批号"); SetHeader("ShelfLife", "保质期"); SetHeader("Weight", "称重重量(g)"); SetHeader("RemainWeight", "剩余重量(Kg)"); SetHeader("StockWeight", "入库总重量(Kg)"); SetHeader("WeightTime", "称重时间"); SetHeader("OpUser", "操作员"); SetHeader("CheckUser", "确认者"); SetHeader("OutTime", "出库时间"); SetHeader("CreateTime", "创建时间"); } #endregion private void frmMain_FormClosed(object sender, FormClosedEventArgs e) { //CurZebraPrint.Close(); if (_scaleTcp != null) { _scaleTcp!.StopAsync(); } } } }