|
|
|
@ -0,0 +1,801 @@
|
|
|
|
|
|
|
|
# CNC机床数据采集系统 - 模拟采集服务设计文档
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> 最后更新:2026-04-30
|
|
|
|
|
|
|
|
> 状态:设计中
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 一、概述
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
模拟采集服务(CncSimulator)是一个独立控制台应用,用于模拟真实采集地址的HTTP JSON接口。开发采集服务时无需连接真实CNC设备,通过本工具模拟各种采集场景进行联调测试。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 定位
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 项目 | 说明 |
|
|
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
|
|
| 性质 | 开发测试工具,不投入生产 |
|
|
|
|
|
|
|
|
| 用户 | 开发人员 |
|
|
|
|
|
|
|
|
| 运行方式 | 控制台应用,双击即运行 |
|
|
|
|
|
|
|
|
| 生命周期 | 采集服务开发完成后仍可用于回归测试 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 核心功能
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 在本机启动HTTP服务,模拟采集地址返回FANUC格式的JSON数据
|
|
|
|
|
|
|
|
- 浏览器管理界面,可操作启停、调整参数、触发特定事件
|
|
|
|
|
|
|
|
- 支持同时启动多个端口,模拟多个采集地址
|
|
|
|
|
|
|
|
- 按预设剧本自动循环各种真实场景
|
|
|
|
|
|
|
|
- 记录每次返回的完整数据日志,方便与采集服务核对
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 二、技术选型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 项 | 选型 | 说明 |
|
|
|
|
|
|
|
|
|----|------|------|
|
|
|
|
|
|
|
|
| 项目类型 | .NET Framework 4.7.2 控制台应用 | 加入CncDataSystem.sln |
|
|
|
|
|
|
|
|
| HTTP服务 | System.Net.HttpListener | 一个端口同时提供:数据API + 管理界面 |
|
|
|
|
|
|
|
|
| 管理界面 | 内嵌HTML + JavaScript | 无需前端构建,浏览器直接打开 |
|
|
|
|
|
|
|
|
| JSON | Newtonsoft.Json 12.0.3 | 与项目一致 |
|
|
|
|
|
|
|
|
| 日志 | log4net → 文件 | 每次返回完整JSON + 关键字段摘要 |
|
|
|
|
|
|
|
|
| 配置 | JSON文件 | simulator.json,运行时可热修改 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 双角色HTTP服务
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
每个端口同时承担两个职责:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
端口 9001:
|
|
|
|
|
|
|
|
GET / → 返回模拟JSON数组(采集服务调用这个地址)
|
|
|
|
|
|
|
|
GET /data → 同上(别名)
|
|
|
|
|
|
|
|
GET /admin → 管理界面HTML
|
|
|
|
|
|
|
|
GET /admin/api/status → JSON状态数据
|
|
|
|
|
|
|
|
POST /admin/api/start → 启动模拟
|
|
|
|
|
|
|
|
POST /admin/api/stop → 停止模拟
|
|
|
|
|
|
|
|
POST /admin/api/event → 触发手动事件
|
|
|
|
|
|
|
|
GET /admin/api/logs → 返回日志列表
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
采集服务配置采集地址时,URL填 `http://localhost:9001/` 即可。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 三、项目结构
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 文件清单
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
src/CncSimulator/
|
|
|
|
|
|
|
|
├── CncSimulator.csproj ← .NET Framework 4.7.2 控制台应用
|
|
|
|
|
|
|
|
├── Program.cs ← 主入口:读取配置→启动引擎→等待退出
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
|
|
├── Core/
|
|
|
|
|
|
|
|
│ ├── SimulatorEngine.cs ← 引擎:管理多个SimulatorServer的生命周期
|
|
|
|
|
|
|
|
│ ├── SimulatorServer.cs ← 单地址服务:HttpListener + 请求路由
|
|
|
|
|
|
|
|
│ └── LogRecorder.cs ← 返回日志记录器(内存环形缓冲 + 文件写入)
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
|
|
├── Device/
|
|
|
|
|
|
|
|
│ ├── DeviceSimulator.cs ← 单台设备状态机(维护当前状态、字段值)
|
|
|
|
|
|
|
|
│ ├── ScenarioPlayer.cs ← 剧本播放器(按时间线切换场景)
|
|
|
|
|
|
|
|
│ └── DeviceState.cs ← 设备状态数据结构
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
|
|
├── Generator/
|
|
|
|
|
|
|
|
│ ├── FanucDataGenerator.cs ← FANUC品牌JSON生成器
|
|
|
|
|
|
|
|
│ └── IBrandGenerator.cs ← 品牌生成器接口(预留扩展)
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
|
|
├── Admin/
|
|
|
|
|
|
|
|
│ ├── AdminHandler.cs ← 管理界面请求处理器
|
|
|
|
|
|
|
|
│ └── HtmlBuilder.cs ← 管理页面HTML生成
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
|
|
├── Config/
|
|
|
|
|
|
|
|
│ └── SimulatorConfig.cs ← 配置数据结构 + JSON序列化
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
|
|
├── simulator.json ← 默认配置文件
|
|
|
|
|
|
|
|
└── App.config ← log4net配置
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 项目引用
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
CncSimulator → Newtonsoft.Json(NuGet)
|
|
|
|
|
|
|
|
log4net(NuGet)
|
|
|
|
|
|
|
|
System.Net.Http(框架内置)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
不引用 CncModels / CncRepository / CncService / CncWebApi
|
|
|
|
|
|
|
|
模拟器完全独立,不依赖生产代码。
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 四、配置文件
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 simulator.json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"gatewayPort": 9000,
|
|
|
|
|
|
|
|
"addresses": [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "FANUC-1号模拟",
|
|
|
|
|
|
|
|
"port": 9001,
|
|
|
|
|
|
|
|
"brand": "fanuc",
|
|
|
|
|
|
|
|
"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
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "FANUC-2号模拟",
|
|
|
|
|
|
|
|
"port": 9002,
|
|
|
|
|
|
|
|
"brand": "fanuc",
|
|
|
|
|
|
|
|
"dataChangeInterval": 15,
|
|
|
|
|
|
|
|
"scenarioMode": "auto",
|
|
|
|
|
|
|
|
"devices": [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"deviceCode": "CNC-B002",
|
|
|
|
|
|
|
|
"desc": "B栋2号",
|
|
|
|
|
|
|
|
"initialProgram": "1566.NC",
|
|
|
|
|
|
|
|
"initialPartCount": 80
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"deviceCode": "CNC-005",
|
|
|
|
|
|
|
|
"desc": "验证机床",
|
|
|
|
|
|
|
|
"initialProgram": "TEST001",
|
|
|
|
|
|
|
|
"initialPartCount": 10
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 配置字段说明
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 默认值 | 说明 |
|
|
|
|
|
|
|
|
|------|------|--------|------|
|
|
|
|
|
|
|
|
| gatewayPort | int | 9000 | 总管理页面端口 |
|
|
|
|
|
|
|
|
| addresses | array | - | 采集地址列表 |
|
|
|
|
|
|
|
|
| address.name | string | - | 显示名称 |
|
|
|
|
|
|
|
|
| address.port | int | - | 本地址的监听端口 |
|
|
|
|
|
|
|
|
| address.brand | string | fanuc | 品牌标识(预留扩展) |
|
|
|
|
|
|
|
|
| address.dataChangeInterval | int | 10 | 数据变化频率(秒) |
|
|
|
|
|
|
|
|
| address.scenarioMode | string | auto | auto=自动剧本 / manual=手动触发 |
|
|
|
|
|
|
|
|
| address.devices | array | - | 模拟设备列表 |
|
|
|
|
|
|
|
|
| device.deviceCode | string | - | 设备编码(需与cnc_machine.device_code一致) |
|
|
|
|
|
|
|
|
| device.desc | string | - | 设备描述 |
|
|
|
|
|
|
|
|
| device.initialProgram | string | O0001 | 初始NC程序名 |
|
|
|
|
|
|
|
|
| device.initialPartCount | int | 0 | 初始零件数 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 五、设备状态机与场景
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 设备状态
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
每台设备维护以下内部状态:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
class DeviceState
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
string DeviceCode; // 设备编码(固定)
|
|
|
|
|
|
|
|
string Desc; // 设备描述(固定)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 动态状态
|
|
|
|
|
|
|
|
string CurrentScenario; // 当前场景名
|
|
|
|
|
|
|
|
bool IsOnline; // 是否在线(断电=false)
|
|
|
|
|
|
|
|
string ProgramName; // 当前NC程序名
|
|
|
|
|
|
|
|
int PartCount; // 当前零件数
|
|
|
|
|
|
|
|
int DeviceStatus; // _io_status: 0=离线, 1=在线
|
|
|
|
|
|
|
|
int RunStatus; // 运行状态: 0=待机, 1=运行, 3=加工中
|
|
|
|
|
|
|
|
int OperateMode; // 操作模式: 1=MEM, 10=JOG
|
|
|
|
|
|
|
|
decimal SpindleSpeedSet; // 主轴设定速度
|
|
|
|
|
|
|
|
decimal FeedSpeedSet; // 进给设定速度
|
|
|
|
|
|
|
|
decimal SpindleSpeedActual; // 主轴实际速度
|
|
|
|
|
|
|
|
decimal FeedSpeedActual; // 进给实际速度
|
|
|
|
|
|
|
|
decimal SpindleLoad; // 主轴负载
|
|
|
|
|
|
|
|
decimal SpindleOverride; // 主轴倍率
|
|
|
|
|
|
|
|
decimal PowerOnTime; // 开机累计时间(秒)
|
|
|
|
|
|
|
|
decimal RunTime; // 运行累计时间(秒)
|
|
|
|
|
|
|
|
decimal CuttingTime; // 切削累计时间(秒)
|
|
|
|
|
|
|
|
decimal CycleTime; // 循环时间(秒)
|
|
|
|
|
|
|
|
string MachiningStatus; // 加工状态: G01/G00/G28/等
|
|
|
|
|
|
|
|
string ProgramContent; // 加工程序内容片段
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 剧本控制
|
|
|
|
|
|
|
|
int ScenarioTick; // 当前场景已持续的tick数
|
|
|
|
|
|
|
|
int ScenarioDuration; // 当前场景总tick数
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 场景定义
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 场景名 | 标识 | 字段变化规则 | 典型持续 |
|
|
|
|
|
|
|
|
|--------|------|-------------|---------|
|
|
|
|
|
|
|
|
| 正常加工 | `machining` | part_count +1,主轴有值,run_status=3,cutting_time递增,machining_status=G01 | 20~40个tick |
|
|
|
|
|
|
|
|
| 加工同一零件 | `same_part` | 所有值不变,只有time更新 | 5~10个tick |
|
|
|
|
|
|
|
|
| 待机空闲 | `idle` | part_count不变,主轴实际=0,run_status=0,feed=0,machining_status=G00 | 10~20个tick |
|
|
|
|
|
|
|
|
| 换零件 | `program_change` | program_name变为新值,part_count清零,cycle_time清零 | 1个tick(瞬时) |
|
|
|
|
|
|
|
|
| 手动清零 | `manual_reset` | program_name不变,part_count突然降为0 | 1个tick(瞬时) |
|
|
|
|
|
|
|
|
| 断电 | `power_off` | 设备从返回数组中消失(IsOnline=false) | 5~15个tick |
|
|
|
|
|
|
|
|
| 恢复开机 | `power_on` | 设备重新出现,_io_status从0→1,part_count从0开始 | 1个tick |
|
|
|
|
|
|
|
|
| 暂停加工 | `pause` | run_status=1(运行但非加工),主轴实际=0,feed=0 | 5~10个tick |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 字段变化详细规则
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 正常加工(machining)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
每次tick:
|
|
|
|
|
|
|
|
part_count += 1
|
|
|
|
|
|
|
|
run_status = 3
|
|
|
|
|
|
|
|
device_status = 1
|
|
|
|
|
|
|
|
operate_mode = 1 // MEM模式
|
|
|
|
|
|
|
|
spindle_speed_actual = spindle_speed_set ± 随机波动10%
|
|
|
|
|
|
|
|
feed_speed_actual = feed_speed_set ± 随机波动5%
|
|
|
|
|
|
|
|
spindle_load = 随机 15~45%
|
|
|
|
|
|
|
|
spindle_override = 100
|
|
|
|
|
|
|
|
machining_status = 随机轮换 [G01, G01, G01, G02, G00] // 加工中以G01为主
|
|
|
|
|
|
|
|
cutting_time += dataChangeInterval
|
|
|
|
|
|
|
|
run_time += dataChangeInterval
|
|
|
|
|
|
|
|
power_on_time += dataChangeInterval
|
|
|
|
|
|
|
|
cycle_time += dataChangeInterval
|
|
|
|
|
|
|
|
program_content = 程序内容片段(随program_name变化)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 加工同一零件(same_part)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
每次tick:
|
|
|
|
|
|
|
|
所有字段值不变(包括part_count)
|
|
|
|
|
|
|
|
只有每个tag的time更新为当前时间
|
|
|
|
|
|
|
|
代表:机床还在加工当前零件,上一次采集和这次采集之间零件没完成
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 待机空闲(idle)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
每次tick:
|
|
|
|
|
|
|
|
part_count 不变
|
|
|
|
|
|
|
|
run_status = 0 // 待机
|
|
|
|
|
|
|
|
operate_mode = 10 // JOG模式
|
|
|
|
|
|
|
|
spindle_speed_actual = 0
|
|
|
|
|
|
|
|
feed_speed_actual = 0
|
|
|
|
|
|
|
|
spindle_load = 0
|
|
|
|
|
|
|
|
feed_speed_set = 0
|
|
|
|
|
|
|
|
machining_status = ""
|
|
|
|
|
|
|
|
power_on_time += dataChangeInterval
|
|
|
|
|
|
|
|
run_time += dataChangeInterval // 运行时间照常增加
|
|
|
|
|
|
|
|
// cutting_time 不增加(没有切削)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 换零件(program_change)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
瞬时触发(1个tick):
|
|
|
|
|
|
|
|
旧的 program_name → 新的 program_name
|
|
|
|
|
|
|
|
新程序名从预设列表轮换: ["O0001", "O0002", "1566.NC", "PART-A", "TEST-03"]
|
|
|
|
|
|
|
|
part_count = 0
|
|
|
|
|
|
|
|
cycle_time = 0
|
|
|
|
|
|
|
|
run_status = 3 // 立即开始加工
|
|
|
|
|
|
|
|
machining_status = G01
|
|
|
|
|
|
|
|
spindle_speed_set = 随机 200~800 // 新程序新的速度
|
|
|
|
|
|
|
|
feed_speed_set = 随机 30~150
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 手动清零(manual_reset)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
瞬时触发(1个tick):
|
|
|
|
|
|
|
|
program_name 不变
|
|
|
|
|
|
|
|
part_count 突然降为 0
|
|
|
|
|
|
|
|
run_status = 3
|
|
|
|
|
|
|
|
其他字段不变
|
|
|
|
|
|
|
|
之后进入 normal_machining 场景继续递增
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 断电(power_off)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
触发后:
|
|
|
|
|
|
|
|
该设备不参与JSON数组生成(从返回数据中消失)
|
|
|
|
|
|
|
|
所有内部状态冻结
|
|
|
|
|
|
|
|
采集服务视角:调HTTP接口正常返回,但少了这台设备的数据
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 恢复开机(power_on)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
瞬时触发(1个tick):
|
|
|
|
|
|
|
|
设备重新出现
|
|
|
|
|
|
|
|
device_status (_io_status) = 0 → 1 // 开机瞬间可能先显示0
|
|
|
|
|
|
|
|
part_count = 0 // 重启后清零
|
|
|
|
|
|
|
|
cycle_time = 0
|
|
|
|
|
|
|
|
program_name = 上次断电前的程序名(设备重启不丢程序)
|
|
|
|
|
|
|
|
然后进入 idle 或 machining 场景
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 暂停加工(pause)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
每次tick:
|
|
|
|
|
|
|
|
part_count 不变
|
|
|
|
|
|
|
|
run_status = 1 // 运行但暂停
|
|
|
|
|
|
|
|
spindle_speed_actual = 0
|
|
|
|
|
|
|
|
feed_speed_actual = 0
|
|
|
|
|
|
|
|
spindle_load = 0
|
|
|
|
|
|
|
|
machining_status = ""
|
|
|
|
|
|
|
|
power_on_time += dataChangeInterval
|
|
|
|
|
|
|
|
run_time += dataChangeInterval
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.4 自动剧本
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
自动模式下,每台设备按以下顺序循环场景:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
剧本循环(每个地址的设备剧本独立运行):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[正常加工] × 随机20~40个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[加工同一零件] × 随机5~10个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[正常加工] × 随机10~20个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[换零件] × 1个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[正常加工] × 随机15~30个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[暂停加工] × 随机5~10个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[正常加工] × 随机10~15个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[加工同一零件] × 随机3~5个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[手动清零] × 1个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[正常加工] × 随机20~30个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[待机空闲] × 随机10~20个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[正常加工] × 随机15~25个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[断电] × 随机5~15个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
[恢复开机] × 1个tick
|
|
|
|
|
|
|
|
↓
|
|
|
|
|
|
|
|
回到开头
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
每个tick间隔 = dataChangeInterval(秒)
|
|
|
|
|
|
|
|
每台设备的剧本随机偏移,避免所有设备同步变化
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 5.5 网络异常模拟
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
网络异常不是设备状态,而是**整个地址级别**的事件。通过管理界面手动触发。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 异常类型 | 标识 | 模拟方式 | 持续 |
|
|
|
|
|
|
|
|
|---------|------|---------|------|
|
|
|
|
|
|
|
|
| 正常 | `normal` | 正常返回JSON | - |
|
|
|
|
|
|
|
|
| HTTP 500 | `http500` | 返回 HTTP 500 + 纯文本错误信息 | 手动恢复 |
|
|
|
|
|
|
|
|
| 连接超时 | `timeout` | 收到请求后延迟60秒才返回(模拟网络慢) | 手动恢复 |
|
|
|
|
|
|
|
|
| 拒绝连接 | `refuse` | 停止HttpListener,端口不通 | 手动恢复 |
|
|
|
|
|
|
|
|
| 空数组 | `empty` | 返回 `[]`,正常HTTP 200 | 手动恢复 |
|
|
|
|
|
|
|
|
| 畸形JSON | `malformed` | 返回 `{broken`,HTTP 200 | 手动恢复 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 六、FANUC数据模板
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 Tag结构
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
基于示例数据,每台设备返回18个Tag:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Tag ID | desc | value格式 | 标准字段映射 |
|
|
|
|
|
|
|
|
|--------|------|----------|-------------|
|
|
|
|
|
|
|
|
| `_io_status` | 设备状态 | `"1.00000"` / `"0.00000"` | device_status |
|
|
|
|
|
|
|
|
| `Tag2` | 当前轴数 | `"4.00000"` | (非标准字段,extra_data) |
|
|
|
|
|
|
|
|
| `Tag5` | 执行的NC主程序名 | `"1566.NC"` / `"O1"` | program_name |
|
|
|
|
|
|
|
|
| `Tag6` | 执行的NC主程序号 | `"N0"` / `"N20"` | (非标准字段) |
|
|
|
|
|
|
|
|
| `Tag7` | 当前加工程序内容 | 多行文本 | (非标准字段) |
|
|
|
|
|
|
|
|
| `Tag8` | 当前加工零件数 | `"1219.00000"` | part_count |
|
|
|
|
|
|
|
|
| `Tag9` | 运行状态 | `"0.00000"` / `"3.00000"` | run_status |
|
|
|
|
|
|
|
|
| `Tag11` | 操作模式 | `"1.00000"` / `"10.00000"` | operate_mode |
|
|
|
|
|
|
|
|
| `Tag14` | 当前主轴倍率 | `"100.00000"` | spindle_override |
|
|
|
|
|
|
|
|
| `Tag17` | 主轴设定速度 | `"300.00000"` | spindle_speed_set |
|
|
|
|
|
|
|
|
| `Tag18` | 进给设定速度 | `"60.00000"` | feed_speed_set |
|
|
|
|
|
|
|
|
| `Tag19` | 主轴实际速度 | `"450.00000"` | spindle_speed_actual |
|
|
|
|
|
|
|
|
| `Tag20` | 进给实际转速 | `"60.00000"` | feed_speed_actual |
|
|
|
|
|
|
|
|
| `Tag21` | 主轴负载 | `"25.00000"` | spindle_load |
|
|
|
|
|
|
|
|
| `Tag22` | 开机时间 | `"23558160.00000"` | power_on_time |
|
|
|
|
|
|
|
|
| `Tag23` | 运行时间 | `"18224.00000"` | run_time |
|
|
|
|
|
|
|
|
| `Tag24` | 切削时间 | `"6848959.00000"` | cutting_time |
|
|
|
|
|
|
|
|
| `Tag25` | 循环时间 | `"699.00000"` | cycle_time |
|
|
|
|
|
|
|
|
| `Tag26` | 加工状态 | `"G01"` | machining_status |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 时间处理
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
每个tag的time字段:
|
|
|
|
|
|
|
|
基准时间 = DateTime.Now
|
|
|
|
|
|
|
|
每个tag在基准时间上随机偏移 -5秒 ~ 0秒
|
|
|
|
|
|
|
|
格式: "yyyy-MM-dd HH:mm:ss"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
|
|
Tag5 time = "2026-04-30 14:30:25" ← 偏移-5秒
|
|
|
|
|
|
|
|
Tag8 time = "2026-04-30 14:30:28" ← 偏移-2秒
|
|
|
|
|
|
|
|
Tag26 time = "2026-04-30 14:30:30" ← 无偏移
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 数值格式
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
数字类型Tag的value格式: "{值}.00000"
|
|
|
|
|
|
|
|
整数值: "1219.00000", "0.00000", "25.00000"
|
|
|
|
|
|
|
|
无小数的真实值加固定5位小数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
字符串类型Tag的value格式: 原样
|
|
|
|
|
|
|
|
"1566.NC", "G01", "N20"
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 6.4 JSON输出示例
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"device": "CNC-A001",
|
|
|
|
|
|
|
|
"desc": "西栋1号",
|
|
|
|
|
|
|
|
"tags": [
|
|
|
|
|
|
|
|
{ "id": "_io_status", "desc": "设备状态", "quality": "0", "value": "1.00000", "time": "2026-04-30 14:30:30" },
|
|
|
|
|
|
|
|
{ "id": "Tag2", "desc": "当前轴数", "quality": "0", "value": "4.00000", "time": "2026-04-30 14:30:28" },
|
|
|
|
|
|
|
|
{ "id": "Tag5", "desc": "执行的NC主程序名", "quality": "0", "value": "O0001", "time": "2026-04-30 14:30:27" },
|
|
|
|
|
|
|
|
{ "id": "Tag6", "desc": "执行的NC主程序号", "quality": "0", "value": "N0", "time": "2026-04-30 14:30:27" },
|
|
|
|
|
|
|
|
{ "id": "Tag7", "desc": "当前加工程序内容", "quality": "0", "value": "<O0001>\nG40G49G80\n( SIMULATOR )", "time": "2026-04-30 14:30:27" },
|
|
|
|
|
|
|
|
{ "id": "Tag8", "desc": "当前加工零件数", "quality": "0", "value": "53.00000", "time": "2026-04-30 14:30:27" },
|
|
|
|
|
|
|
|
{ "id": "Tag9", "desc": "运行状态", "quality": "0", "value": "3.00000", "time": "2026-04-30 14:30:28" },
|
|
|
|
|
|
|
|
{ "id": "Tag11", "desc": "操作模式", "quality": "0", "value": "1.00000", "time": "2026-04-30 14:30:28" },
|
|
|
|
|
|
|
|
{ "id": "Tag14", "desc": "当前主轴倍率", "quality": "0", "value": "100.00000", "time": "2026-04-30 14:30:28" },
|
|
|
|
|
|
|
|
{ "id": "Tag17", "desc": "主轴设定速度", "quality": "0", "value": "450.00000", "time": "2026-04-30 14:30:28" },
|
|
|
|
|
|
|
|
{ "id": "Tag18", "desc": "进给设定速度", "quality": "0", "value": "60.00000", "time": "2026-04-30 14:30:28" },
|
|
|
|
|
|
|
|
{ "id": "Tag19", "desc": "主轴实际速度", "quality": "0", "value": "448.00000", "time": "2026-04-30 14:30:29" },
|
|
|
|
|
|
|
|
{ "id": "Tag20", "desc": "进给实际转速", "quality": "0", "value": "58.00000", "time": "2026-04-30 14:30:29" },
|
|
|
|
|
|
|
|
{ "id": "Tag21", "desc": "主轴负载", "quality": "0", "value": "32.00000", "time": "2026-04-30 14:30:29" },
|
|
|
|
|
|
|
|
{ "id": "Tag22", "desc": "开机时间", "quality": "0", "value": "23559020.00000", "time": "2026-04-30 14:30:29" },
|
|
|
|
|
|
|
|
{ "id": "Tag23", "desc": "运行时间", "quality": "0", "value": "18384.00000", "time": "2026-04-30 14:30:29" },
|
|
|
|
|
|
|
|
{ "id": "Tag24", "desc": "切削时间", "quality": "0", "value": "6849019.00000", "time": "2026-04-30 14:30:30" },
|
|
|
|
|
|
|
|
{ "id": "Tag25", "desc": "循环时间", "quality": "0", "value": "759.00000", "time": "2026-04-30 14:30:30" },
|
|
|
|
|
|
|
|
{ "id": "Tag26", "desc": "加工状态", "quality": "0", "value": "G01", "time": "2026-04-30 14:30:30" }
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 七、管理界面设计
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 总管理页面(http://localhost:9000/admin)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
|
|
│ CNC 模拟采集服务 [全部启动] [全部停止] │
|
|
|
|
|
|
|
|
├─────────────────────────────────────────────────────────────┤
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 地址列表 ──────────────────────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
|
|
│ │ 名称 端口 状态 设备数 数据频率 操作 │ │
|
|
|
|
|
|
|
|
│ │ FANUC-1号模拟 9001 ●运行中 3台 10秒 [管理] │ │
|
|
|
|
|
|
|
|
│ │ FANUC-2号模拟 9002 ○已停止 2台 15秒 [管理] │ │
|
|
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 控制台日志 ────────────────────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ 14:30:30 [9001] 返回3台设备, 总耗时5ms │ │
|
|
|
|
|
|
|
|
│ │ 14:30:28 [9002] 已停止 │ │
|
|
|
|
|
|
|
|
│ │ 14:30:20 [9001] 返回3台设备, 总耗时4ms │ │
|
|
|
|
|
|
|
|
│ │ ... │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 单地址管理页面(http://localhost:9001/admin)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
|
|
│ FANUC-1号模拟 (端口 9001) [启动] [停止] │
|
|
|
|
|
|
|
|
├─────────────────────────────────────────────────────────────┤
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 全局设置 ──────────────────────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ 数据变化频率: [====10====] 秒 │ │
|
|
|
|
|
|
|
|
│ │ 剧本模式: (●) 自动循环 ( ) 手动触发 │ │
|
|
|
|
|
|
|
|
│ │ 网络异常模拟: [正常 ▼] │ │
|
|
|
|
|
|
|
|
│ │ 正常 / HTTP 500 / 连接超时 / 拒绝连接 │ │
|
|
|
|
|
|
|
|
│ │ / 返回空数组 / 畸形JSON │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 设备状态卡片 ──────────────────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
|
|
│ │ ┌ CNC-A001 西栋1号 ──────────┐ │ │
|
|
|
|
|
|
|
|
│ │ │ 场景: 正常加工 │ [换零件] [手动清零] │ │
|
|
|
|
|
|
|
|
│ │ │ 程序: O0001 │ [断电] [暂停] │ │
|
|
|
|
|
|
|
|
│ │ │ 零件数: 53 │ [恢复] │ │
|
|
|
|
|
|
|
|
│ │ │ 运行状态: 3 (加工中) │ │ │
|
|
|
|
|
|
|
|
│ │ │ 主轴: 448/450 负载32% │ │ │
|
|
|
|
|
|
|
|
│ │ └────────────────────────────┘ │ │
|
|
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
|
|
│ │ ┌ CNC-006 6号机床 ────────────┐ │ │
|
|
|
|
|
|
|
|
│ │ │ 场景: 待机空闲 │ [换零件] [手动清零] │ │
|
|
|
|
|
|
|
|
│ │ │ 程序: O0002 │ [断电] [暂停] │ │
|
|
|
|
|
|
|
|
│ │ │ 零件数: 120 │ [恢复] │ │
|
|
|
|
|
|
|
|
│ │ │ 运行状态: 0 (待机) │ │ │
|
|
|
|
|
|
|
|
│ │ │ 主轴: 0/0 负载0% │ │ │
|
|
|
|
|
|
|
|
│ │ └────────────────────────────┘ │ │
|
|
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
|
|
│ │ ┌ CNC-008 8号机床 ────────────┐ │ │
|
|
|
|
|
|
|
|
│ │ │ 场景: 断电 (已断电3个tick) │ [恢复] │ │
|
|
|
|
|
|
|
|
│ │ │ (设备离线,不返回数据) │ │ │
|
|
|
|
|
|
|
|
│ │ └────────────────────────────┘ │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 当前返回JSON预览 ─────────────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ [ │ │
|
|
|
|
|
|
|
|
│ │ { "device": "CNC-A001", "desc": "西栋1号", ... }, │ │
|
|
|
|
|
|
|
|
│ │ { "device": "CNC-006", "desc": "6号机床", ... } │ │
|
|
|
|
|
|
|
|
│ │ ] │ │
|
|
|
|
|
|
|
|
│ │ ↑ 每次 tick 自动刷新 │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 返回数据日志(最近100条)──────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ # 时间 设备数 关键变化 │ │
|
|
|
|
|
|
|
|
│ │ 100 14:30:30 2 CNC-A001 零件53, 程序O0001 │ │
|
|
|
|
|
|
|
|
│ │ 99 14:30:20 2 CNC-A001 零件52, 程序O0001 │ │
|
|
|
|
|
|
|
|
│ │ 98 14:30:10 2 CNC-008 断电消失 │ │
|
|
|
|
|
|
|
|
│ │ 97 14:30:10 3 CNC-A001 零件51, 程序O0001 │ │
|
|
|
|
|
|
|
|
│ │ ... │ │
|
|
|
|
|
|
|
|
│ │ [点击展开完整JSON] │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ ┌ 统计 ─────────────────────────────────────────────────┐ │
|
|
|
|
|
|
|
|
│ │ 总请求次数: 256 │ │
|
|
|
|
|
|
|
|
│ │ 成功次数: 253 失败次数: 3 (网络异常模拟) │ │
|
|
|
|
|
|
|
|
│ │ 平均响应时间: 4ms │ │
|
|
|
|
|
|
|
|
│ │ 本次启动时长: 42分30秒 │ │
|
|
|
|
|
|
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 管理界面技术实现
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
HTML生成方式:
|
|
|
|
|
|
|
|
服务端拼接HTML字符串(HtmlBuilder.cs)
|
|
|
|
|
|
|
|
不使用任何前端框架
|
|
|
|
|
|
|
|
CSS内联,JavaScript内联
|
|
|
|
|
|
|
|
页面通过 setInterval 每2秒 AJAX 刷新状态
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AJAX接口:
|
|
|
|
|
|
|
|
GET /admin/api/status → 所有设备当前状态
|
|
|
|
|
|
|
|
GET /admin/api/logs?page=1 → 日志列表
|
|
|
|
|
|
|
|
POST /admin/api/start → 启动
|
|
|
|
|
|
|
|
POST /admin/api/stop → 停止
|
|
|
|
|
|
|
|
POST /admin/api/interval?value=15 → 修改数据变化频率
|
|
|
|
|
|
|
|
POST /admin/api/event → 触发手动事件
|
|
|
|
|
|
|
|
body: { deviceId: "CNC-A001", eventType: "program_change" }
|
|
|
|
|
|
|
|
POST /admin/api/network → 设置网络异常类型
|
|
|
|
|
|
|
|
body: { type: "http500" }
|
|
|
|
|
|
|
|
POST /admin/api/mode → 切换剧本模式
|
|
|
|
|
|
|
|
body: { mode: "manual" }
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 八、日志记录
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 文件日志
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
logs/simulator-2026-04-30.log
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[14:30:30 INFO ] [9001] 返回请求: 3台设备, 耗时4ms
|
|
|
|
|
|
|
|
[14:30:30 DEBUG] [9001] 完整JSON: [{"device":"CNC-A001","desc":"西栋1号","tags":[...]}, ...]
|
|
|
|
|
|
|
|
[14:30:30 INFO ] [9001] 关键数据: CNC-A001(P=53,Prog=O0001,Run=3) CNC-006(P=120,Prog=O0002,Run=0)
|
|
|
|
|
|
|
|
[14:30:20 INFO ] [9001] 返回请求: 3台设备, 耗时5ms
|
|
|
|
|
|
|
|
[14:30:20 INFO ] [9001] 关键数据: CNC-A001(P=52,Prog=O0001,Run=3) CNC-006(P=120,Prog=O0002,Run=0)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 日志级别
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 级别 | 内容 |
|
|
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
|
|
| INFO | 每次请求摘要(设备数、耗时) |
|
|
|
|
|
|
|
|
| DEBUG | 完整JSON响应(默认关闭,调试时开启) |
|
|
|
|
|
|
|
|
| WARN | 场景切换事件(换零件、断电、恢复等) |
|
|
|
|
|
|
|
|
| ERROR | 网络异常模拟触发 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 8.3 关键数据摘要格式
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
设备编码(P=零件数,Prog=程序名,Run=运行状态,Status=设备状态)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
|
|
CNC-A001(P=53,Prog=O0001,Run=3,St=1) CNC-006(P=120,Prog=O0002,Run=0,St=1)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 8.4 内存环形缓冲
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
管理界面的日志列表不查文件,从内存环形缓冲读取:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
RingBuffer<LogEntry>
|
|
|
|
|
|
|
|
容量: 200条
|
|
|
|
|
|
|
|
每条记录: { timestamp, addressPort, deviceCount, keyData, fullJson, duration }
|
|
|
|
|
|
|
|
溢出时覆盖最旧的记录
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 九、数据流时序
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 正常流程
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
采集服务 CncSimulator(:9001)
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ GET / │
|
|
|
|
|
|
|
|
│ ───────────────────────────→ │
|
|
|
|
|
|
|
|
│ │ ← ScenarioPlayer tick(按interval)
|
|
|
|
|
|
|
|
│ │ ← 每台DeviceSimulator生成当前状态
|
|
|
|
|
|
|
|
│ │ ← FanucDataGenerator组装JSON
|
|
|
|
|
|
|
|
│ │ ← LogRecorder记录日志
|
|
|
|
|
|
|
|
│ HTTP 200 + JSON Array │
|
|
|
|
|
|
|
|
│ ←─────────────────────────── │
|
|
|
|
|
|
|
|
│ │
|
|
|
|
|
|
|
|
│ (等30秒 collect_interval) │
|
|
|
|
|
|
|
|
│ │ ← ScenarioPlayer tick
|
|
|
|
|
|
|
|
│ │ ← 数据变化(part_count+1等)
|
|
|
|
|
|
|
|
│ GET / │
|
|
|
|
|
|
|
|
│ ───────────────────────────→ │
|
|
|
|
|
|
|
|
│ HTTP 200 + 新JSON │
|
|
|
|
|
|
|
|
│ ←─────────────────────────── │
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 9.2 断电场景
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
Tick N-1: 返回 [CNC-A001, CNC-006, CNC-008] ← 3台设备正常
|
|
|
|
|
|
|
|
Tick N: ScenarioPlayer触发 CNC-008 断电
|
|
|
|
|
|
|
|
返回 [CNC-A001, CNC-006] ← CNC-008消失
|
|
|
|
|
|
|
|
日志: [WARN] CNC-008 断电,从返回数据中移除
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
采集服务视角:
|
|
|
|
|
|
|
|
第N-1次采集: 3台设备,CNC-008 last_program_name = O0003
|
|
|
|
|
|
|
|
第N次采集: 2台设备,CNC-008 不在列表中
|
|
|
|
|
|
|
|
→ CNC-008 不更新 last_collect_time,状态停留在上次
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 9.3 换零件场景
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
Tick N-1: CNC-A001 part_count=52, program_name=O0001
|
|
|
|
|
|
|
|
Tick N: ScenarioPlayer触发 CNC-A001 换零件
|
|
|
|
|
|
|
|
CNC-A001: program_name=O0002, part_count=0
|
|
|
|
|
|
|
|
返回数据中 CNC-A001 的 Tag5="O0002", Tag8="0.00000"
|
|
|
|
|
|
|
|
日志: [WARN] CNC-A001 换零件: O0001 → O0002, part_count 52 → 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
采集服务视角:
|
|
|
|
|
|
|
|
→ 检测到 program_name 变化
|
|
|
|
|
|
|
|
→ 结账旧段(O0001, qty=52)
|
|
|
|
|
|
|
|
→ 开新段(O0002, start=0)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 十、扩展其他品牌
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 10.1 品牌生成器接口
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
interface IBrandGenerator
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/// <summary>品牌标识(配置文件用)</summary>
|
|
|
|
|
|
|
|
string BrandKey { get; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>根据设备状态生成一个设备的JSON对象</summary>
|
|
|
|
|
|
|
|
/// <param name="state">设备当前状态</param>
|
|
|
|
|
|
|
|
/// <returns>Newtonsoft.Json.Linq.JObject(一个设备对象,含device/desc/tags)</returns>
|
|
|
|
|
|
|
|
JObject GenerateDevice(DeviceState state);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 10.2 新增品牌步骤
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
1. 创建 {BrandName}DataGenerator.cs,实现 IBrandGenerator
|
|
|
|
|
|
|
|
2. 在 SimulatorEngine 中注册新品牌生成器
|
|
|
|
|
|
|
|
3. simulator.json 中 brand 字段填新品牌标识
|
|
|
|
|
|
|
|
4. 设备状态机共用,只是输出JSON格式不同
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 10.3 预留扩展
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 品牌 | JSON结构差异 | Tag体系 |
|
|
|
|
|
|
|
|
|------|-------------|---------|
|
|
|
|
|
|
|
|
| FANUC | device/desc/tags 数组 | Tag5=程序名, Tag8=零件数, ... |
|
|
|
|
|
|
|
|
| Siemens | 待确认 | 可能是不同的tag命名 |
|
|
|
|
|
|
|
|
| Mitsubishi | 待确认 | 可能是不同的tag命名 |
|
|
|
|
|
|
|
|
| 兄弟 | 待确认 | 可能是不同的tag命名 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
不同品牌的差异仅在 `IBrandGenerator.GenerateDevice()` 中处理,状态机、剧本、管理界面完全复用。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 十一、启动与运行
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 11.1 控制台输出
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
CNC 模拟采集服务 v1.0
|
|
|
|
|
|
|
|
================================================
|
|
|
|
|
|
|
|
加载配置: simulator.json
|
|
|
|
|
|
|
|
- 地址1: FANUC-1号模拟 (:9001) 3台设备
|
|
|
|
|
|
|
|
- 地址2: FANUC-2号模拟 (:9002) 2台设备
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
启动服务...
|
|
|
|
|
|
|
|
[✓] 总管理页面: http://localhost:9000/admin
|
|
|
|
|
|
|
|
[✓] FANUC-1号模拟: http://localhost:9001/ (管理: http://localhost:9001/admin)
|
|
|
|
|
|
|
|
[✓] FANUC-2号模拟: http://localhost:9002/ (管理: http://localhost:9002/admin)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
按任意键退出...
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 11.2 运行时控制台
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
14:30:30 [9001] GET / → 3台设备, 4ms
|
|
|
|
|
|
|
|
14:30:30 [9001] 场景: CNC-A001 正常加工(P=53) CNC-006 待机(P=120) CNC-008 断电
|
|
|
|
|
|
|
|
14:30:35 [9001] GET / → 3台设备, 3ms
|
|
|
|
|
|
|
|
14:30:40 [9001] GET / → 2台设备, 4ms ← CNC-008 断电消失
|
|
|
|
|
|
|
|
14:30:45 [9002] GET / → 2台设备, 5ms
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 11.3 退出
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
按任意键后:
|
|
|
|
|
|
|
|
停止所有HttpListener...
|
|
|
|
|
|
|
|
保存日志...
|
|
|
|
|
|
|
|
已退出。
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 十二、开发顺序
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 步骤 | 内容 | 可验证结果 |
|
|
|
|
|
|
|
|
|------|------|-----------|
|
|
|
|
|
|
|
|
| 1 | 项目骨架 + HttpListener + 返回固定JSON | 浏览器访问localhost:9001看到固定JSON |
|
|
|
|
|
|
|
|
| 2 | DeviceSimulator 状态机 + ScenarioPlayer | 每次请求返回不同数据(part_count递增) |
|
|
|
|
|
|
|
|
| 3 | FanucDataGenerator 完整Tag生成 | 返回18个Tag,格式与示例一致 |
|
|
|
|
|
|
|
|
| 4 | 所有场景实现(断电/换零件/手动清零等) | 剧本自动循环 |
|
|
|
|
|
|
|
|
| 5 | 管理界面HTML + AJAX | 浏览器操作启停、查看日志 |
|
|
|
|
|
|
|
|
| 6 | 多端口支持 + 总管理页面 | 同时模拟2个地址 |
|
|
|
|
|
|
|
|
| 7 | 网络异常模拟 | 手动触发HTTP 500/超时等 |
|
|
|
|
|
|
|
|
| 8 | 日志记录(文件+内存缓冲) | 日志文件可查、管理界面可看 |
|
|
|
|
|
|
|
|
| 9 | 配置文件读写 | 修改simulator.json后重启生效 |
|