19 KiB
发那科采集数据结构变更 - 系统调整方案
创建时间:2026-05-11 触发原因:发那科系统采集示例文件(根目录
发那科系统采集示例.txt)更新,实际FANUC采集数据结构与系统原有设计存在差异 涉及模块:CncSimulator模拟器、数据库设计、采集服务设计、后台管理功能
一、实际FANUC数据结构(2026-05-11 采自生产环境)
1.1 顶层结构
[
{
"device": "fanake-1.2_1.2", // 设备编码,匹配 cnc_machine.device_code
"desc": "西-1.2", // 设备描述
"tags": [ ... ] // 10个Tag
},
...
]
共32台设备,每台10个Tag。其中25台在线、7台离线。
1.2 Tag清单(10个)
| # | Tag ID | desc(中文) | 类型 | 示例值 | 备注 |
|---|---|---|---|---|---|
| 1 | _io_status |
设备状态 | 数值 | 0.00000 / 1.00000 | 0=离线, 1=在线;始终有效 |
| 2 | Tag5 |
执行的NC主程序名 | 字符串 | O1, O9001, 1370.NC, 7, 1027, O2, 037.NC | 可为空 |
| 3 | Tag6 |
执行的NC主程序号 | 字符串 | N0, N1, N9, N20 | |
| 4 | Tag7 |
当前加工程序内容 | 字符串 | G代码片段 | 可为空 |
| 5 | Tag8 |
当前加工零件数 | 数值 | 35.00000 | 当前程序的零件计数 |
| 6 | Tag9 |
运行状态 | 数值 | 0.00000 / 1.00000 / 3.00000 | 0=待机, 1=运行, 3=加工中 |
| 7 | Tag11 |
操作模式 | 数值 | 1 / 4 / 5 / 10 | 1=MEM, 4=?, 5=?, 10=JOG |
| 8 | Tag22 |
开机时间 | 数值 | 45129840.00000 | 秒,历史累计 |
| 9 | Tag23 |
运行时间 | 数值 | 12201.00000 | 秒,历史累计 |
| 10 | Tag1 |
加工零件总数 | 数值 | 45930.00000 | 历史终身累计,新增字段 |
1.3 数据有效性规则
规则1:Quality字段
| quality值 | 含义 | 处理方式 |
|---|---|---|
"0" |
数据有效 | 正常解析 |
"1" |
数据无效(采集失败/设备离线) | 忽略该tag的value |
规则2:Time字段(采集失败标记)
| time值 | 含义 | 处理方式 |
|---|---|---|
1970-01-01 08:00:00 |
采集失败 | 忽略该tag的value |
| 其他正常时间 | 数据时间戳 | 正常使用 |
规则3:_io_status 特殊处理
_io_status tag 不受规则1和规则2约束,始终读取value判断设备在线/离线:
- 即使设备离线,
_io_status的 quality=0、time=真实时间、value=0
规则4:Time与Value独立变化
每个tag的 time 和 value 更新频率不固定:
- time变了、value没变
- value变了、time没变
- 都变了
- 都没变
采集服务的变更检测必须对比value值,不能依赖time变化作为变更信号。
1.4 离线设备特征
以 fanake-1.10 为例:
_io_status: quality=0, value=0.00000, time=2026-05-11 17:06:34 ← 始终有效
Tag5: quality=1, value="", time=1970-01-01 08:00:00 ← 无效
Tag6: quality=1, value="", time=1970-01-01 08:00:00 ← 无效
Tag7: quality=1, value="", time=1970-01-01 08:00:00 ← 无效
Tag8: quality=1, value=0.00000, time=1970-01-01 08:00:00 ← 无效
Tag9: quality=1, value=0.00000, time=1970-01-01 08:00:00 ← 无效
Tag11: quality=1, value=0.00000, time=1970-01-01 08:00:00 ← 无效
Tag22: quality=1, value=0.00000, time=1970-01-01 08:00:00 ← 无效
Tag23: quality=1, value=0.00000, time=1970-01-01 08:00:00 ← 无效
Tag1: quality=1, value=0.00000, time=1970-01-01 08:00:00 ← 无效
二、与现有设计的差异对比
2.1 模拟器(FanucDataGenerator)差异
| 差异项 | 现有模拟器(18个Tag) | 实际FANUC(10个Tag) | 影响 |
|---|---|---|---|
| Tag2 (当前轴数) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag14 (主轴倍率) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag17 (主轴设定速度) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag18 (进给设定速度) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag19 (主轴实际速度) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag20 (进给实际转速) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag21 (主轴负载) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag24 (切削时间) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag25 (循环时间) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag26 (加工状态) | ✅ 有 | ❌ 不存在 | 模拟器多余 |
| Tag1 (加工零件总数) | ❌ 缺少 | ✅ 有 | 模拟器缺少,需新增 |
| Tag6 desc | "执行的NC主程序号" | "执行的NC主程序号" | 一致 |
2.2 数据库设计差异
| 差异项 | 现有设计 | 实际需求 |
|---|---|---|
| 标准字段 total_part_count | ❌ 无 | ✅ 需要新增,对应Tag1 |
| cnc_collect_record.total_part_count | ❌ 无 | ✅ 需要新增列 |
| cnc_machine.last_total_part_count | ❌ 无 | ✅ 需要新增列 |
2.3 采集服务设计差异
| 差异项 | 现有设计假设 | 实际行为 |
|---|---|---|
| 数据有效性判断 | 未明确quality处理 | 需按quality+time双规则过滤 |
| _io_status处理 | 未明确特殊性 | 始终有效,不受quality/time约束 |
| 变更检测 | 未明确time/value独立性 | 必须对比value,不看time |
| Tag1(总零件数) | 无此字段 | 需解析并存储 |
2.4 后台管理差异
| 差异项 | 现有设计 | 实际需求 |
|---|---|---|
| 速度/倍率/负载字段 | 设计了展示 | FANUC设备这些字段为NULL,需处理空值 |
| 加工零件总数 | 未设计展示 | 可选展示 |
| 操作模式枚举 | 仅定义1=MEM, 10=JOG | 实际还有4、5值,需补充定义 |
三、调整方案
3.1 CncSimulator 模拟器调整(优先级:高)
目标:让模拟器生成的数据结构与实际FANUC完全一致
3.1.1 DeviceState.cs 修改
public class DeviceState
{
// ===== 固定信息(来自配置) =====
public string DeviceCode { get; set; }
public string Desc { get; set; }
// ===== 动态状态 =====
public string CurrentScenario { get; set; } = "idle";
public bool IsOnline { get; set; } = true;
public string ProgramName { get; set; } = "O0001";
public int PartCount { get; set; } = 0;
+ /// <summary>历史终身累计零件总数(对应Tag1)</summary>
+ public decimal TotalPartCount { get; set; } = 0;
public int DeviceStatus { get; set; } = 1;
public int RunStatus { get; set; } = 0;
public int OperateMode { get; set; } = 1;
- public decimal SpindleSpeedSet { get; set; } = 450;
- public decimal FeedSpeedSet { get; set; } = 60;
- public decimal SpindleSpeedActual { get; set; } = 0;
- public decimal FeedSpeedActual { get; set; } = 0;
- public decimal SpindleLoad { get; set; } = 0;
- public decimal SpindleOverride { get; set; } = 100;
public decimal PowerOnTime { get; set; } = 0;
public decimal RunTime { get; set; } = 0;
- public decimal CuttingTime { get; set; } = 0;
- public decimal CycleTime { get; set; } = 0;
- public string MachiningStatus { get; set; } = "";
public string ProgramContent { get; set; } = "";
// ... 其余字段保留不变
}
说明:移除实际FANUC不存在的字段(SpindleSpeedSet、FeedSpeedSet、SpindleSpeedActual、FeedSpeedActual、SpindleLoad、SpindleOverride、CuttingTime、CycleTime、MachiningStatus),新增TotalPartCount。
3.1.2 FanucDataGenerator.cs 修改
private JArray GenerateTags(DeviceState state)
{
var tags = new JArray();
DateTime baseTime = DateTime.Now;
// 每个tag的时间基准上随机偏移 -5~0 秒
DateTime TagTime()
{
return baseTime.AddSeconds(-_rng.Next(0, 6));
}
void AddNumericTag(string id, string desc, decimal value) { /* 不变 */ }
void AddStringTag(string id, string desc, string value) { /* 不变 */ }
- // 18个tag → 10个tag,与实际FANUC完全一致
AddNumericTag("_io_status", "设备状态", state.DeviceStatus);
- AddNumericTag("Tag2", "当前轴数", 4);
+ AddNumericTag("Tag1", "加工零件总数", state.TotalPartCount);
AddStringTag("Tag5", "执行的NC主程序名", state.ProgramName);
AddStringTag("Tag6", "执行的NC主程序号", "N0");
AddStringTag("Tag7", "当前加工程序内容", /* ... */);
AddNumericTag("Tag8", "当前加工零件数", state.PartCount);
AddNumericTag("Tag9", "运行状态", state.RunStatus);
AddNumericTag("Tag11", "操作模式", state.OperateMode);
- AddNumericTag("Tag14", "当前主轴倍率", state.SpindleOverride);
- AddNumericTag("Tag17", "主轴设定速度", state.SpindleSpeedSet);
- AddNumericTag("Tag18", "进给设定速度", state.FeedSpeedSet);
- AddNumericTag("Tag19", "主轴实际速度", state.SpindleSpeedActual);
- AddNumericTag("Tag20", "进给实际转速", state.FeedSpeedActual);
- AddNumericTag("Tag21", "主轴负载", state.SpindleLoad);
AddNumericTag("Tag22", "开机时间", state.PowerOnTime);
AddNumericTag("Tag23", "运行时间", state.RunTime);
- AddNumericTag("Tag24", "切削时间", state.CuttingTime);
- AddNumericTag("Tag25", "循环时间", state.CycleTime);
- AddStringTag("Tag26", "加工状态", state.MachiningStatus);
return tags;
}
3.1.3 DeviceSimulator.cs 修改
- 构造函数:新增
TotalPartCount初始值配置(从simulator.json读取) machining场景:TotalPartCount++ApplyProgramChange():TotalPartCount 不清零(终身累计)ApplyManualReset():TotalPartCount 不清零ApplyPowerOn():TotalPartCount 不清零- 移除所有已删除字段(SpindleSpeed*、SpindleLoad、SpindleOverride、CuttingTime、CycleTime、MachiningStatus)的模拟逻辑
3.1.4 simulator.json 修改
devices配置中新增 initialTotalPartCount 字段:
{
"deviceCode": "CNC-A001",
"desc": "西栋1号",
"initialProgram": "O0001",
"initialPartCount": 50,
"initialTotalPartCount": 45000
}
3.1.5 模拟器离线行为
当设备断电(power_off)时,需要生成与实际一致的离线数据:
_io_status:quality=0, value=0, time=当前时间- 其他所有tag:quality=1, value="" 或 "0.00000", time="1970-01-01 08:00:00"
3.2 数据库设计调整(优先级:高)
3.2.1 cnc_brand_field_mapping 标准字段约定
在 docs/01-数据库设计.md 的标准字段约定表中新增:
| standard_field | 含义 | data_type |
|---|---|---|
| total_part_count | 历史累计加工零件总数 | number |
3.2.2 cnc_collect_record 表
新增列:
ALTER TABLE cnc_collect_record
ADD COLUMN total_part_count DECIMAL(15,5) NULL COMMENT '历史累计加工零件总数'
AFTER part_count;
完整DDL变更位置:在 part_count 列之后增加 total_part_count 列。
3.2.3 cnc_machine 表
新增列:
ALTER TABLE cnc_machine
ADD COLUMN last_total_part_count DECIMAL(15,5) NULL COMMENT '最新历史累计加工零件总数'
AFTER last_part_count;
3.2.4 FANUC品牌预置映射数据
在 database/ 预置数据脚本中,FANUC品牌的 field_mapping 新增一行:
| brand_id | standard_field | field_name | match_by | data_type | is_required |
|---|---|---|---|---|---|
| (FANUC) | total_part_count | Tag1 | id | number | 0 |
同时,以下标准字段在实际FANUC中不存在,映射记录应标记 is_required=0 或不创建映射(由采集服务按实际数据决定):
- spindle_speed_set → 无对应Tag
- feed_speed_set → 无对应Tag
- spindle_speed_actual → 无对应Tag
- feed_speed_actual → 无对应Tag
- spindle_load → 无对应Tag
- spindle_override → 无对应Tag
- cutting_time → 无对应Tag
- cycle_time → 无对应Tag
- machining_status → 无对应Tag
3.3 采集服务设计调整(优先级:高)
3.3.1 采集循环核心逻辑(伪代码)
每次采集循环(按采集地址配置的间隔):
1. Ping目标地址
- Ping失败 → 更新 cnc_collect_address.fail_count++
- 连续5次失败 → 写告警
- 跳过本次采集
2. HTTP GET 拉取JSON数组
3. 原始JSON存日志库(cnc_raw_log,每次都存,不管有没有变化)
4. 逐设备处理JSON数组中的每个元素:
4.1 用 device 值匹配 cnc_machine.device_code
- 未匹配到 → 记录告警"发现未知设备",跳过
4.2 解析 _io_status(特殊处理):
→ 直接读取 value,不管 quality 和 time
→ value=1 → 设备在线
→ value=0 → 设备离线
4.3 解析其他tag(标准处理):
for each tag in tags (排除 _io_status):
if tag.quality != "0" → 跳过(数据无效)
if tag.time 以 "1970-01-01" 开头 → 跳过(采集失败)
否则 → 按 brand_field_mapping 映射为标准字段,解析 value
4.4 写入结构化采集记录:
→ INSERT INTO cnc_collect_record (每台设备每次采集一条)
→ total_part_count 列写入 Tag1 的值(如果有效)
→ 速度/倍率/负载等字段写 NULL(FANUC无此数据)
4.5 业务变更检测(只对比 value,不看 time):
a. NC程序名变更检测:
if 新Tag5.value != 旧 cnc_machine.last_program_name:
→ 结账当前产量段 (close_reason='program_change')
→ 开新产量段
b. 零件数下降检测(手动清零):
if 新Tag8.value < 旧 cnc_machine.last_part_count
AND 新Tag5.value == 旧 cnc_machine.last_program_name:
→ 结账当前产量段 (close_reason='manual_reset')
→ 开新产量段,part_count 从新值开始
c. 运行状态变更:
if 新Tag9.value != 旧 cnc_machine.last_run_status:
→ 仅更新实时状态,不触发产量段逻辑
d. 总零件数:仅记录,不触发任何业务逻辑
4.6 更新机床实时状态(cnc_machine 的 last_* 字段):
→ last_program_name = Tag5.value
→ last_part_count = Tag8.value
→ last_total_part_count = Tag1.value
→ last_run_status = Tag9.value
→ last_operate_mode = Tag11.value
→ last_device_status = _io_status.value
→ is_online = (_io_status.value == 1)
→ last_collect_time = 当前服务器时间
5. 更新采集地址状态:
→ last_collect_time = 当前时间
→ last_collect_status = 'success'
→ fail_count = 0
3.3.2 数据有效性判断流程图
收到一个tag
│
├── tag.id == "_io_status" ?
│ ├── YES → 始终有效,直接读 value
│ └── NO → 进入标准判断 ↓
│
├── tag.quality != "0" ?
│ ├── YES → 无效,跳过
│ └── NO → 继续判断 ↓
│
└── tag.time 以 "1970-01-01" 开头 ?
├── YES → 无效,跳过
└── NO → 有效,解析 tag.value
3.4 后台管理功能调整(优先级:中)
3.4.1 设备状态展示
cnc_machine 表已有 last_device_status、last_run_status 等字段,展示逻辑不变。新增:
- 设备详情中可选展示"累计加工总数"(来自
last_total_part_count)
3.4.2 大屏看板空值处理
FANUC设备的以下字段恒为NULL:
- 主轴设定速度、进给设定速度
- 主轴实际速度、进给实际速度
- 主轴负载、主轴倍率
- 切削时间、循环时间
- 加工状态
大屏如果展示这些指标:
- 数值型 → 显示
--或N/A - 图表型 → 该FANUC设备的线不画
3.4.3 品牌字段映射管理
映射配置页面需支持新增的 total_part_count 标准字段。FANUC品牌预置映射中需包含 Tag1 → total_part_count。
3.4.4 前端Mock数据同步
前端Mock数据中与FANUC相关的设备状态Mock,应同步为10个Tag的字段集(移除速度/负载等,新增total_part_count)。
3.5 设计文档更新(优先级:中)
| 文档 | 更新内容 |
|---|---|
docs/01-数据库设计.md |
标准字段约定表增加 total_part_count;cnc_collect_record 和 cnc_machine 的DDL增加新列 |
docs/00-需求与设计文档.md |
4.2数据格式章节补充quality/1970-time/Tag1说明 |
docs/03-API接口设计.md |
如果有涉及采集记录字段的API响应格式,需同步增加 total_part_count |
database/ |
ALTER TABLE脚本 + FANUC预置映射数据新增 |
四、不需要调整的部分
| 项目 | 原因 |
|---|---|
顶层JSON结构 [{device, desc, tags}] |
实际数据与设计一致 |
| device字段匹配机床逻辑 | 设计正确,device值匹配device_code |
| 零件分段计数核心逻辑(基于Tag8) | 不受影响,仍基于NC程序名切换+清零 |
| 产量分段记录表 cnc_production_segment | 结构不变,total_part_count不影响分段计算 |
| 采集地址/品牌管理配置架构 | 不变 |
| 双库分离设计(原始JSON存日志库) | 不变 |
| 采集间隔配置(地址级统一间隔) | 不变 |
| 日终汇总逻辑 | 不变 |
五、执行顺序
阶段1:模拟器适配(让测试数据与实际一致)
├── 1.1 DeviceState.cs 新增 TotalPartCount,移除多余字段
├── 1.2 FanucDataGenerator.cs 改为10个Tag
├── 1.3 DeviceSimulator.cs 调整场景逻辑
├── 1.4 simulator.json 新增 initialTotalPartCount
└── 1.5 编译验证
阶段2:数据库变更(数据基础)
├── 2.1 cnc_collect_record 增加 total_part_count 列
├── 2.2 cnc_machine 增加 last_total_part_count 列
├── 2.3 标准字段约定表增加 total_part_count
├── 2.4 FANUC预置映射数据增加 Tag1 映射
└── 2.5 设计文档同步更新
阶段3:采集服务设计定稿(核心逻辑)
├── 3.1 编写采集服务数据解析逻辑(含quality/time过滤)
├── 3.2 编写变更检测逻辑(只看value不看time)
└── 3.3 编写Tag1存储逻辑(total_part_count仅记录不触发业务)
阶段4:后台管理适配(展示层)
├── 4.1 大屏空值处理(FANUC设备速度/负载为NULL时显示N/A)
├── 4.2 前端Mock数据同步为10个Tag结构
└── 4.3 品牌映射管理支持total_part_count字段
六、补充说明
6.1 操作模式枚举值
实际数据中出现了以下操作模式值,具体含义需与现场确认:
| 值 | 已知含义 | 出现设备 |
|---|---|---|
| 1 | MEM(内存运行) | 加工中的设备 |
| 4 | 待确认 | fanake-1.19(运行状态=1) |
| 5 | 待确认 | fanake-1.24(运行状态=0) |
| 10 | JOG(手动) | 待机中的设备 |
6.2 device字段格式
实际数据格式为 fanake-1.2_1.2(品牌-编号_编号),机床表的 device_code 需按此格式录入。
6.3 数值精度
所有数值型Tag的value都带5位小数(如 35.00000),采集服务解析时需 Convert.ToDecimal 后存储到 DECIMAL(15,5) 列。
6.4 time字段格式
统一格式 yyyy-MM-dd HH:mm:ss,需要按此格式解析。设备离线时为 1970-01-01 08:00:00(UTC+8时区下的epoch时间)。