using FATrace.App.Model;
using FATrace.Com;
using FATrace.Model;
using NLog;
using System.Data;
using System.Globalization;
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;
///
/// 关闭确认标记:用于避免重复弹窗(如关闭过程触发多次 FormClosing)
///
private bool _closeConfirmed;
// 打印机连接参数(用于状态检测)
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;
///
/// 称重上限
///
private double WeightUp { get; set; }
///
/// 称重下限
///
private double WeightDown { get; set; }
///
/// 计算扫描 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 端口(示例值,实际请替换)
if (double.TryParse(ConfigHelper.GetStringOrDefault("WeightUp", "10"), out var weightUp))
{
WeightUp = weightUp;
}
if (double.TryParse(ConfigHelper.GetStringOrDefault("WeightDown", "200"), out var weightDown))
{
WeightDown = weightDown;
}
txtWeightUp.Text = weightUp.ToString();
txtWeightDown.Text = weightDown.ToString();
//日产量初始获取信息
CurDayCount = GetDayCount();
ListRawCtrInfo = new List()
{
new RawCtrInfo(){
RawName="瑞士乳杆菌GCL1815",
RawCode="1121000265",
BtnControlName="btnRawName1",
RawSource=RawSource.Japan
},
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("称重连接断开");
}
}
CurDaySgl.CurDay = DateTime.Now.ToString("yyyy-MM-dd");
// 每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; } = 0;
/////
///// 当前剩余数据
/////
//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;
}
if (WeightDown > 0 && WeightUp > 0)
{
if (CurWeight < WeightDown || CurWeight > WeightUp)
{
DialogResult resultRangeCheck = frmMessage.ShowConfirm($"检测到当前重量 {CurWeight:0.###}g 不在配置范围 [{WeightDown:0.###}g, {WeightUp:0.###}g] 内,确定要【打印】操作吗?", "确认操作", this);
if (resultRangeCheck == DialogResult.Cancel)
{
return;
}
else
{
logger.Info("你打印了一个不在配置范围内的重量!!!");
}
}
}
//确认数据
// 显示消息框,并等待用户响应
DialogResult result = frmMessage.ShowConfirm("确定要【打印】操作吗?", "确认操作", this);
if (result == DialogResult.Cancel)
{
return;
}
//if (CurWeight < 2.0)
//{
// //确认数据
// // 显示消息框,并等待用户响应
// DialogResult resultWeightCheck = frmMessage.ShowConfirm("检测到当前的重量小于2g,确定要【打印】操作吗?", "确认操作", this);
// if (resultWeightCheck == DialogResult.Cancel)
// {
// return;
// }
//}
//新的剩余重量 Kg
var NewRemainWeight = CurSelectedRawProInput.RemainWeight - CurWeight / 1000.0;
//当前产品的剩余重量
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);
}
}
///
/// 格式化重量显示,自动适配整数部分位数(2-4位),小数部分固定2位
///
/// 原始重量值
/// /// 格式化后的重量字符串(如:09.50, 81.10, 100.00, 1000.00)
private string FormatWeight(double weight)
{
// 根据重量值大小决定格式
if (weight >= 1000)
return weight.ToString("0000.00"); // 四位整数:1000.00-9999.99
else if (weight >= 100)
return weight.ToString("000.00"); // 三位整数:100.00-999.99
else
return weight.ToString("00.00"); // 两位整数:00.00-99.99
}
///
/// 获取当前的日产量信息
///
///
private DayCount GetDayCount()
{
var CurDayCountInfo = FSqlContext.FDb.Select().Where(a => a.DayInfo == DateTime.Now.ToString("yyyy-MM-dd")).First();
if (CurDayCountInfo == null)
{
logger.Info($"日产量信息不存在,新建日产量信息");
//当日的日产量信息不存在,第一次的话就新建信息
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(FormatWeight(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();
}
///
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.CheckName == txtCheckUserName.Text.Trim() && a.OpName == txtOpName.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(), "称重用户");
this.TabControlMain.SelectedIndex = 0;
txtCheckUserName.Text = "";
txtOpName.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();
}
}
///
/// 重新打印之前最新的一个
///
///
///
private void btnReprint_Click(object sender, EventArgs e)
{
try
{
if (CurSelectedRawProInput == null)
{
MessageBox.Show("请先选择要称重的产品", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var LastData = FSqlContext.FDb.Select().OrderByDescending(a => a.WeightTime).First();
if (LastData == null)
{
MessageBox.Show("没有找到最新的消息", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// 显示消息框,并等待用户响应
DialogResult result = frmMessage.ShowConfirm($"确定要【重复打印】{LastData.InBagCode} 条码数据吗?", "确认操作", this);
if (result == DialogResult.Cancel)
{
return;
}
// 执行打印
try
{
UpdateStatusLabel(tslPrintState, "正在打印...", Color.Goldenrod, Color.White);
CurZebraPrint.PrintWeight(LastData.InBagCode!, 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;
}
}
catch (Exception ex)
{
logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString()));
}
}
///
/// 保存配置到Config并使用
///
///
///
private void btnConfigSave_Click(object sender, EventArgs e)
{
try
{
var downText = (txtWeightDown.Text ?? string.Empty).Trim();
var upText = (txtWeightUp.Text ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(downText) ||
!double.TryParse(downText.Replace(',', '.').Replace('。', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var down) || down <= 0)
{
MessageBox.Show("请输入有效的下限(g),必须为正数。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtWeightDown.Focus();
txtWeightDown.SelectAll();
return;
}
if (string.IsNullOrWhiteSpace(upText) ||
!double.TryParse(upText.Replace(',', '.').Replace('。', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var up) || up <= 0)
{
MessageBox.Show("请输入有效的上限(g),必须为正数。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtWeightUp.Focus();
txtWeightUp.SelectAll();
return;
}
if (down >= up)
{
MessageBox.Show("下限(g)必须小于上限(g)。", "校验失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtWeightDown.Focus();
txtWeightDown.SelectAll();
return;
}
var downStr = down.ToString("0.###", CultureInfo.InvariantCulture);
var upStr = up.ToString("0.###", CultureInfo.InvariantCulture);
ConfigHelper.SetValue("WeightDown", downStr);
ConfigHelper.SetValue("WeightUp", upStr);
WeightDown = down;
WeightUp = up;
txtWeightDown.Text = downStr;
txtWeightUp.Text = upStr;
logger.Info($"称重配置已更新:WeightDown={downStr}g, WeightUp={upStr}g");
MessageBox.Show("配置已保存并立即生效。", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
logger.Error(ex, "保存称重配置失败");
MessageBox.Show($"保存配置失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (!_closeConfirmed && e.CloseReason == CloseReason.UserClosing)
{
try
{
var result = MessageBox.Show(
"确认退出系统?",
"退出确认",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2);
if (result != DialogResult.Yes)
{
e.Cancel = true;
logger.Info("已取消关闭");
return;
}
_closeConfirmed = true;
logger.Info("用户确认关闭,开始退出");
}
catch (Exception ex)
{
// 弹窗异常时,为保证可关闭,默认继续退出,但记录日志
logger.Error(ex, "关闭确认弹窗异常");
_closeConfirmed = true;
}
}
}
}
}