初期稳定版本260119

This commit is contained in:
2026-01-19 12:40:45 +08:00
parent 82d0bd6b95
commit f65fa21760
32 changed files with 2494 additions and 138 deletions

View File

@@ -0,0 +1,360 @@
# FATrace.App 用户操作手册SOP
## 1. 文档说明
- **适用范围**
称重客户端,用于原料入库、称重打印、消耗记录入库与历史数据查询。
- **目标读者**
产线操作员、确认者/班组长、IT 运维。
- **术语约定**
- **入库重量/剩余重量**:单位为 **Kg**
- **称重重量**:单位为 **g**
- **内袋二维码**:打印在内袋标签上的二维码内容(系统生成)。
## 2. 系统概述
称重客户端用于在产线对特定原料进行:
- **原料入库建档**录入批号、保质期、入库重量Kg形成一条“分拆中”的原料记录。
- **实时称重 + 打印内袋二维码**从称重仪表获取稳定重量g生成二维码并调用斑马打印机打印标签。
- **消耗记录入库**:每次打印成功后写入使用记录,并更新该原料剩余重量。
- **历史数据查询**:按日期范围、原料名称/编码关键字、批号查询消耗明细。
![image-20260116144602471](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116144602471.png)
## 3. 运行环境与依赖
- **操作系统**
Windows 10/11。
- **网络与设备**
- SQL Server 数据库(称重服务器)
- 称重仪表
- 斑马打印机
## 4. 安装与部署IT/工程师运维)
### 4.1 部署方式
- **推荐**:使用发布后的程序目录(含可执行文件、依赖 DLL、`App.config``NLog.config` 等)。
- **注意**:首次部署时确保程序目录对当前登录用户具有读写权限(日志目录会写入到程序目录下)。
### 4.2 单实例程序限制
**==程序启动时会检测是否已运行;若已运行会提示“程序已运行,不能再次打开!”。==**
## 5. 配置说明(必须掌握)
### 5.1 数据库连接App.config
文件:`FATrace.App/App.config`
系统实际使用的连接键为:`connecting`
- **connecting**FATrace 主库SQL Server连接字符串
**修改建议**
- **[服务器地址]**`Data Source=...`
- **[数据库名]**`Initial Catalog=...`
- **[账号密码]**`User ID=...;Password=...`
- 如启用证书校验,需按现场策略配置 `TrustServerCertificate` 等参数。
### 5.2 PLC 配置App.config
`PLCIP / PLCPort / PLCScan` 为保留配置项;若现场版本启用 PLC 通信,请由 IT 运维按实际设备信息配置。
### 5.3 打印机与称重仪表配置(当前版本说明)
当前版本中:
- 打印机 IP/端口:在主窗体中默认值为 `_printerIp=192.0.1.21``_printerPort=9100`
- 称重仪表 IP/端口:在主窗体加载时使用示例值 `192.168.0.80:10251`
如果现场设备地址不同:
- **建议由开发/IT 运维修改为配置化**(后续可以将 IP/Port 放入 `App.config` 并提供配置界面)。
- 在未配置化前,需更新代码并重新发布程序。
### 5.4 日志配置NLog.config
文件:`FATrace.App/NLog.config`
- **错误日志**`${basedir}/Log/${shortdate}/ErrorMessage.txt`
- **操作日志**`${basedir}/OperationLogs/${shortdate}/OperationLog.txt`
- **保留天数**:默认 `maxArchiveDays=60`
## 6. 界面结构与功能入口
主界面由“左侧导航栏 + 右侧 Tab 页面”组成。
![image-20260116145200152](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145200152.png)
### 6.1 左侧导航栏
- **主界面**:进入生产主页面(需要已登录)
- **历史数据**:进入历史查询页面
- **用户登录**:进入登录页面
- **退出**:关闭程序
### 6.2 右侧页面
- **生产主页面**:原料选择、入库信息录入、实时称重显示、打印与剩余重量显示
![image-20260116145253025](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145253025.png)
- **用户登录页面**:确认者/操作者登录
![image-20260116145313862](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145313862.png)
- **历史数据页面**:按条件查询并显示 `RawProUse` 历史记录
![image-20260116145330211](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145330211.png)
### 6.3 状态栏(底部)
![image-20260116145400609](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145400609.png)
- **用户**:显示当前确认者用户
- **称重状态**:称重仪表连接/通信状态
- **服务器通信状态**:数据库心跳状态
- **打印机状态**:打印机在线/离线状态
## 7. 角色与权限(业务规则)
### 7.1 登录角色
- **确认者用户**:用于确认生产记录的责任人(系统登录校验账号)
- **操作者用户**:用于记录实际操作人(当前版本为文本输入,不做账号校验)
### 7.2 用户来源
系统登录会在数据库表 `TbUser` 中按 `UserName` 查询并校验密码。
- **账号不存在**:提示“当前用户不存在!”
- **密码错误**:提示“密码错误!”
- **登录成功**:提示“登录成功!”并切换到主界面
## 8. 日常操作 SOP产线操作员
### 8.1 开机前检查(每日/每班)
> **==图片示例:称重连接失败,打印机未正常连接==**
>
> ![image-20260116145400609](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145400609.png)
- **[数据库连接]**
状态栏“服务器通信状态”应显示“服务器正常”。
- **[称重仪表]**
状态栏“称重状态”应显示“称重已连接/称重通讯正常”。
- **[打印机]**
状态栏“打印机状态”应显示“打印机在线”。
- **[标签耗材]**
确认打印机纸张/碳带充足,标签尺寸与模板匹配。
***==确保4个设备连接正常绿色展示==***
### 8.2 登录(必须)
1. 点击左侧 **用户登录**
2. 输入:
- 确认者用户名
- 操作者用户名
- 密码(校验确认者账号)
3. 点击 **登录**
4. 登录成功后进入主界面状态栏显示“确认者用户xxx”。
![image-20260116145846017](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145846017.png)
### 8.3 选择原料(开始生产前)
1. 在主页面左上区域点击原料按钮。
![image-20260116145926213](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116145926213.png)
***注意:***
**==在拆包分小包之前要确认当前是哪种原料,您可以点击对应的添加剂进行选中操作==**
**==在你选中后一个原料后,系统会根据选中原料查询之前是否有大包未使用完毕的添加剂,如果有的话,直接在之前未使用完毕的原料上继续分拆操作,可以开始称重操作==**
**==否则您需要新填写一个原料信息,如下:==**
![image-20260116151640708](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116151640708.png)
**==上面信息填写完毕后,请点击【确认】按钮,代表一个新的原料开始要拆分使用了==**
**==如果当前原料还剩余很多但是因为一些其他的原因不能再使用了,需要使用另一包开始拆分使用,那么您可以点击【清零当前原料】清空当前原料信息,然后进行新原料的输入操作==**
2. 系统会检查该原料是否存在“分拆中”的记录:
- **存在**:自动加载该原料的入库重量、批号、保质期、剩余重量。
- **不存在**:视为新原料,需进行入库信息录入。
**重要规则**
- **同一原料在未分拆完成前,系统优先使用最新一条“分拆中”记录。**
### 8.4 原料入库建档(新原料/新批次)
适用场景:该原料首次使用,或上一批次已分拆完成,需要建立新的入库记录。
1. 在“入库信息”区域录入:
- **入库重量Kg**:必须为正数
- **批号**:不能为空、长度不超过 20
- **保质期(月)**:必须为正整数
2. 点击 **确认**
3. 保存成功后:
- 系统写入保存(状态为 Splitting/分拆中)
- 剩余重量初始化为入库重量
### 8.5 实时称重检查
- “实时重量”文本框会显示称重仪表的稳定重量值。
- 若实时重量长时间不更新:优先检查称重状态、网线、仪表 IP/端口配置是否正确。
### 8.6 称重打印(核心流程)
适用场景:每次从大包装分装到小包装/内袋时。
1. 确认已选择原料且已录入批号/保质期/原料编码。
2. 确认称重仪表显示稳定重量g
3. 点击 **打印**
4. 系统弹窗确认:选择 OK 执行Cancel 取消。
5. 打印成功后系统将自动:
- 生成内袋二维码(包含原料编码、批号、重量、保质期、产地编码、日产量序号等信息)
- 写入称重使用记录到服务器
- 更新该原料的剩余重量信息
**重量与单位说明**
- 实时称重 ``g
- 剩余重量 ``Kg
- 系统计算:`新剩余(Kg) = 旧剩余(Kg) - 称重(g)/1000`
### 8.7 剩余重量不足/分拆完成处理
==**打印完成后系统会做剩余重量检查:**==
- ==**当剩余重量非常接近 0 或小于本次称重折算值时:**==
- ==**系统会提示并将该原料记录置为“分拆完成”**==
- ==**清空当前选中原料与输入框**==
- ==**操作员应按现场实际将大包装清空并更换下一批/下一包**==
### 8.8 手动清零(异常处理/强制结束)
如果现场需要强制结束当前原料(例如称重异常、原料报废/更换、需要重新建档):
1. 点击 **清零当前的原料**
2. 系统将把当前原料使用状态更新为“分拆完成”。
3. 返回后可重新选择原料并重新建档。
![image-20260116154854918](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116154854918.png)
### 8.9 用户的管理(在工作站上操作)
因为当前是工控机是触控电脑没有鼠标不方便输入一些姓名和文字等信息所以把当前的确认者和操作者的一些增加、修改、删除等操作放到工作站上进行管理具体操作说明请见服务器侧【UserManual.pdf】
![image-20260116160606467](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116160606467.png)
## 9. 历史数据查询 SOP班组长/质检/追溯)
### 9.1 进入历史数据页面
点击左侧 **历史数据**
### 9.2 查询条件说明
- **开始时间/结束时间**按日期选择系统按整日00:00:00 至 23:59:59进行查询
- **原料名称**:下拉可选,也可手工输入;支持按原料名称或原料编码关键字模糊匹配
- **批号**:精确匹配(输入完整批号)
### 9.3 执行查询
![image-20260116160635748](C:\Users\chong\AppData\Roaming\Typora\typora-user-images\image-20260116160635748.png)
点击 **查询**,结果显示在表格中。
列头已转换为中文(如:原料编号、称重重量(g)、称重时间、操作员、确认者、出库时间等)。
## 10. 数据口径与数据库表说明IT/工程师追溯必读)
- **RawProInput原料入库/分拆主表)**
- 记录一包/一批原料的入库重量与剩余重量
- 状态Splitting分拆中/SplitComplete分拆完成
- **RawProUse称重使用明细**
- 每次打印产生一条记录
- 字段包括:原料信息、内袋二维码、称重重量、剩余重量、称重时间、操作员/确认者等
- **DayCount日计数**
- 用于生成二维码中的日产量序号
- **TbUser用户表**
- 登录认证来源(用户名/密码/权限等级)
## 11. 日志与问题排查IT 运维)
### 11.1 日志位置
- **错误日志**`程序目录/Log/日期/ErrorMessage.txt`
- **操作日志**`程序目录/OperationLogs/日期/OperationLog.txt`
### 11.2 常见异常与处理
- **[服务器异常]**
- 现象:状态栏“服务器异常”,或查询/保存提示失败
- 排查:检查 `App.config` 连接字符串、SQL Server 服务状态、网络连通性、防火墙
- **[称重未连接/不更新]**
- 现象:实时重量不刷新,状态栏显示未连接
- 排查:检查称重仪表 IP/端口、仪表是否开启 TCP Server、网络是否通、现场交换机端口是否隔离
- **[打印机离线/打印失败]**
- 现象:状态栏提示离线,或弹窗“打印失败”
- 排查:检查打印机 IP、网络、耗材、9100端口是否被策略阻断必要时用 Ping 验证可达
- **[重复启动失败]**
- 现象:提示“程序已运行,不能再次打开!”
- 处理:在任务管理器中结束旧进程或联系 IT 排查是否有后台残留
## 12. 维护与数据安全建议
- **[数据库备份]**
建议 SQL Server 设置每日自动备份,并保留至少 30 天。
- **[日志清理]**
NLog 默认保留 60 天,若磁盘压力大可调整 `maxArchiveDays`
- **[时间同步]**
生产追溯依赖时间准确性,建议电脑加入域或配置 NTP 同步。
- **[网络稳定]**
称重与打印均依赖局域网;建议独立 VLAN/交换机端口,避免办公网高峰抖动。
## 13. 常见问题FAQ
- **Q为什么重量显示有时不是两位小数**
- A称重来源可能为浮点值显示建议使用固定格式`00.00`)。存库建议按业务要求统一四舍五入后再保存。
- **Q打印出来二维码内容是什么**
- A二维码包含原料编码、批号、重量固定格式去掉小数点、保质期、产地编码与日产量序号用于后续追溯。
- **Q电脑如何开机**
- A**==工控机通电后自动开机==,关掉后可以断电,请不要在运行时直接断电,可能会导致工控机损坏**
## 14. 版本记录
- **V1.0**:初版 SOP安装/配置/登录/入库/称重打印/历史查询/日志排查)

Binary file not shown.

View File

@@ -83,6 +83,8 @@ namespace FATrace.App
label2 = new Label();
label1 = new Label();
tabPage3 = new TabPage();
txtOpName = new TextBox();
label28 = new Label();
btnLogin = new Button();
txtPassword = new TextBox();
txtCheckUserName = new TextBox();
@@ -103,8 +105,6 @@ namespace FATrace.App
dtpSearchStartTime = new DateTimePicker();
dataGridView1 = new DataGridView();
label22 = new Label();
txtOpName = new TextBox();
label28 = new Label();
statusStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
splitContainer1.Panel1.SuspendLayout();
@@ -245,6 +245,7 @@ namespace FATrace.App
label20.Size = new Size(74, 22);
label20.TabIndex = 6;
label20.Text = "系统配置";
label20.Visible = false;
//
// label19
//
@@ -266,6 +267,7 @@ namespace FATrace.App
btnSysConfig.Size = new Size(64, 62);
btnSysConfig.TabIndex = 4;
btnSysConfig.UseVisualStyleBackColor = true;
btnSysConfig.Visible = false;
btnSysConfig.Click += btnSysConfig_Click;
//
// bntFrmLogin
@@ -390,7 +392,6 @@ namespace FATrace.App
txtCode.ReadOnly = true;
txtCode.Size = new Size(410, 33);
txtCode.TabIndex = 16;
txtCode.Text = "YG05030013,202509231802,3,01,0001";
//
// label13
//
@@ -707,7 +708,7 @@ namespace FATrace.App
btnRawName1.Name = "btnRawName1";
btnRawName1.Size = new Size(266, 48);
btnRawName1.TabIndex = 1;
btnRawName1.Text = "DYG05030013 瑞士乳杆菌GCL1815";
btnRawName1.Text = "YG05030013 瑞士乳杆菌GCL1815";
btnRawName1.UseVisualStyleBackColor = true;
btnRawName1.Click += btnRawName1_Click;
//
@@ -752,6 +753,24 @@ namespace FATrace.App
tabPage3.TabIndex = 2;
tabPage3.UseVisualStyleBackColor = true;
//
// txtOpName
//
txtOpName.Font = new Font("微软雅黑", 16F);
txtOpName.Location = new Point(527, 218);
txtOpName.Name = "txtOpName";
txtOpName.Size = new Size(97, 36);
txtOpName.TabIndex = 9;
//
// label28
//
label28.AutoSize = true;
label28.Font = new Font("微软雅黑", 16F);
label28.Location = new Point(439, 219);
label28.Name = "label28";
label28.Size = new Size(84, 30);
label28.TabIndex = 8;
label28.Text = "操作者:";
//
// btnLogin
//
btnLogin.BackColor = Color.SandyBrown;
@@ -961,24 +980,6 @@ namespace FATrace.App
label22.Text = "历史数据";
label22.TextAlign = ContentAlignment.MiddleCenter;
//
// txtOpName
//
txtOpName.Font = new Font("微软雅黑", 16F);
txtOpName.Location = new Point(527, 218);
txtOpName.Name = "txtOpName";
txtOpName.Size = new Size(97, 36);
txtOpName.TabIndex = 9;
//
// label28
//
label28.AutoSize = true;
label28.Font = new Font("微软雅黑", 16F);
label28.Location = new Point(439, 219);
label28.Name = "label28";
label28.Size = new Size(84, 30);
label28.TabIndex = 8;
label28.Text = "操作者:";
//
// frmMain
//
AutoScaleDimensions = new SizeF(7F, 17F);

View File

@@ -227,9 +227,9 @@ namespace FATrace.App
{
new RawCtrInfo(){
RawName="瑞士乳杆菌GCL1815",
RawCode="DYG05030013",
RawCode="YG05030013",
BtnControlName="btnRawName1",
RawSource=RawSource.China
RawSource=RawSource.Japan
},
new RawCtrInfo(){
RawName="抗性糊精",
@@ -348,7 +348,7 @@ namespace FATrace.App
/// <summary>
/// 当前重量数据
/// </summary>
private double CurWeight { get; set; } = 80.8;
private double CurWeight { get; set; } = 0;
///// <summary>
///// 当前剩余数据
@@ -662,9 +662,27 @@ namespace FATrace.App
return;
}
//确认数据
// 显示消息框,并等待用户响应
DialogResult result = MessageBox.Show("确定要【打印】操作吗?", "确认操作", MessageBoxButtons.OKCancel);
if (result==DialogResult.Cancel)
{
return;
}
if (CurWeight<2.0)
{
//确认数据
// 显示消息框,并等待用户响应
DialogResult resultWeightCheck = MessageBox.Show("检测到当前的重量小于2g确定要【打印】操作吗", "确认操作", MessageBoxButtons.OKCancel);
if (resultWeightCheck == DialogResult.Cancel)
{
return;
}
}
//新的剩余重量 Kg
var NewRemainWeight = CurSelectedRawProInput.RemainWeight - CurWeight / 1000;
var NewRemainWeight = CurSelectedRawProInput.RemainWeight - CurWeight / 1000.0;
//当前产品的剩余重量
txtRemainWeight.Text = NewRemainWeight.ToString();
@@ -767,6 +785,16 @@ namespace FATrace.App
}
}
/// <summary>
/// 格式化重量显示整数部分至少2位不足补0小数部分固定2位
/// </summary>
/// <param name="weight">原始重量值</param>
/// <returns>格式化后的重量字符串09.50, 81.10, 100.00</returns>
private string FormatWeight(double weight)
{
return weight.ToString("00.00");
}
/// <summary>
/// 获取当前的日产量信息
/// </summary>
@@ -849,7 +877,7 @@ namespace FATrace.App
Code.Append(',');
Code.Append(Batch);
Code.Append(',');
Code.Append(Weight.ToString().Replace(".", ""));
Code.Append(FormatWeight(Weight).ToString().Replace(".", ""));
Code.Append(',');
Code.Append(ShelfLife.ToString());
Code.Append(',');
@@ -950,17 +978,20 @@ namespace FATrace.App
return;
}
var ListUser = FSqlContext.FDb.Select<TbUser>().Where(a => a.UserName == txtCheckUserName.Text.Trim()).ToList();
var ListUser = FSqlContext.FDb.Select<TbWeightUser>()
.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(), ListUser.FirstOrDefault().AccessLevel);
Main_PopUserNameEvent(txtCheckUserName.Text.Trim(), txtOpName.Text.Trim(), "称重用户");
this.TabControlMain.SelectedIndex = 0;
txtCheckUserName.Text = "";
txtOpName.Text = "";
txtPassword.Text = "";
//PopUserNameEvent(txtUserName.Text.Trim());
//this.Close();