From 54910626b11f84ab8e93bdfb07f685ff1dd84a5b Mon Sep 17 00:00:00 2001 From: haoliang <821644@qq.com> Date: Tue, 12 May 2026 16:17:15 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=87=E9=9B=86=E6=9C=8D=E5=8A=A1=E4=BA=A7?= =?UTF-8?q?=E9=87=8F=E7=BB=9F=E8=AE=A1=E5=85=A8=E6=B5=81=E7=A8=8B=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=AA=8C=E8=AF=81=EF=BC=9ADB=20Schema=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E3=80=81=E5=93=81=E7=89=8C=E6=98=A0=E5=B0=84=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E3=80=81=E9=87=87=E9=9B=86/=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E5=99=A8=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9=E3=80=8110?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E6=B5=8B=E8=AF=95=E5=85=A8=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/01-init-schema.sql | 2 + database/02-init-data.sql | 26 +- docs/00-需求与设计文档.md | 3 + docs/01-数据库设计.md | 3 + docs/07-发那科数据结构变更调整方案.md | 508 ++++++++++++++++++ frontend/mock/brand.ts | 29 +- frontend/mock/machine.ts | 4 +- frontend/mock/simulator.ts | 8 +- frontend/src/api/simulator.ts | 7 +- frontend/src/types/index.ts | 22 +- .../src/views/machine/MachineDetailPage.vue | 5 +- simulator.json | 10 +- src/CncCollector/Core/CollectRecordWriter.cs | 27 +- src/CncCollector/Core/CollectWorker.cs | 24 +- src/CncCollector/Core/DataParser.cs | 36 +- src/CncModels/Entity/CollectRecord.cs | 3 + src/CncSimulator/Config/SimulatorConfig.cs | 4 + src/CncSimulator/Core/SimulatorEngine.cs | 61 +++ src/CncSimulator/Core/SimulatorServer.cs | 65 ++- src/CncSimulator/Device/DeviceSimulator.cs | 49 +- src/CncSimulator/Device/DeviceState.cs | 30 +- .../Generator/FanucDataGenerator.cs | 16 +- src/CncWebApi/Global.asax.cs | 8 +- src/CncWebApi/Web.config | 34 ++ 发那科系统采集示例.txt | 2 +- 25 files changed, 789 insertions(+), 197 deletions(-) create mode 100644 docs/07-发那科数据结构变更调整方案.md diff --git a/database/01-init-schema.sql b/database/01-init-schema.sql index 978c604..c3e29c4 100644 --- a/database/01-init-schema.sql +++ b/database/01-init-schema.sql @@ -83,6 +83,7 @@ CREATE TABLE IF NOT EXISTS cnc_machine ( last_run_status VARCHAR(20) NULL, last_program_name VARCHAR(200) NULL, last_part_count DECIMAL(15,5) NULL, + last_total_part_count DECIMAL(15,5) NULL, last_operate_mode VARCHAR(20) NULL, last_machining_status VARCHAR(20) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -128,6 +129,7 @@ CREATE TABLE IF NOT EXISTS cnc_collect_record ( device_time DATETIME NULL, program_name VARCHAR(200) NULL, part_count DECIMAL(15,5) NULL, + total_part_count DECIMAL(15,5) NULL, device_status VARCHAR(20) NULL, run_status VARCHAR(20) NULL, operate_mode VARCHAR(20) NULL, diff --git a/database/02-init-data.sql b/database/02-init-data.sql index f58b0d3..aef1fca 100644 --- a/database/02-init-data.sql +++ b/database/02-init-data.sql @@ -15,26 +15,18 @@ INSERT IGNORE INTO cnc_workshop (name, sort_order, is_enabled) VALUES INSERT IGNORE INTO cnc_brand (brand_name, device_field, tags_path, is_enabled) VALUES ('FANUC', 'device', 'tags', 1); --- FANUC字段映射预置 +-- FANUC字段映射预置(field_name = 实际FANUC采集JSON中的tag id) SET @fanuc_brand_id = (SELECT id FROM cnc_brand WHERE brand_name = 'FANUC'); INSERT IGNORE INTO cnc_brand_field_mapping (brand_id, standard_field, field_name, match_by, data_type, is_required) VALUES -(@fanuc_brand_id, 'program_name', 'program_name', 'id', 'string', 1), -(@fanuc_brand_id, 'part_count', 'part_count', 'id', 'number', 1), -(@fanuc_brand_id, 'device_status', 'device_status', 'id', 'number', 0), -(@fanuc_brand_id, 'run_status', 'run_status', 'id', 'number', 0), -(@fanuc_brand_id, 'operate_mode', 'operate_mode', 'id', 'number', 0), -(@fanuc_brand_id, 'spindle_speed_set', 'spindle_speed_set', 'id', 'number', 0), -(@fanuc_brand_id, 'feed_speed_set', 'feed_speed_set', 'id', 'number', 0), -(@fanuc_brand_id, 'spindle_speed_actual', 'spindle_speed_actual', 'id', 'number', 0), -(@fanuc_brand_id, 'feed_speed_actual', 'feed_speed_actual', 'id', 'number', 0), -(@fanuc_brand_id, 'spindle_load', 'spindle_load', 'id', 'number', 0), -(@fanuc_brand_id, 'spindle_override', 'spindle_override', 'id', 'number', 0), -(@fanuc_brand_id, 'power_on_time', 'power_on_time', 'id', 'number', 0), -(@fanuc_brand_id, 'run_time', 'run_time', 'id', 'number', 0), -(@fanuc_brand_id, 'cutting_time', 'cutting_time', 'id', 'number', 0), -(@fanuc_brand_id, 'cycle_time', 'cycle_time', 'id', 'number', 0), -(@fanuc_brand_id, 'machining_status', 'machining_status', 'id', 'string', 0); +(@fanuc_brand_id, 'program_name', 'Tag5', 'id', 'string', 1), +(@fanuc_brand_id, 'part_count', 'Tag8', 'id', 'number', 1), +(@fanuc_brand_id, 'total_part_count', 'Tag1', 'id', 'number', 0), +(@fanuc_brand_id, 'device_status', '_io_status', 'id', 'number', 0), +(@fanuc_brand_id, 'run_status', 'Tag9', 'id', 'number', 0), +(@fanuc_brand_id, 'operate_mode', 'Tag11', 'id', 'number', 0), +(@fanuc_brand_id, 'power_on_time', 'Tag22', 'id', 'number', 0), +(@fanuc_brand_id, 'run_time', 'Tag23', 'id', 'number', 0); -- 系统配置预置 INSERT IGNORE INTO cnc_sys_config (config_key, config_value, value_type, description) VALUES diff --git a/docs/00-需求与设计文档.md b/docs/00-需求与设计文档.md index d966815..dea637e 100644 --- a/docs/00-需求与设计文档.md +++ b/docs/00-需求与设计文档.md @@ -63,6 +63,9 @@ | 原始数据 | 存原始JSON到日志库,解析后结构化数据存业务库 | | 时间标准 | 以服务器时间为准,机床时间仅存为参考字段 | | 数值处理 | 自动去除.00000尾缀,转换为数值/字符串 | +| 数据有效性 | quality≠0的tag无效;time以1970-01-01开头的tag无效(采集失败);_io_status始终有效不受quality/time约束 | +| 总零件数 | Tag1(加工零件总数)为历史终身累计值,与Tag8(当前程序零件数)不同维度 | +| 变更检测 | 采集服务对比tag的value值判断变化,不依赖time字段(time和value独立变化) | ### 4.3 零件产量统计 diff --git a/docs/01-数据库设计.md b/docs/01-数据库设计.md index d15a363..72d3767 100644 --- a/docs/01-数据库设计.md +++ b/docs/01-数据库设计.md @@ -88,6 +88,7 @@ CREATE TABLE cnc_brand_field_mapping ( | cutting_time | 切削时间 | number | | cycle_time | 循环时间 | number | | machining_status | 加工状态 | string | +| total_part_count | 历史累计加工零件总数 | number | 索引:uk_brand_standard唯一约束同时作为索引,保证同一品牌下标准字段不重复,采集时按brand_id查询所有映射规则。 @@ -137,6 +138,7 @@ CREATE TABLE cnc_machine ( last_run_status VARCHAR(20) NULL, last_program_name VARCHAR(200) NULL, last_part_count DECIMAL(15,5) NULL, + last_total_part_count DECIMAL(15,5) NULL, last_operate_mode VARCHAR(20) NULL, last_machining_status VARCHAR(20) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -208,6 +210,7 @@ CREATE TABLE cnc_collect_record ( device_time DATETIME NULL, program_name VARCHAR(200) NULL, part_count DECIMAL(15,5) NULL, + total_part_count DECIMAL(15,5) NULL, device_status VARCHAR(20) NULL, run_status VARCHAR(20) NULL, operate_mode VARCHAR(20) NULL, diff --git a/docs/07-发那科数据结构变更调整方案.md b/docs/07-发那科数据结构变更调整方案.md new file mode 100644 index 0000000..a43564d --- /dev/null +++ b/docs/07-发那科数据结构变更调整方案.md @@ -0,0 +1,508 @@ +# 发那科采集数据结构变更 - 系统调整方案 + +> 创建时间:2026-05-11 +> 触发原因:发那科系统采集示例文件(根目录 `发那科系统采集示例.txt`)更新,实际FANUC采集数据结构与系统原有设计存在差异 +> 涉及模块:CncSimulator模拟器、数据库设计、采集服务设计、后台管理功能 + +--- + +## 一、实际FANUC数据结构(2026-05-11 采自生产环境) + +### 1.1 顶层结构 + +```json +[ + { + "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 修改 + +```diff + 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; ++ /// 历史终身累计零件总数(对应Tag1) ++ 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 修改 + +```diff + 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` 字段: + +```json +{ + "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 表 + +新增列: + +```sql +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 表 + +新增列: + +```sql +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时间)。 diff --git a/frontend/mock/brand.ts b/frontend/mock/brand.ts index 70b7027..3df9cb7 100644 --- a/frontend/mock/brand.ts +++ b/frontend/mock/brand.ts @@ -1,35 +1,26 @@ import type { MockMethod } from './types' const brands = [ - { id: 1, brandName: 'FANUC', deviceField: 'device', tagsPath: 'tags', isEnabled: 1, fieldCount: 16 }, + { id: 1, brandName: 'FANUC', deviceField: 'device', tagsPath: 'tags', isEnabled: 1, fieldCount: 8 }, { id: 2, brandName: 'SIEMENS', deviceField: 'device', tagsPath: 'tags', isEnabled: 1, fieldCount: 12 }, { id: 3, brandName: 'MITSUBISHI', deviceField: 'device', tagsPath: 'tags', isEnabled: 0, fieldCount: 8 }, ] const standardFields = [ - 'program_name', 'part_count', 'device_status', 'run_status', 'operate_mode', - 'spindle_speed_set', 'feed_speed_set', 'spindle_speed_actual', 'feed_speed_actual', - 'spindle_load', 'spindle_override', 'power_on_time', 'run_time', 'cutting_time', - 'cycle_time', 'machining_status', + 'program_name', 'part_count', 'total_part_count', 'device_status', 'run_status', 'operate_mode', + 'power_on_time', 'run_time', ] +// FANUC字段映射(与实际FANUC采集JSON中的tag id一致) const fanucMappings = [ { id: 1, standardField: 'program_name', fieldName: 'Tag5', matchBy: 'id', dataType: 'string', isRequired: 1 }, { id: 2, standardField: 'part_count', fieldName: 'Tag8', matchBy: 'id', dataType: 'number', isRequired: 1 }, - { id: 3, standardField: 'device_status', fieldName: '_io_status', matchBy: 'id', dataType: 'number', isRequired: 1 }, - { id: 4, standardField: 'run_status', fieldName: 'Tag9', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 5, standardField: 'operate_mode', fieldName: 'Tag10', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 6, standardField: 'spindle_speed_set', fieldName: 'Tag11', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 7, standardField: 'feed_speed_set', fieldName: 'Tag12', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 8, standardField: 'spindle_speed_actual', fieldName: 'Tag13', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 9, standardField: 'feed_speed_actual', fieldName: 'Tag14', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 10, standardField: 'spindle_load', fieldName: 'Tag15', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 11, standardField: 'spindle_override', fieldName: 'Tag16', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 12, standardField: 'power_on_time', fieldName: 'Tag17', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 13, standardField: 'run_time', fieldName: 'Tag18', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 14, standardField: 'cutting_time', fieldName: 'Tag19', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 15, standardField: 'cycle_time', fieldName: 'Tag20', matchBy: 'id', dataType: 'number', isRequired: 0 }, - { id: 16, standardField: 'machining_status', fieldName: 'Tag21', matchBy: 'id', dataType: 'string', isRequired: 0 }, + { id: 3, standardField: 'total_part_count', fieldName: 'Tag1', matchBy: 'id', dataType: 'number', isRequired: 0 }, + { id: 4, standardField: 'device_status', fieldName: '_io_status', matchBy: 'id', dataType: 'number', isRequired: 1 }, + { id: 5, standardField: 'run_status', fieldName: 'Tag9', matchBy: 'id', dataType: 'number', isRequired: 0 }, + { id: 6, standardField: 'operate_mode', fieldName: 'Tag11', matchBy: 'id', dataType: 'number', isRequired: 0 }, + { id: 7, standardField: 'power_on_time', fieldName: 'Tag22', matchBy: 'id', dataType: 'number', isRequired: 0 }, + { id: 8, standardField: 'run_time', fieldName: 'Tag23', matchBy: 'id', dataType: 'number', isRequired: 0 }, ] const mock: MockMethod[] = [ diff --git a/frontend/mock/machine.ts b/frontend/mock/machine.ts index d0afb7a..2c752f6 100644 --- a/frontend/mock/machine.ts +++ b/frontend/mock/machine.ts @@ -59,7 +59,7 @@ const mock: MockMethod[] = [ method: 'get', response: () => ({ code: 0, - data: { programName: '1566.NC', partCount: 580, runStatus: '运行中', operateMode: '自动', spindleSpeedSet: 3000, feedSpeedSet: 500, spindleSpeedActual: 2980, feedSpeedActual: 480, spindleLoad: 65, machiningStatus: 'G01', lastCollectTime: '2026-04-25T17:36:38' }, + data: { programName: '1566.NC', partCount: 580, totalPartCount: 88624, runStatus: '运行中', operateMode: '自动', lastCollectTime: '2026-04-25T17:36:38' }, }), }, { @@ -68,7 +68,7 @@ const mock: MockMethod[] = [ method: 'get', response: () => ({ code: 0, - data: { programName: '1566.NC', partCount: 580, runStatus: '运行中', operateMode: '自动', spindleSpeedSet: 3000, feedSpeedSet: 500, spindleSpeedActual: 2980, feedSpeedActual: 480, spindleLoad: 65, machiningStatus: 'G01', lastCollectTime: '2026-04-25T17:36:38' }, + data: { programName: '1566.NC', partCount: 580, totalPartCount: 88624, runStatus: '运行中', operateMode: '自动', lastCollectTime: '2026-04-25T17:36:38' }, }), }, { diff --git a/frontend/mock/simulator.ts b/frontend/mock/simulator.ts index c513110..1f6d7a1 100644 --- a/frontend/mock/simulator.ts +++ b/frontend/mock/simulator.ts @@ -45,11 +45,11 @@ const mockStatusList = [ } ] -// 模拟设备状态 +// 模拟设备状态(与实际FANUC 10个Tag结构一致) const mockDevices = [ - { deviceCode: 'fanake_1.2', desc: '西-1.2', scenario: 'machining', isOnline: true, programName: 'O504', partCount: 14, runStatus: 3, operateMode: 10, spindleSpeedSet: 3000, spindleSpeedActual: 2980, feedSpeedSet: 500, feedSpeedActual: 490, spindleLoad: 65, machiningStatus: 'cutting', scenarioTick: 45, scenarioDuration: 120 }, - { deviceCode: 'fanake_1.3', desc: '西-1.3', scenario: 'idle', isOnline: true, programName: 'O1', partCount: 53, runStatus: 1, operateMode: 10, spindleSpeedSet: 0, spindleSpeedActual: 0, feedSpeedSet: 0, feedSpeedActual: 0, spindleLoad: 5, machiningStatus: 'idle', scenarioTick: 12, scenarioDuration: 60 }, - { deviceCode: 'fanake_1.4', desc: '西-1.4', scenario: 'offline', isOnline: false, programName: 'O200', partCount: 0, runStatus: 0, operateMode: 0, spindleSpeedSet: 0, spindleSpeedActual: 0, feedSpeedSet: 0, feedSpeedActual: 0, spindleLoad: 0, machiningStatus: 'offline', scenarioTick: 0, scenarioDuration: 0 } + { deviceCode: 'fanake_1.2', desc: '西-1.2', scenario: 'machining', isOnline: true, programName: 'O504', partCount: 14, totalPartCount: 45930, runStatus: 3, operateMode: 1, scenarioTick: 45, scenarioDuration: 120 }, + { deviceCode: 'fanake_1.3', desc: '西-1.3', scenario: 'idle', isOnline: true, programName: 'O1', partCount: 53, totalPartCount: 62324, runStatus: 0, operateMode: 10, scenarioTick: 12, scenarioDuration: 60 }, + { deviceCode: 'fanake_1.4', desc: '西-1.4', scenario: 'offline', isOnline: false, programName: '', partCount: 0, totalPartCount: 0, runStatus: 0, operateMode: 0, scenarioTick: 0, scenarioDuration: 0 } ] // 模拟请求日志 diff --git a/frontend/src/api/simulator.ts b/frontend/src/api/simulator.ts index 4448c9c..2decb12 100644 --- a/frontend/src/api/simulator.ts +++ b/frontend/src/api/simulator.ts @@ -40,14 +40,9 @@ export interface DeviceStatus { isOnline: boolean programName: string partCount: number + totalPartCount: number runStatus: number operateMode: number - spindleSpeedSet: number - spindleSpeedActual: number - feedSpeedSet: number - feedSpeedActual: number - spindleLoad: number - machiningStatus: string scenarioTick: number scenarioDuration: number } diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 7354950..33bbf61 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -53,18 +53,20 @@ export interface Machine { export interface MachineStatus { programName: string partCount: number + /** 历史累计加工零件总数(FANUC Tag1) */ + totalPartCount?: number runStatus: string operationMode: string - /** 主轴设定转速 */ - spindleSpeedSet?: number - /** 进给设定速度 */ - feedSpeedSet?: number - /** 主轴实际转速 */ - spindleSpeedActual?: number - spindleSpeed: number - feedRate: number - spindleLoad: number - machiningStatus: string + /** 主轴设定转速(部分品牌无此数据时为null) */ + spindleSpeedSet?: number | null + /** 进给设定速度(部分品牌无此数据时为null) */ + feedSpeedSet?: number | null + /** 主轴实际转速(部分品牌无此数据时为null) */ + spindleSpeedActual?: number | null + spindleSpeed?: number | null + feedRate?: number | null + spindleLoad?: number | null + machiningStatus?: string | null lastCollectTime: string } diff --git a/frontend/src/views/machine/MachineDetailPage.vue b/frontend/src/views/machine/MachineDetailPage.vue index e6373c0..11cafa2 100644 --- a/frontend/src/views/machine/MachineDetailPage.vue +++ b/frontend/src/views/machine/MachineDetailPage.vue @@ -27,12 +27,9 @@ {{status.programName||'-'}} {{status.partCount??'-'}} + {{status.totalPartCount??'-'}} {{status.runStatus||'-'}} {{status.operationMode||'-'}} - {{status.spindleSpeedSet??'-'}} - {{status.feedSpeedSet??'-'}} - {{status.spindleSpeedActual??'-'}} - {{status.spindleLoad??'-'}}% diff --git a/simulator.json b/simulator.json index 914d815..ed8876b 100644 --- a/simulator.json +++ b/simulator.json @@ -8,9 +8,9 @@ "dataChangeInterval": 10, "scenarioMode": "auto", "devices": [ - { "deviceCode": "CNC-A001", "desc": "西栋1号", "initialProgram": "O0001", "initialPartCount": 50 }, - { "deviceCode": "CNC-006", "desc": "6号机床", "initialProgram": "O0002", "initialPartCount": 120 }, - { "deviceCode": "CNC-008", "desc": "8号机床", "initialProgram": "O0003", "initialPartCount": 0 } + { "deviceCode": "CNC-A001", "desc": "西栋1号", "initialProgram": "O0001", "initialPartCount": 50, "initialTotalPartCount": 45000 }, + { "deviceCode": "CNC-006", "desc": "6号机床", "initialProgram": "O0002", "initialPartCount": 120, "initialTotalPartCount": 88000 }, + { "deviceCode": "CNC-008", "desc": "8号机床", "initialProgram": "O0003", "initialPartCount": 0, "initialTotalPartCount": 126000 } ] }, { @@ -20,8 +20,8 @@ "dataChangeInterval": 15, "scenarioMode": "auto", "devices": [ - { "deviceCode": "CNC-B002", "desc": "B栋2号", "initialProgram": "1566.NC", "initialPartCount": 80 }, - { "deviceCode": "CNC-005", "desc": "验证机床", "initialProgram": "TEST001", "initialPartCount": 10 } + { "deviceCode": "CNC-B002", "desc": "B栋2号", "initialProgram": "1566.NC", "initialPartCount": 80, "initialTotalPartCount": 67000 }, + { "deviceCode": "CNC-005", "desc": "验证机床", "initialProgram": "TEST001", "initialPartCount": 10, "initialTotalPartCount": 23000 } ] } ] diff --git a/src/CncCollector/Core/CollectRecordWriter.cs b/src/CncCollector/Core/CollectRecordWriter.cs index bf18699..80ae98f 100644 --- a/src/CncCollector/Core/CollectRecordWriter.cs +++ b/src/CncCollector/Core/CollectRecordWriter.cs @@ -84,13 +84,11 @@ namespace CncCollector.Core foreach (var r in records) { conn.Execute(@"INSERT INTO cnc_collect_record (machine_id, collect_time, device_time, program_name, part_count, - device_status, run_status, operate_mode, spindle_speed_set, feed_speed_set, - spindle_speed_actual, feed_speed_actual, spindle_load, spindle_override, - power_on_time, run_time, cutting_time, cycle_time, machining_status, extra_data, created_at) + total_part_count, device_status, run_status, operate_mode, + power_on_time, run_time, extra_data, created_at) VALUES (@MachineId, @CollectTime, @DeviceTime, @ProgramName, @PartCount, - @DeviceStatus, @RunStatus, @OperateMode, @SpindleSpeedSet, @FeedSpeedSet, - @SpindleSpeedActual, @FeedSpeedActual, @SpindleLoad, @SpindleOverride, - @PowerOnTime, @RunTime, @CuttingTime, @CycleTime, @MachiningStatus, @ExtraData, @CreatedAt)", + @TotalPartCount, @DeviceStatus, @RunStatus, @OperateMode, + @PowerOnTime, @RunTime, @ExtraData, @CreatedAt)", new { r.MachineId, @@ -98,20 +96,12 @@ namespace CncCollector.Core DeviceTime = r.DeviceTime, ProgramName = r.ProgramName ?? (string)null, PartCount = r.PartCount, + TotalPartCount = r.TotalPartCount, DeviceStatus = r.DeviceStatus ?? (string)null, RunStatus = r.RunStatus ?? (string)null, OperateMode = r.OperateMode ?? (string)null, - SpindleSpeedSet = r.SpindleSpeedSet, - FeedSpeedSet = r.FeedSpeedSet, - SpindleSpeedActual = r.SpindleSpeedActual, - FeedSpeedActual = r.FeedSpeedActual, - SpindleLoad = r.SpindleLoad, - SpindleOverride = r.SpindleOverride, PowerOnTime = r.PowerOnTime, RunTime = r.RunTime, - CuttingTime = r.CuttingTime, - CycleTime = r.CycleTime, - MachiningStatus = r.MachiningStatus ?? (string)null, ExtraData = r.ExtraData ?? (string)null, CreatedAt = now }, tran); @@ -133,7 +123,8 @@ namespace CncCollector.Core conn.Execute(@"UPDATE cnc_machine SET last_collect_time = @CollectTime, last_device_status = @DeviceStatus, last_run_status = @RunStatus, last_program_name = @ProgramName, last_part_count = @PartCount, - last_operate_mode = @OperateMode, last_machining_status = @MachiningStatus + last_total_part_count = @TotalPartCount, + last_operate_mode = @OperateMode WHERE id = @MachineId", new { @@ -143,8 +134,8 @@ namespace CncCollector.Core RunStatus = r.RunStatus ?? (string)null, ProgramName = r.ProgramName ?? (string)null, r.PartCount, - OperateMode = r.OperateMode ?? (string)null, - MachiningStatus = r.MachiningStatus ?? (string)null + r.TotalPartCount, + OperateMode = r.OperateMode ?? (string)null }); } catch (Exception ex) diff --git a/src/CncCollector/Core/CollectWorker.cs b/src/CncCollector/Core/CollectWorker.cs index 17ad956..66748c6 100644 --- a/src/CncCollector/Core/CollectWorker.cs +++ b/src/CncCollector/Core/CollectWorker.cs @@ -447,32 +447,32 @@ namespace CncCollector.Core // 解析字段 var parsed = DataParser.ParseDevice(deviceObj, brand, mappings); - // 构建 CollectRecord + // 构建 CollectRecord(仅包含实际FANUC数据中存在的字段) var record = new CollectRecord { MachineId = machine.Id, CollectTime = collectTime, ProgramName = GetStringValue(parsed, "program_name"), PartCount = GetDecimalValue(parsed, "part_count"), + TotalPartCount = GetDecimalValue(parsed, "total_part_count"), DeviceStatus = GetStringValue(parsed, "device_status"), RunStatus = GetStringValue(parsed, "run_status"), OperateMode = GetStringValue(parsed, "operate_mode"), - SpindleSpeedSet = GetDecimalValue(parsed, "spindle_speed_set"), - FeedSpeedSet = GetDecimalValue(parsed, "feed_speed_set"), - SpindleSpeedActual = GetDecimalValue(parsed, "spindle_speed_actual"), - FeedSpeedActual = GetDecimalValue(parsed, "feed_speed_actual"), - SpindleLoad = GetDecimalValue(parsed, "spindle_load"), - SpindleOverride = GetDecimalValue(parsed, "spindle_override"), PowerOnTime = GetDecimalValue(parsed, "power_on_time"), - RunTime = GetDecimalValue(parsed, "run_time"), - CuttingTime = GetDecimalValue(parsed, "cutting_time"), - CycleTime = GetDecimalValue(parsed, "cycle_time"), - MachiningStatus = GetStringValue(parsed, "machining_status") + RunTime = GetDecimalValue(parsed, "run_time") }; + // 断电设备过滤:如果program_name和part_count都被过滤为空, + // 说明设备处于断电状态(quality≠0),跳过此设备不生成记录 + if (string.IsNullOrEmpty(record.ProgramName) && !record.PartCount.HasValue) + { + _log.Debug($"设备{deviceCode}所有有效tag为空(可能断电),跳过记录"); + continue; + } + records.Add(record); - // 产量跟踪 + // 产量跟踪(program_name为空时不触发分段逻辑) _tracker.Track(machine.Id, record.ProgramName, record.PartCount, collectTime); } diff --git a/src/CncCollector/Core/DataParser.cs b/src/CncCollector/Core/DataParser.cs index 7dcae5b..aa0f68a 100644 --- a/src/CncCollector/Core/DataParser.cs +++ b/src/CncCollector/Core/DataParser.cs @@ -33,7 +33,9 @@ namespace CncCollector.Core } /// - /// 解析单台设备的 JSON tags 数据 + /// 解析单台设备的 JSON tags 数据。 + /// 根据数据有效性规则过滤无效tag:quality≠0 或 time以1970-01-01开头的tag视为无效。 + /// _io_status 始终有效,不受quality/time约束。 /// /// 设备 JSON 对象 /// 品牌配置 @@ -57,16 +59,35 @@ namespace CncCollector.Core var tagsArray = tagsToken as JArray; - // 构建 tag id → value 的快速查找字典 + // 构建 tag id → rawValue 的快速查找字典(经过有效性过滤) var tagDict = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var tag in tagsArray) { string id = tag["id"]?.ToString() ?? ""; - string value = tag["value"]?.ToString() ?? ""; - if (!string.IsNullOrEmpty(id)) + if (string.IsNullOrEmpty(id)) continue; + + // _io_status 始终有效,跳过过滤 + if (string.Equals(id, "_io_status", StringComparison.OrdinalIgnoreCase)) + { + tagDict[id] = tag["value"]?.ToString() ?? ""; + continue; + } + + // 数据有效性规则1:quality ≠ 0 → 无效,忽略 + string qualityStr = tag["quality"]?.ToString() ?? "0"; + if (qualityStr != "0") { - tagDict[id] = value; + continue; } + + // 数据有效性规则2:time 以 1970-01-01 开头 → 采集失败标记,忽略 + string timeStr = tag["time"]?.ToString() ?? ""; + if (timeStr.StartsWith("1970-01-01", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + tagDict[id] = tag["value"]?.ToString() ?? ""; } // 按字段映射表提取 @@ -79,7 +100,7 @@ namespace CncCollector.Core string rawValue; if (!tagDict.TryGetValue(tagId, out rawValue)) { - // 字段不存在,跳过(非必填字段可能不存在) + // 字段不存在或被过滤为无效,跳过 continue; } @@ -90,10 +111,9 @@ namespace CncCollector.Core StringValue = rawValue }; - // 数值型:去除 .00000 尾缀 + // 数值型:解析为 decimal if (dataType == "number" && !string.IsNullOrEmpty(rawValue)) { - // 尝试解析为 decimal string cleanValue = rawValue.Trim(); decimal numVal; if (decimal.TryParse(cleanValue, System.Globalization.NumberStyles.Float, diff --git a/src/CncModels/Entity/CollectRecord.cs b/src/CncModels/Entity/CollectRecord.cs index 430299b..fbba2bf 100644 --- a/src/CncModels/Entity/CollectRecord.cs +++ b/src/CncModels/Entity/CollectRecord.cs @@ -25,6 +25,9 @@ namespace CncModels.Entity /// 零件计数 public decimal? PartCount { get; set; } + /// 历史终身累计零件总数(Tag1) + public decimal? TotalPartCount { get; set; } + /// 设备状态 public string DeviceStatus { get; set; } diff --git a/src/CncSimulator/Config/SimulatorConfig.cs b/src/CncSimulator/Config/SimulatorConfig.cs index 36b3549..f7276c2 100644 --- a/src/CncSimulator/Config/SimulatorConfig.cs +++ b/src/CncSimulator/Config/SimulatorConfig.cs @@ -73,5 +73,9 @@ namespace CncSimulator.Config /// 初始零件数 [JsonProperty("initialPartCount")] public int InitialPartCount { get; set; } = 0; + + /// 初始历史累计零件总数 + [JsonProperty("initialTotalPartCount")] + public int InitialTotalPartCount { get; set; } = 0; } } diff --git a/src/CncSimulator/Core/SimulatorEngine.cs b/src/CncSimulator/Core/SimulatorEngine.cs index 11b3194..ae66166 100644 --- a/src/CncSimulator/Core/SimulatorEngine.cs +++ b/src/CncSimulator/Core/SimulatorEngine.cs @@ -326,6 +326,67 @@ namespace CncSimulator.Core _dbAddresses = _dbReader.ReadAddresses(); SendResponse(ctx, 200, "{\"ok\":true,\"count\":" + _dbAddresses.Count + "}", "application/json"); } + else if (path == "/admin/api/event") + { + // 转发事件到对应的SimulatorServer(数据端口) + string eventBody = ReadRequestBody(ctx); + var eventObj = JObject.Parse(eventBody); + string deviceId = eventObj["deviceId"]?.ToString(); + string eventType = eventObj["eventType"]?.ToString(); + if (string.IsNullOrEmpty(deviceId) || string.IsNullOrEmpty(eventType)) + { + SendResponse(ctx, 400, "{\"error\":\"缺少deviceId或eventType\"}", "application/json"); + return; + } + // 在所有server中查找目标设备并触发事件 + bool found = false; + foreach (var server in _servers) + { + server.TriggerDeviceEvent(deviceId, eventType); + found = true; + break; // TriggerDeviceEvent内部只处理第一个匹配的设备 + } + SendResponse(ctx, 200, found ? "{\"ok\":true}" : "{\"ok\":false,\"error\":\"设备未找到\"}", "application/json"); + } + else if (path == "/admin/api/event-history") + { + // 转发事件历史查询 + var allEvents = new JArray(); + foreach (var server in _servers) + { + // 利用server的HandleEventHistoryApi逻辑提取数据 + foreach (var dev in server.Devices) + { + foreach (var evt in dev.State.EventHistory) + { + allEvents.Add(new JObject + { + ["timestamp"] = evt.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"), + ["deviceCode"] = evt.DeviceCode, + ["eventType"] = evt.EventType, + ["oldProgram"] = evt.OldProgram, + ["newProgram"] = evt.NewProgram, + ["partCountBefore"] = evt.PartCountBefore, + ["partCountAfter"] = evt.PartCountAfter, + ["detail"] = evt.Detail + }); + } + } + } + SendResponse(ctx, 200, allEvents.ToString(), "application/json"); + } + else if (path == "/admin/api/mode") + { + // 转发模式切换到所有SimulatorServer + string modeBody = ReadRequestBody(ctx); + var modeObj = JObject.Parse(modeBody); + string mode = modeObj["mode"]?.ToString() ?? "auto"; + foreach (var server in _servers) + { + server.SetMode(mode); + } + SendResponse(ctx, 200, "{\"ok\":true,\"mode\":\"" + mode + "\"}", "application/json"); + } else { SendResponse(ctx, 200, "CNC模拟采集服务网关。请访问 /admin 管理页面。", "text/plain"); diff --git a/src/CncSimulator/Core/SimulatorServer.cs b/src/CncSimulator/Core/SimulatorServer.cs index c41695e..0b7f9b2 100644 --- a/src/CncSimulator/Core/SimulatorServer.cs +++ b/src/CncSimulator/Core/SimulatorServer.cs @@ -285,14 +285,9 @@ namespace CncSimulator.Core ["isOnline"] = s.IsOnline, ["programName"] = s.ProgramName, ["partCount"] = s.PartCount, + ["totalPartCount"] = s.TotalPartCount, ["runStatus"] = s.RunStatus, ["operateMode"] = s.OperateMode, - ["spindleSpeedSet"] = s.SpindleSpeedSet, - ["spindleSpeedActual"] = s.SpindleSpeedActual, - ["feedSpeedSet"] = s.FeedSpeedSet, - ["feedSpeedActual"] = s.FeedSpeedActual, - ["spindleLoad"] = s.SpindleLoad, - ["machiningStatus"] = s.MachiningStatus, ["scenarioTick"] = s.ScenarioTick, ["scenarioDuration"] = s.ScenarioDuration }); @@ -318,7 +313,7 @@ namespace CncSimulator.Core } } - /// 生成当前JSON响应 + /// 生成当前JSON响应(包括在线和离线设备) private string GenerateCurrentJson() { var devices = new JArray(); @@ -328,10 +323,66 @@ namespace CncSimulator.Core { devices.Add(_generator.GenerateDevice(dev.State)); } + else + { + // 离线设备:生成带有quality=1和1970时间的tag + devices.Add(GenerateOfflineDevice(dev.State)); + } } return devices.ToString(Formatting.None); } + /// 生成离线设备的JSON(_io_status有效,其余tag quality=1) + private JObject GenerateOfflineDevice(DeviceState state) + { + var tags = new JArray(); + DateTime now = DateTime.Now; + + // _io_status 始终有效 + tags.Add(new JObject + { + ["id"] = "_io_status", + ["desc"] = "设备状态", + ["quality"] = "0", + ["value"] = "0.00000", + ["time"] = now.ToString("yyyy-MM-dd HH:mm:ss") + }); + + // 其余tag全部标记为无效(quality=1, time=1970) + string epoch = "1970-01-01 08:00:00"; + var offlineTags = new[] + { + new { id = "Tag1", desc = "加工零件总数", value = "0.00000" }, + new { id = "Tag5", desc = "执行的NC主程序名", value = "" }, + new { id = "Tag6", desc = "执行的NC主程序号", value = "" }, + new { id = "Tag7", desc = "当前加工程序内容", value = "" }, + new { id = "Tag8", desc = "当前加工零件数", value = "0.00000" }, + new { id = "Tag9", desc = "运行状态", value = "0.00000" }, + new { id = "Tag11", desc = "操作模式", value = "0.00000" }, + new { id = "Tag22", desc = "开机时间", value = "0.00000" }, + new { id = "Tag23", desc = "运行时间", value = "0.00000" } + }; + + foreach (var t in offlineTags) + { + tags.Add(new JObject + { + ["id"] = t.id, + ["desc"] = t.desc, + ["quality"] = "1", + ["value"] = t.value, + ["time"] = epoch + }); + } + + return new JObject + { + ["device"] = state.DeviceCode, + ["desc"] = state.Desc, + ["tags"] = tags + }; + } + /// 生成关键数据摘要 private string GenerateKeyData() { diff --git a/src/CncSimulator/Device/DeviceSimulator.cs b/src/CncSimulator/Device/DeviceSimulator.cs index 0672f26..6d8a9e8 100644 --- a/src/CncSimulator/Device/DeviceSimulator.cs +++ b/src/CncSimulator/Device/DeviceSimulator.cs @@ -4,13 +4,13 @@ namespace CncSimulator.Device { /// /// 单台设备的状态机。 - /// 维护设备当前状态,根据场景规则更新各字段。 + /// 维护设备当前状态,根据场景规则更新字段。 + /// 仅包含实际FANUC采集数据中存在的字段。 /// public class DeviceSimulator { private readonly DeviceState _state; private readonly Random _rng; - private static readonly string[] MachiningStatusOptions = { "G01", "G01", "G01", "G02", "G00" }; private static readonly string[] ProgramPool = { "O0001", "O0002", "1566.NC", "PART-A", "TEST-03" }; private int _programPoolIndex; @@ -26,23 +26,17 @@ namespace CncSimulator.Device Desc = config.Desc, ProgramName = config.InitialProgram, PartCount = config.InitialPartCount, - // LastPartCount removed - not in DeviceState + // 历史累计零件总数:配置值优先,否则随机 + TotalPartCount = config.InitialTotalPartCount > 0 + ? config.InitialTotalPartCount + : _rng.Next(40000, 50000), DataChangeInterval = dataChangeInterval, IsOnline = true, DeviceStatus = 1, RunStatus = 0, OperateMode = 10, - SpindleSpeedSet = 450, - FeedSpeedSet = 60, - SpindleSpeedActual = 0, - FeedSpeedActual = 0, - SpindleLoad = 0, - SpindleOverride = 100, PowerOnTime = _rng.Next(20000000, 24000000), RunTime = _rng.Next(15000, 20000), - CuttingTime = _rng.Next(6000000, 7000000), - CycleTime = _rng.Next(500, 800), - MachiningStatus = "", ProgramContent = "", CurrentScenario = "idle", ScenarioTick = 0, @@ -117,6 +111,7 @@ namespace CncSimulator.Device { case "machining": _state.PartCount++; + _state.TotalPartCount++; _state.TotalPartsSinceStart++; // 按NC程序名累计零件数 if (!_state.PartsByProgram.ContainsKey(_state.ProgramName)) @@ -125,16 +120,8 @@ namespace CncSimulator.Device _state.RunStatus = 3; _state.DeviceStatus = 1; _state.OperateMode = 1; - // 主轴实际速度 = 设定 ± 10%随机波动 - _state.SpindleSpeedActual = _state.SpindleSpeedSet * (1m + (decimal)(_rng.NextDouble() * 0.2 - 0.1)); - _state.FeedSpeedActual = _state.FeedSpeedSet * (1m + (decimal)(_rng.NextDouble() * 0.1 - 0.05)); - _state.SpindleLoad = _rng.Next(15, 46); - _state.SpindleOverride = 100; - _state.MachiningStatus = MachiningStatusOptions[_rng.Next(MachiningStatusOptions.Length)]; - _state.CuttingTime += interval; _state.RunTime += interval; _state.PowerOnTime += interval; - _state.CycleTime += interval; _state.ProgramContent = "<" + _state.ProgramName + ">\nG40G49G80\n( SIMULATOR )"; break; @@ -147,21 +134,12 @@ namespace CncSimulator.Device case "idle": _state.RunStatus = 0; _state.OperateMode = 10; - _state.SpindleSpeedActual = 0; - _state.FeedSpeedActual = 0; - _state.SpindleLoad = 0; - _state.FeedSpeedSet = 0; - _state.MachiningStatus = ""; _state.PowerOnTime += interval; _state.RunTime += interval; break; case "pause": _state.RunStatus = 1; - _state.SpindleSpeedActual = 0; - _state.FeedSpeedActual = 0; - _state.SpindleLoad = 0; - _state.MachiningStatus = ""; _state.PowerOnTime += interval; _state.RunTime += interval; break; @@ -182,15 +160,11 @@ namespace CncSimulator.Device _programPoolIndex = (_programPoolIndex + 1) % ProgramPool.Length; _state.ProgramName = ProgramPool[_programPoolIndex]; _state.PartCount = 0; - _state.CycleTime = 0; _state.RunStatus = 3; _state.DeviceStatus = 1; _state.OperateMode = 1; - _state.MachiningStatus = "G01"; - _state.SpindleSpeedSet = _rng.Next(200, 801); - _state.FeedSpeedSet = _rng.Next(30, 151); - _state.SpindleOverride = 100; _state.ProgramContent = "<" + _state.ProgramName + ">\nG40G49G80\n( SIMULATOR )"; + // TotalPartCount不清零(终身累计) // 记录事件历史 _state.RecordEvent("program_change", oldProgram, _state.ProgramName, oldPartCount, 0); } @@ -202,6 +176,7 @@ namespace CncSimulator.Device _state.PartCount = 0; _state.RunStatus = 3; _state.DeviceStatus = 1; + // TotalPartCount不清零(终身累计) // 记录事件历史 _state.RecordEvent("manual_reset", _state.ProgramName, _state.ProgramName, oldPartCount, 0); } @@ -222,13 +197,9 @@ namespace CncSimulator.Device _state.IsOnline = true; _state.DeviceStatus = 1; _state.PartCount = 0; - _state.CycleTime = 0; _state.RunStatus = 0; _state.OperateMode = 10; - _state.SpindleSpeedActual = 0; - _state.FeedSpeedActual = 0; - _state.SpindleLoad = 0; - _state.MachiningStatus = ""; + // TotalPartCount不清零(终身累计) // 记录事件历史 _state.RecordEvent("power_on", _state.ProgramName, _state.ProgramName, oldPartCount, 0, "设备开机,零件数清零"); } diff --git a/src/CncSimulator/Device/DeviceState.cs b/src/CncSimulator/Device/DeviceState.cs index 52ede52..57d9a2c 100644 --- a/src/CncSimulator/Device/DeviceState.cs +++ b/src/CncSimulator/Device/DeviceState.cs @@ -26,6 +26,9 @@ namespace CncSimulator.Device /// 当前零件数 public int PartCount { get; set; } = 0; + /// 历史终身累计零件总数(对应Tag1) + public decimal TotalPartCount { get; set; } = 0; + /// 设备状态 _io_status: 0=离线, 1=在线 public int DeviceStatus { get; set; } = 1; @@ -35,39 +38,12 @@ namespace CncSimulator.Device /// 操作模式: 1=MEM, 10=JOG 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; - - /// 加工状态: G01/G00/G02/等 - public string MachiningStatus { get; set; } = ""; - /// 加工程序内容片段 public string ProgramContent { get; set; } = ""; diff --git a/src/CncSimulator/Generator/FanucDataGenerator.cs b/src/CncSimulator/Generator/FanucDataGenerator.cs index 7fc04ca..57f2b33 100644 --- a/src/CncSimulator/Generator/FanucDataGenerator.cs +++ b/src/CncSimulator/Generator/FanucDataGenerator.cs @@ -6,7 +6,7 @@ namespace CncSimulator.Generator { /// /// FANUC品牌JSON数据生成器。 - /// 根据设备状态生成18个Tag的FANUC格式JSON。 + /// 根据设备状态生成10个Tag的FANUC格式JSON,与实际FANUC采集数据结构一致。 /// public class FanucDataGenerator : IBrandGenerator { @@ -27,7 +27,7 @@ namespace CncSimulator.Generator return device; } - /// 生成18个Tag的JArray + /// 生成10个Tag的JArray private JArray GenerateTags(DeviceState state) { var tags = new JArray(); @@ -65,8 +65,9 @@ namespace CncSimulator.Generator }); } + // 严格按实际FANUC采集数据的10个Tag生成 AddNumericTag("_io_status", "设备状态", state.DeviceStatus); - AddNumericTag("Tag2", "当前轴数", 4); + AddNumericTag("Tag1", "加工零件总数", state.TotalPartCount); AddStringTag("Tag5", "执行的NC主程序名", state.ProgramName); AddStringTag("Tag6", "执行的NC主程序号", "N0"); AddStringTag("Tag7", "当前加工程序内容", @@ -76,17 +77,8 @@ namespace CncSimulator.Generator 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; } diff --git a/src/CncWebApi/Global.asax.cs b/src/CncWebApi/Global.asax.cs index fe60f5c..64abcad 100644 --- a/src/CncWebApi/Global.asax.cs +++ b/src/CncWebApi/Global.asax.cs @@ -18,12 +18,8 @@ namespace CncWebApi /// protected void Application_Start() { - // 初始化 log4net - var logConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config"); - if (File.Exists(logConfig)) - { - log4net.Config.XmlConfigurator.Configure(new FileInfo(logConfig)); - } + // 初始化 log4net(从 Web.config 的 节读取) + log4net.Config.XmlConfigurator.Configure(); GlobalConfiguration.Configure(WebApiConfig.Register); } diff --git a/src/CncWebApi/Web.config b/src/CncWebApi/Web.config index 5f7333d..bd7b4c8 100644 --- a/src/CncWebApi/Web.config +++ b/src/CncWebApi/Web.config @@ -4,6 +4,40 @@ 部署到 IIS 时,需要将此文件复制到站点根目录 --> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \nG40G49G80\n( NAME: Administrator )\n( M","time":"2026-05-06 15:06:46"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"5.00000","time":"2026-05-06 15:07:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"1.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"4.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"26481840.00000","time":"2026-05-06 15:04:27"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"16611.00000","time":"2026-05-06 15:04:55"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"12225.00000","time":"2026-05-06 15:05:23"}]},{"device":"fanake_1.7","desc":"-1.7","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:51"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:19"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-7.450 \nX-5.850 \nZ6.900 \nG00 Z42.000 \nY7.000 \n","time":"2026-05-06 15:06:46"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"12.00000","time":"2026-05-06 15:07:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"29563620.00000","time":"2026-05-06 15:04:27"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"14635.00000","time":"2026-05-06 15:04:55"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"18112.00000","time":"2026-05-06 15:05:23"}]},{"device":"fanake_1.9","desc":"-1.9","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:51"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:19"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"M1 \nG90A90.\nX-105.Y-48.303\nG43Z41.738H01\nM10\nZ2","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"18.00000","time":"2026-05-06 15:07:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"5.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"24744120.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"1121.00000","time":"2026-05-06 15:04:55"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"16472.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.10","desc":"-1.10","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O88","time":"2026-05-06 15:05:51"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N380","time":"2026-05-06 15:06:19"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 X-115.000 \nG02 X-130.100 Y-127.500 I0.000 J","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"1789.00000","time":"2026-05-06 15:07:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"83095080.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"953670.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"61675.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.11","desc":"-1.11","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N170","time":"2026-05-06 15:06:19"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"N178 Y-132.000 F1000. \nN180 X-131.500 \nN182 Y35","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"1153.00000","time":"2026-05-06 15:07:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"81371460.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"1784670.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"51653.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.12","desc":"-1.12","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O2","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-06 15:06:19"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":".189%","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"1162.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"57159300.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"2611091.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"52058.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.13","desc":"-1.13","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:19"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G91 G28 Y0.000 \nM30\n%","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"150.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"60468480.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"52678.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"58791.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.14","desc":"-1.14","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"173.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:00"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:00"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"63446160.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"55461.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"69633.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.15","desc":"-1.15","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O506","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 X20.775 \nY-8.284 \nX4.800 \nY8.284 \nX36.750 \n","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"4.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"54781260.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"6403.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"32442.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.16","desc":"-1.16","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G02 X-60.570 Y47.947 I-0.989 J4.973 \nG01 X-60.6","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"103.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"56446080.00000","time":"2026-05-06 15:04:28"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"71194.00000","time":"2026-05-06 15:04:56"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"43958.00000","time":"2026-05-06 15:05:24"}]},{"device":"fanake_1.17","desc":"-1.17","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"Y-12.865 \nY-12.269 \nY-11.673 \nY-11.077 \nY-10.48","time":"2026-05-06 15:06:47"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"193.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"53566620.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"78701.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"25609.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.18","desc":"-1.18","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O2","time":"2026-05-06 15:05:52"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-187.885 Y-28.629 \nG02 X-192.115 I-2.115 J3.66","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"0.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"86160480.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"0.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"113856.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.19","desc":"-1.19","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"Z-4.500 \nG00 Z33.000 \nG90 X-90.641 Y-28.053 \nZ-","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"0.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"136464540.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"68.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"144029.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.20","desc":"-1.20","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 X-2.200 Y14.549 \nX-4.200 Y18.013 \nX-2.100 Y","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"0.00000","time":"2026-05-06 15:07:15"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"141877620.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"0.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"139788.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.21","desc":"-1.21","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G02 X-114.170 I2.830 J-4.902 \nG01 X-108.900 Y27","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"0.00000","time":"2026-05-06 15:07:16"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"140563920.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"639.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"127265.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.22","desc":"-1.22","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"1027","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-06 15:06:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"24.00000","time":"2026-05-06 15:07:16"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:01"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:01"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"130547880.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"7936.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"161742.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.23","desc":"-1.23","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O2","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-06 15:06:21"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 X-235. M8 F39. \nG00 Z200.000 M9 \nM5 \nG28 Y0","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"38.00000","time":"2026-05-06 15:07:16"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:02"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"1.00000","time":"2026-05-06 15:04:02"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"124760580.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"14200.00000","time":"2026-05-06 15:04:57"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"106705.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.24","desc":"-1.24","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-06 15:06:21"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-60.000 \nY-67.700 F500. \nG00 Z155.000 \nM9 \nM5 ","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"15.00000","time":"2026-05-06 15:07:16"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:02"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:02"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"117256500.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"12020.00000","time":"2026-05-06 15:04:58"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"106460.00000","time":"2026-05-06 15:05:25"}]},{"device":"fanake_1.25","desc":"-1.25","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:05:53"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:21"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G99 G83 Z-12.980 Q1.000 R9.020 F20. \nG80\nG0Z56.","time":"2026-05-06 15:06:48"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"13.00000","time":"2026-05-06 15:07:16"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:02"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:02"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"107630700.00000","time":"2026-05-06 15:04:29"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"18953.00000","time":"2026-05-06 15:04:58"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"88768.00000","time":"2026-05-06 15:05:26"}]},{"device":"fanake_1.26","desc":"-1.26","tags":[{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake_1.27","desc":"-1.27","tags":[{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake_1.28","desc":"-1.28","tags":[{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake_1.29","desc":"-1.29","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:06:18"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-06 15:06:45"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G02 X-17.910 I-1.910 J3.308 \nG01 X-26.800 Y-18.","time":"2026-05-06 15:07:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"19.00000","time":"2026-05-06 15:03:59"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:26"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:26"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"54806160.00000","time":"2026-05-06 15:04:54"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"20654.00000","time":"2026-05-06 15:05:22"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"57603.00000","time":"2026-05-06 15:05:50"}]},{"device":"fanake_1.30","desc":"-1.30","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:06:18"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-06 15:06:45"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G91 G28 Y0.000 \nM30\n%","time":"2026-05-06 15:07:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"31.00000","time":"2026-05-06 15:03:59"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:26"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:26"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"62122620.00000","time":"2026-05-06 15:04:54"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"17469.00000","time":"2026-05-06 15:05:22"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"62111.00000","time":"2026-05-06 15:05:50"}]},{"device":"fanake_1.31","desc":"-1.31","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:06:18"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-06 15:06:45"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X0.000 \nX8.000 \nZ1.100 \nG00 Z50.000 \nM9 \nG91G28","time":"2026-05-06 15:07:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"0.00000","time":"2026-05-06 15:03:59"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-06 15:04:26"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:26"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"63428160.00000","time":"2026-05-06 15:04:54"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"1001.00000","time":"2026-05-06 15:05:22"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"85647.00000","time":"2026-05-06 15:05:50"}]},{"device":"fanake_1.32","desc":"-1.32","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:06:18"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-06 15:06:46"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-06 15:07:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"90.00000","time":"2026-05-06 15:03:59"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:27"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"5.00000","time":"2026-05-06 15:04:27"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"67187760.00000","time":"2026-05-06 15:04:54"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"23644.00000","time":"2026-05-06 15:05:23"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"86248.00000","time":"2026-05-06 15:05:50"}]},{"device":"fanake_1.33","desc":"-1.33","tags":[{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-06 15:06:18"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-06 15:06:46"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"Y0.000 \nY4.792 \nG02 X-10.400 Y8.429 I4.200 J0.0","time":"2026-05-06 15:07:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"3.00000","time":"2026-05-06 15:03:59"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-06 15:04:27"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-06 15:04:27"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"85447860.00000","time":"2026-05-06 15:04:54"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"577.00000","time":"2026-05-06 15:05:23"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"99421.00000","time":"2026-05-06 15:05:50"}]}] \ No newline at end of file +[{"device":"fanake-1.2_1.2","desc":"-1.2","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:12"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:11"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:11"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-23.000 \nG02 X-20.400 Y14.780 I5.200 J0.000 \nG","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"35.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"45129840.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"12201.00000","time":"2026-05-11 17:33:12"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"45930.00000","time":"2026-05-11 17:33:12"}]},{"device":"fanake-1.3_1.3","desc":"-1.3","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 Y2.955 \nG03 X-15.947 Y3.637 I-1.000 J0.000 ","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"31.00000","time":"2026-05-11 17:33:13"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"45420840.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"26722.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"49652.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.4_1.4","desc":"-1.4","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:12"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:11"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-200.454 \nX-210.454 \nZ-4.500 \nG00 Z33.000 \nG90","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"17.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"46104660.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"19609.00000","time":"2026-05-11 17:33:12"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"40607.00000","time":"2026-05-11 17:33:12"}]},{"device":"fanake-1.5_1.5","desc":"-1.5","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:14"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O9001","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G43 Z100.000 H2 \nZ64.300 \nG41 D2 \nG01 Z63.300 F","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"30.00000","time":"2026-05-11 17:33:13"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"42139260.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"26286.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"44113.00000","time":"2026-05-11 17:33:14"}]},{"device":"fanake-1.6_1.6","desc":"-1.6","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"1370.NC","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"<1370.NC>\nG40G49G80\n( NAME: Administrator )\n( M","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"88.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"26697720.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"21227.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"12323.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.7_1.7","desc":"-1.7","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:12"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"037.NC","time":"2026-05-11 17:33:11"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-11 17:33:11"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-11 17:33:11"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"83.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"29783220.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"102938.00000","time":"2026-05-11 17:33:12"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"18221.00000","time":"2026-05-11 17:33:12"}]},{"device":"fanake-1.8_1.8","desc":"-1.8","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G03 I-4.200 J0.000 \nG01 X-37.800 \nZ26.000 \nG00 ","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"77.00000","time":"2026-05-11 17:33:13"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"24738120.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"19332.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"16243.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.9_1.9","desc":"-1.9","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:21"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:20"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G98 G83 Z21.346 Q0.800 R35.498 F25. \nG80 \nG00 Z","time":"2026-05-11 17:33:20"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:20"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:20"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:20"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"24958740.00000","time":"2026-05-11 17:33:21"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"18760.00000","time":"2026-05-11 17:33:21"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"16558.00000","time":"2026-05-11 17:33:21"}]},{"device":"fanake-1.10_1.10","desc":"-1.10","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:06:34"},{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake-1.11_1.11","desc":"-1.11","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:06:34"},{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake-1.12_1.12","desc":"-1.12","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:06:34"},{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake-1.13_1.13","desc":"-1.13","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:20"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:19"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-11 17:33:20"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-11 17:33:20"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"297.00000","time":"2026-05-11 17:33:20"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:33:20"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:20"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"60710880.00000","time":"2026-05-11 17:33:20"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"70372.00000","time":"2026-05-11 17:33:20"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"59265.00000","time":"2026-05-11 17:33:20"}]},{"device":"fanake-1.14_1.14","desc":"-1.14","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:14"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"112.00000","time":"2026-05-11 17:33:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"63688860.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"124322.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"69813.00000","time":"2026-05-11 17:33:14"}]},{"device":"fanake-1.15_1.15","desc":"-1.15","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:14"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O506","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G02 X30.141 Y12.351 I2.350 J17.341 \nX38.650 Y2.","time":"2026-05-11 17:33:14"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"130.00000","time":"2026-05-11 17:33:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"55027680.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"206904.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"32568.00000","time":"2026-05-11 17:33:14"}]},{"device":"fanake-1.16_1.16","desc":"-1.16","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:15"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X0.000 \nX8.000 \nZ2.200 \nG00 Z50.000 \nS350 M3 \nG","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"8.00000","time":"2026-05-11 17:33:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"85741920.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"5808.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"99897.00000","time":"2026-05-11 17:33:15"}]},{"device":"fanake-1.17_1.17","desc":"-1.17","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:14"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X0.000 \nX8.000 \nZ2.200 \nG00 Z50.000 \nS350 M3 \nG","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"8.00000","time":"2026-05-11 17:33:13"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"85741920.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"5807.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"99897.00000","time":"2026-05-11 17:33:14"}]},{"device":"fanake-1.18_1.18","desc":"-1.18","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G03 X18.353 Y11.342 I3.767 J-3.288 \nX19.000 Y11","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"25.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"86459280.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"5429.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"114209.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.19_1.19","desc":"-1.19","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"7","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"<7>\nG21\nG40G17G49G80G90G69G54\nM05\nM09\nT01M06\n(n","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"2.00000","time":"2026-05-11 17:33:13"},{"id":"Tag9","desc":"״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"4.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"136902000.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"2261.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"144261.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.20_1.20","desc":"-1.20","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:15"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X0.000 \nX8.000 \nZ2.200 \nG00 Z50.000 \nS350 M3 \nG","time":"2026-05-11 17:33:14"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"8.00000","time":"2026-05-11 17:33:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"85741920.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"5808.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"99897.00000","time":"2026-05-11 17:33:15"}]},{"device":"fanake-1.21_1.21","desc":"-1.21","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 X2.800 Y-17.321 \nG02 X-2.800 I-2.800 J4.850","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"16.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"141001800.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"2492.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"127537.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.22_1.22","desc":"-1.22","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"1027","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G90 G54 X-1.691 Y0.054 A0.000 S5000 M3 \nG43 Z50","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"15.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"130986840.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"2691.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"162236.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.23_1.23","desc":"-1.23","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O2","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"33.00000","time":"2026-05-11 17:33:13"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:33:13"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:13"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"125015280.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"26808.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"107000.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.24_1.24","desc":"-1.24","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:14"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N0","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"O0001S100M3\nG0G90X0Y0Z200.\nG0Z0\nG1X-238.M8F37\nG","time":"2026-05-11 17:33:13"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"0.00000","time":"2026-05-11 17:33:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"5.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"117511200.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"0.00000","time":"2026-05-11 17:33:14"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"106743.00000","time":"2026-05-11 17:33:14"}]},{"device":"fanake-1.25_1.25","desc":"-1.25","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N20","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G02 X-5.085 Y-9.221 I-5.020 J7.745 \nG01 X-25.04","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"20.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"107923020.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"27780.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"88902.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.26_1.26","desc":"-1.26","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:06:34"},{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake-1.27_1.27","desc":"-1.27","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:06:34"},{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake-1.28_1.28","desc":"-1.28","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"0.00000","time":"2026-05-11 17:06:34"},{"id":"Tag5","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag6","desc":"ִеNC","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag7","desc":"ǰӹ","quality":"1","value":"","time":"1970-01-01 08:00:00"},{"id":"Tag8","desc":"ǰӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag9","desc":"״̬","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag11","desc":"ģʽ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag22","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag23","desc":"ʱ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"},{"id":"Tag1","desc":"ӹ","quality":"1","value":"0.00000","time":"1970-01-01 08:00:00"}]},{"device":"fanake-1.29_1.29","desc":"-1.29","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:12"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:11"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-124.090 Y-23.839 \nG02 X-127.910 I-1.910 J3.30","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"24.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"55003260.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"26610.00000","time":"2026-05-11 17:33:12"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"57744.00000","time":"2026-05-11 17:33:12"}]},{"device":"fanake-1.30_1.30","desc":"-1.30","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:12"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"G01 X-7.000 Y-6.351 \nG02 X-9.000 Y-2.887 I2.000","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"37.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"62319300.00000","time":"2026-05-11 17:33:13"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"20687.00000","time":"2026-05-11 17:33:13"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"62324.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.31_1.31","desc":"-1.31","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:12"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:11"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-205.000 \nX-213.000 \nZ1.100 \nG00 Z50.000 \nY35.","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"7.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"63867120.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"8597.00000","time":"2026-05-11 17:33:12"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"85846.00000","time":"2026-05-11 17:33:12"}]},{"device":"fanake-1.32_1.32","desc":"-1.32","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:13"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:11"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N9","time":"2026-05-11 17:33:12"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X-207.000 \nX-214.300 \nZ-4.930 \nG00 Z40.000 \nG40","time":"2026-05-11 17:33:12"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"12.00000","time":"2026-05-11 17:33:12"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:12"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:12"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"67540320.00000","time":"2026-05-11 17:33:12"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"5144.00000","time":"2026-05-11 17:33:12"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"87814.00000","time":"2026-05-11 17:33:13"}]},{"device":"fanake-1.33_1.33","desc":"-1.33","tags":[{"id":"_io_status","desc":"豸״̬","quality":"0","value":"1.00000","time":"2026-05-11 17:33:15"},{"id":"Tag5","desc":"ִеNC","quality":"0","value":"O1","time":"2026-05-11 17:33:13"},{"id":"Tag6","desc":"ִеNC","quality":"0","value":"N1","time":"2026-05-11 17:33:13"},{"id":"Tag7","desc":"ǰӹ","quality":"0","value":"X0.000 \nX8.000 \nZ2.200 \nG00 Z50.000 \nS350 M3 \nG","time":"2026-05-11 17:33:14"},{"id":"Tag8","desc":"ǰӹ","quality":"0","value":"8.00000","time":"2026-05-11 17:33:14"},{"id":"Tag9","desc":"״̬","quality":"0","value":"3.00000","time":"2026-05-11 17:33:14"},{"id":"Tag11","desc":"ģʽ","quality":"0","value":"10.00000","time":"2026-05-11 17:33:14"},{"id":"Tag22","desc":"ʱ","quality":"0","value":"85741920.00000","time":"2026-05-11 17:33:14"},{"id":"Tag23","desc":"ʱ","quality":"0","value":"5808.00000","time":"2026-05-11 17:33:15"},{"id":"Tag1","desc":"ӹ","quality":"0","value":"99897.00000","time":"2026-05-11 17:33:15"}]}] \ No newline at end of file