Compare commits

...

10 Commits

Author SHA1 Message Date
Tony Cao
c3d9fb3708 Cargo.lock: io-uring feature deps 2026-08-12 23:17:35 +08:00
Tony Cao
9db5cf4504 Full RT link verification report: methodology, metric analysis, 3h soak, RTT optimization, DC-Synchron findings 2026-08-12 22:01:42 +08:00
Tony Cao
4083df04fb RTT optimization: 8kHz support, io_uring backend option (tested slower), RT-promote tx_rx thread; 8kHz RTT min 0.75ms mean 1.04ms 2026-08-12 21:47:50 +08:00
Tony Cao
d95973ab68 Soak sampler: add atomic nMatchLag freshness monitoring; add lag sampling script 2026-08-12 17:39:50 +08:00
Tony Cao
253f4b3cf7 J1900-side DC-Synchron: EL6695 secondary firmware sync_mode=2 via CoE, DC telemetry, verified 180s zero sync errors 2026-08-10 01:56:41 +08:00
Tony Cao
cdb90c3e9a Extend async pipelined FPWR to 800B: 4kHz spin loop 2040->2960 cycles/s 2026-08-10 01:28:43 +08:00
Tony Cao
aabfe31526 Enable EL6695 DC-Synchron mode (AssignActivate 0x320)
- DC selection must use the structured <Dc><Data AssignActivate/> XSD
  element; legacy DcData hex writes get normalized to zeros by TwinCAT
- TwinCAT converts it to DcData '2003...' (0x0320) on save: SYNC0/SYNC1
  cyclic + SYNC0 activate for the bridge
- Verified: clean activation, ticker OP, bSync=True, 90k cycles 0 errors,
  timing unchanged (jitter 7.3us mean)
- docs: pitfall #10 (DC selection method)
2026-08-09 15:32:56 +08:00
Tony Cao
3c4b6e6d54 D2000 (aarch64) migration: cross-build works with zero code changes
- .cargo/config.toml: rust-lld linker pinned for x86_64 + aarch64 musl
  targets (RUSTFLAGS no longer needed)
- scripts/d2000-deploy.ps1: build -> AArch64 ELF check -> scp deploy
- docs/d2000-dev-plan.md: M2 done; rustup mirror gotcha documented
  (cached channel manifest pins absolute mirror URLs; patch it to
  static.rust-lang.org when a component 404s)
- verified: target/aarch64-unknown-linux-musl/release/el6695-rt is a
  static AArch64 ELF (e_machine=0xB7), ~1MB
2026-08-06 02:05:05 +08:00
Tony Cao
f0163d8d70 800B PDO variant + long-term stability evidence + analysis docs
- twin_layout_blob_sized generalized: multi-entry TwinCAT line format
  (1 real @240bit + N continuation + remainder entry); 1024B path kept
  byte-identical; 800B = 27 entries verified on hardware
- 800B constants (PAYLOAD/SM 800), deployed as el6695_rt_800b
- 10-min + 5h stability runs: 18M cycles, zero errors, zero mismatches,
  PLC nMismatch frozen over the whole run (monitor_5h.csv evidence)
- docs: 800B plan/design/verification/test-report chapters,
  jitter analysis (NFS rootfs stall primary cause), D2000 dev plan
- scripts: 800B deploy/relink/verify, mismatch-delta, 5h monitor
2026-08-06 01:10:50 +08:00
Tony Cao
8c1f519526 Async pipelined FPWR + 4kHz: exec 245us->24us, RTT 2ms->1.25-1.5ms
- FPWR fire-and-forget: two alternating leaked tx slots, or()-driven
  20us kick to reach sendable, poll_once reap after next spin-detect
  (late=0 over 350k cycles). Fixes: reap placement, poll_once kick
- exec drops to 24us mean, freeing budget for 4kHz (250us) operation
- 4kHz verified: TwinCAT PLC at 4001.8Hz, lag 5-6 cycles
  (RTT 1.25-1.5ms); J1900 effective 2.9kHz - FPRD poll cost (71us x N)
  exceeds 250us budget, occasional frame miss
- Conclusion: J1900 write wait never contributed to lag; the 4-stage
  pipeline is TwinCAT IO mapping + bridge forwarding
2026-07-28 01:18:05 +08:00
36 changed files with 1913 additions and 27 deletions

5
.cargo/config.toml Normal file
View File

@ -0,0 +1,5 @@
[target.x86_64-unknown-linux-musl]
linker = "rust-lld"
[target.aarch64-unknown-linux-musl]
linker = "rust-lld"

12
Cargo.lock generated
View File

@ -382,6 +382,7 @@ dependencies = [
"ethercrab-wire",
"futures-lite",
"heapless",
"io-uring",
"libc",
"lock_api",
"log",
@ -508,6 +509,17 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "io-uring"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d64d8ca234d152948ceaede1f419b6a83983a5ecccaac05fb337a809c96d3aa6"
dependencies = [
"bitflags 2.13.1",
"cfg-if",
"libc",
]
[[package]]
name = "ipnetwork"
version = "0.20.0"

View File

@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
description = "J1900 <-> TwinCAT 1 kHz communication verification over an EL6695 EtherCAT bridge"
[dependencies]
ethercrab = { version = "0.7", default-features = false, features = ["std"] }
ethercrab = { version = "0.7", default-features = false, features = ["std", "io-uring"] }
futures-lite = "2"
libc = "0.2"
crc32fast = "1.4"

BIN
deploy_800b_dc.log Normal file

Binary file not shown.

BIN
deploy_800b_dc2.log Normal file

Binary file not shown.

BIN
deploy_800b_elevated.log Normal file

Binary file not shown.

BIN
deploy_800b_regress.log Normal file

Binary file not shown.

BIN
deploy_800b_regress2.log Normal file

Binary file not shown.

BIN
deploy_800b_regress3.log Normal file

Binary file not shown.

124
docs/800b-pdo-plan.md Normal file
View File

@ -0,0 +1,124 @@
# EL6695 PDO 扩展至 800 字节 — 方案文档
> **版本**: 1.0
> **日期**: 2026-07-27
> **状态**: 待评审
> **前置文档**: `8b-bridge-design.md`(8B 实现)、`500us-latency-plan.md`(延迟优化)
---
## 1. 目标
在现有 8B 工程基础上,新建 **800 字节 PDO** 变体工程 `TwinCAT_EL6695_Primary_800B`,实现 TwinCAT ↔ J1900 双向 800B/周期数据交换,并保持逐字节可验证性。
选型说明:800B 介于 8B(验证通路)与 1024B(ESI 默认上限)之间,可作为实际应用的带宽档位;同时验证任意尺寸的映射 blob 生成规则。
---
## 2. 关键技术点(复用既有经验)
| # | 经验 | 应用到 800B |
|---|---|---|
| 1 | 链接必须整数组(`LinkVariables`) | 同 8B,PLC 数组与 PDO 条目同类型同尺寸 |
| 2 | 类型 GUID 必须复用 PLC 构建生成的随机 GUID | 先建 PLC `ARRAY[0..799]`,构建取 GUID 填 PDO 条目 |
| 3 | SM3 不挂 0x1A01 诊断 PDO,否则设备报 `size not allowed` | SM3 纯 800B,不含诊断(若含诊断则需 802B) |
| 4 | EL6695 固件按 PDO 赋值总和校验 SM 长度 | SM2=SM3=800(0x0320),两侧一致 |
| 5 | 映射 blob 须为 TwinCAT 行格式(240bit 条目 + 余量) | 见 §3 数学 |
| 6 | 先激活 TwinCAT,再启动 J1900 ticker(同一会话) | 部署顺序不变 |
---
## 3. PDO 映射 blob 数学(800B)
EL6695 固件要求映射为 TwinCAT 行格式:真实对象条目 @240bit + 若干 0x0000:00 延续条目,总位数 = payload 位数。
```
800 B = 6400 bit = 26 × 240 bit + 160 bit
blob = [条目数=27 (u16 LE)]
+ [240, 1, obj_lo, obj_hi] (真实对象 0x6000/0x7000 子索引1)
+ 25 × [240, 0, 0, 0] (延续)
+ 1 × [160, 0, 0, 0] (余量延续)
```
`bridge.rs``twin_layout_blob_sized` 需泛化:`payload_bytes ≤ 30` 走单条目;否则按 `ceil` 生成多条目(1 真实 + (n-2) 延续 + 1 余量延续),1024B 时退化为现有 35 条目格式(字节级兼容已验证)。
---
## 4. 工程结构
```
twincat3-auto-cli/
├─ TwinCAT_EL6695_Primary\ (8B, 不动)
├─ TwinCAT_EL6695_Primary_2kHz\ (2kHz, 不动)
├─ TwinCAT_EL6695_Primary_4kHz\ (4kHz, 不动)
└─ TwinCAT_EL6695_Primary_800B\ (新建, 本方案)
```
J1900 二进制: `/root/gateway/el6695_rt_800b`(新构建,旧的全部保留)。
---
## 5. 实施步骤
### 5.1 TwinCAT 侧(副本工程)
| 步骤 | 内容 |
|---|---|
| 1 | robocopy 复制 `TwinCAT_EL6695_Primary``_800B`;修 sln 为相对路径(防绝对路径 bug) |
| 2 | PLC: `GVL_Sync.aIn1/aOut1``ARRAY[0..799] OF BYTE`;MAIN 协议改 800B 布局(§6) |
| 3 | 构建 PLC → 从 tsproj 取 `ARRAY [0..799]` 的随机 GUID |
| 4 | tsproj PDO 条目(0x1608/0x1A08)类型替换为该 GUID 的 `ARRAY [0..799] OF BYTE` |
| 5 | SM2 记录: `001c200364...`(len=0x0320=800);SM3 记录: `008e200320...`(len=800),0x1A01 仍不挂 SM3 |
| 6 | `tc-relink.ps1` 整数组重建链接;部署激活;验证 _Boot tszip 中 SM2/SM3=800 |
### 5.2 J1900 侧
| 步骤 | 内容 |
|---|---|
| 1 | `twin_layout_blob_sized` 泛化为多条目(§3) |
| 2 | 常量: `PAYLOAD_LEN=SM_TX_LEN=SM_RX_LEN=SM_RX_PLD_LEN=800` |
| 3 | SM 长度写 800;FMMU len 写 800 |
| 4 | ticker 走 1KB 式协议路径(f(x)=x·7+3 + 图案回显 + 时间戳字段),现有编译期守卫 `PAYLOAD_LEN>=256/280` 自动生效 |
| 5 | 交叉编译(rust-lld)→ scp 为 `el6695_rt_800b` |
### 5.3 数据协议(800B 布局)
| 偏移 | 内容 | 方向 |
|---|---|---|
| [0..7] | TwinCAT `nCycles`(ULINT) → J1900 回 `nCycles*7+3` | 双向 |
| [8..15] | J1900 本地周期号 | J→T |
| [16..799] | 逐字节图案 `(nCycles + i) mod 256`,J1900 原样回显 | 双向 |
| [264..267] | J1900 周期抖动 ns | J→T |
| [268..271] | J1900 图案失配计数 | J→T |
| [272..279] | J1900 回存的原始 tc_seq | J→T |
TwinCAT 校验(复用 1KB 逻辑):从 `nProcessedSeq` 反推原始 seq → 按 seq 重建预期图案逐字节比对 [16..799] → `bSync`;RTT = nCycles nReturnedSeq。
### 5.4 验证
1. J1900 日志:SM2/SM3 len=800、OP、stale=0%、mism=0;
2. TwinCAT ADS: `bSync=True`、`aIn1` 非零字节数 ≈ 784(图案区)、RTT 稳定;
3. 逐字节证据:抽 [16..799] 中多个偏移核对 `(seq+i) mod 256`;
4. 连续 10 分钟:nMismatch 零增长、tx/rx 零错误;
5. 频率:保持 1kHz(带宽目标,不引入 2k/4k 变量)。
---
## 6. 风险与注意点
| # | 风险 | 缓解 |
|---|---|---|
| R1 | 多条目 blob 被固件拒绝(余量条目不认) | 先用 verify 模式观察 AL 状态码;备选:全部按 240bit 进位到 27×240(6480bit,多 80bit 填 0) |
| R2 | 800B 时 SM 长度设备校验失败 | 事件日志看 `min/max size`;按报告值对齐 |
| R3 | PLC 构建后 tsproj 类型 GUID 抓取失败 | 构建后 rg 校验 GUID 存在再替换 |
| R4 | 1kHz 下 800B 帧对 E-bus 其他端子无影响 | 既有端子已在 1kHz 验证过,800B 增量小 |
## 7. 工时估计
约 23 小时(含一次失败重试余量):tsproj 手术 30min、PLC 30min、J1900 blob 泛化+构建 40min、联调验证 40min。
## 8. 修订历史
| 日期 | 内容 |
|---|---|
| 2026-07-27 | 初版方案 |

View File

@ -1,6 +1,6 @@
# EL6695 桥接 8 字节双向实时通讯 — 设计文档
> **版本**: 1.0
> **版本**: 1.1(新增 §8 800B 变体)
> **日期**: 2026-07-27
> **状态**: 已实现并验证通过
> **前置文档**: `1kb-verification-progress.md`(1KB 版本演进史)
@ -224,3 +224,65 @@ scp -J tonycao@192.168.58.8 target/x86_64-unknown-linux-musl/release/el6695-rt `
1. **无诊断 PDO**: 8B 配置不含 "External device not connected" 等状态位。若需要,须在 SM3 同时赋值 0x1A01+0x1A08 并保持 SM3=10(TwinCAT 侧重算逻辑需相应修正)。
2. **延迟**: 往返固定 4 个 PLC 周期(≈4ms @1kHz),未做 DC 同步优化。
3. **尺寸扩展**: 本设计的修改方法(GUID 类型 + SM 记录 + J1900 常量)适用于任意 ≤240bit 的单条目尺寸;更大尺寸需在 `twin_layout_blob_sized` 中恢复多条目延续格式。
---
## 8. 800B 变体(已实现并验证)
在 8B 验证通路基础上,扩展出 800 字节 PDO 工程 `TwinCAT_EL6695_Primary_800B`,作为实际应用的带宽档位,并验证了任意尺寸的映射 blob 生成规则。
### 8.1 与 8B 设计的差异
| 项 | 8B | 800B |
|---|---|---|
| PDO 条目类型 | `ARRAY [0..7] OF BYTE` | `ARRAY [0..799] OF BYTE`(GUID `{7D10FDFE-13E3-A012-D822-BE30F7D36D23}`,PLC 构建生成) |
| SM2/SM3 长度 | 8 / 8 | **800 / 800**(0x0320) |
| 0x1A01 诊断 PDO | 不挂 SM3 | 同左(沿用,否则设备报 `size not allowed`) |
| 映射 blob | 单条目 64bit | **27 条目**(见 §8.2) |
| 数据协议 | 每字节 XOR 0x5A + 历史环 | seq + f(x)=x·7+3 + 逐字节图案 + 反推校验(类 1KB) |
| 周期 | 1kHz(基线) | 1kHz |
### 8.2 多条目映射 blob 生成规则
EL6695 固件接受 TwinCAT 行格式:1 真实对象条目 @240bit + N 延续条目(0x0000:00)@240bit + 1 余量延续条目。
```
800 B = 6400 bit = 26 × 240 bit + 160 bit
blob = [27 (u16 LE)]
+ [240, 1, obj_lo, obj_hi] 真实对象 (0x6000:01 / 0x7000:01)
+ 25 × [240, 0, 0, 0] 延续
+ 1 × [160, 0, 0, 0] 余量延续
```
`twin_layout_blob_sized(obj, n)` 泛化实现:≤240bit 单条目;>240bit 按上式多条目;1024B 特判保持原 35 条目格式(字节级兼容)。
### 8.3 800B 数据布局
| 偏移 | 内容 | 方向 |
|---|---|---|
| [0..7] | TwinCAT `nCycles` → J1900 回 `nCycles*7+3` | 双向 |
| [8..15] | J1900 本地周期号 | J→T |
| [16..263] | 逐字节图案 `(nCycles+i) mod 256`,J1900 原样回显 | 双向 |
| [264..267] | J1900 周期抖动 ns | J→T |
| [268..271] | J1900 图案失配计数 | J→T |
| [272..279] | J1900 回存的原始 tc_seq | J→T |
| [280..799] | 逐字节图案(同上) | 双向 |
**设计要点:图案区必须避开 [264..279] 统计字段**——首版实现按 [16..799] 连续图案,J1900 统计字段覆盖 16 字节导致 PLC 每周期 16 字节失配。
### 8.4 新增陷阱(800B 实测)
| # | 现象 | 根因 | 解决 |
|---|---|---|---|
| 8 | 激活时 EL6695 SAFEOP 失败,AL 状态码 0x0003 | **激活与 J1900 ticker 会话并发**:relink 激活时 ticker 正在跑,桥两侧主站会话冲突 | 激活必须在 J1900 空闲时进行;失败后空载重新激活即恢复 |
| 9 | 数据通但 PLC bSync=False,每周期固定 16 字节失配 | 图案区 [16..799] 与 J1900 统计字段 [264..279] 重叠 | 图案区改为 [16..263]+[280..799](见 §8.3) |
| 10 | 手写 `<DcData>` 十六进制被 TwinCAT 归一化为全零 | DC 模式选择不接受 legacy hex 直写 | **用 XSD 结构化形式 `<Dc><Data AssignActivate="#x320"/></Dc>`**,TwinCAT 读取后自动生成正确 DcData(`2003...`,即 0x0320)并启用 DC-Synchron(2026-08-09 已验证) |
### 8.5 800B 实测性能
| 指标 | 值 |
|---|---|
| J1900 exec(800B,同步写) | 均值 384µs / p99 400µs |
| FPRD(读 800B) | 均值 139µs / p99 156µs(8B 时为 71µs) |
| FPWR(写 800B) | 均值 244µs / p99 260µs(与 8B 基本持平) |
| 桥接 RTT(1kHz) | 4 周期(lag=4,同 8B/1KB 结构) |

View File

@ -1,6 +1,6 @@
# EL6695 8B 双向通讯 — 测试文档
> **版本**: 1.0
> **版本**: 1.1(新增 §8 800B 测试)
> **日期**: 2026-07-27
> **结论**: ✅ 全部通过
> **配套文档**: `8b-bridge-design.md`、`8b-verification-methodology.md`
@ -172,3 +172,60 @@ Copy-Item "C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary\TwinCA
| 日期 | 内容 |
|---|---|
| 2026-07-27 | 初版:8B 双向交换全部测试通过 |
---
## 8. 800B 变体测试(2026-07-28)
### 8.1 环境差异
| 项 | 值 |
|---|---|
| TwinCAT 工程 | `TwinCAT_EL6695_Primary_800B`(1kHz 任务) |
| PDO 尺寸 | 800B(SM2/SM3 = 0x0320) |
| J1900 二进制 | `/root/gateway/el6695_rt_800b` |
| 校验脚本 | `scripts/tc-ads-verify-800b.ps1`、`tc-ads-mismatch-delta.ps1`、`tc-relink-800b.ps1`、`tc-deploy-retry-800b.ps1` |
### 8.2 配置确认(激活后 _Boot tszip)
```
SM2: 001c200364... (start=0x1C00 len=0x0320=800 ✓ 保留)
SM3: 008e200320... (start=0x8E00 len=0x0320=800 ✓ 保留)
PDO 条目: ARRAY [0..799] OF BYTE (GUID {7D10FDFE-13E3-A012-D822-BE30F7D36D23})
事件日志: 无 EL6695 错误
```
### 8.3 功能验证
| 项 | 结果 |
|---|---|
| J1900 映射 blob | 27 条目(26×240+160bit)被固件接受,SAFEOP/OP 通过 |
| 处理证明 | `proc = raw*7+3` 逐样本精确(如 107636 → 753455) |
| PLC 校验 | bSync=True,lag=4 |
| 图案覆盖 | aIn1 非零 775776/800;[16..263]+[280..799] 逐字节 `(seq+i)%256` 抽查全对 |
### 8.4 10 分钟稳定性(600,000 周期)
| 指标 | 结果 |
|---|---|
| 周期完整 | 600000 周期 / 600.0s,零丢失 |
| 传输错误 | tx_err=0, rx_err=0 |
| J1900 图案失配 | mism=0 |
| PLC nMismatch | 295s 运行窗口零增长;90s 确认测试 delta=0 |
| stale | 2999 周期(0.5%) |
| 周期抖动 | 均值 4.89µs / p99 5.4µs(单次 499µs 离群) |
| exec | 均值 384µs / p99 400µs |
| FPRD / FPWR | 139µs / 244µs |
注:起止时刻的 nMismatch 增长均为**对端缺席期**的预期累积(ticker 未启动/已停止),运行窗口内零失配。
### 8.5 800B 特有故障记录
| # | 故障 | 根因 | 处置 |
|---|---|---|---|
| F1 | relink 激活时 EL6695 SAFEOP 失败(AL 0x0003) | 激活与运行中的 ticker 会话并发冲突 | J1900 空闲后重新激活即恢复;流程约定:**先激活 TwinCAT,后启动 ticker,激活期间 J1900 必须空闲** |
| F2 | 数据通但每周期固定 16 字节失配 | 图案区 [16..799] 覆盖 J1900 统计字段 [264..279] | 图案区改为 [16..263]+[280..799](MAIN 与校验循环同步修改) |
### 8.6 回退
800B 工程为独立副本,不影响 8B/1kHz 主工程;切回:重新激活 `TwinCAT_EL6695_Primary`(`tc-deploy-retry.ps1`),J1900 使用 `el6695_rt_8b_xor`

View File

@ -1,6 +1,6 @@
# EL6695 8B 双向通讯 — 验证说明文档
> **版本**: 1.0
> **版本**: 1.1(新增 §7 800B 验证)
> **日期**: 2026-07-27
> **配套文档**: `8b-bridge-design.md`(设计)、`8b-test-report.md`(测试记录)
@ -151,3 +151,47 @@ aIn1^5A = 98-B8-D8-F8-18-38-58-78 ← 8/8 全等
1. 验证图案为周期性确定图案,未做随机负载压力测试;
2. `lag=4` 为当前拓扑/周期下的实测值,未验证不同任务周期下的行为;
3. 8B 配置不含诊断 PDO,"对端是否在线"需以数据流本身判断(失配即掉线)。
---
## 7. 800B 变体的验证方法
800B 工程采用**序列号 + 图案 + 反推校验**协议(类 1KB),与 8B 的 XOR 历史环不同,原因是 800B 要同时验证"大载荷每个字节"与"序号处理"两个维度。
### 7.1 协议与验证链
```
TwinCAT 发送: aOut1[0..7] = nCycles; aOut1[i] = (nCycles+i) mod 256
(i ∈ [16..263] [280..799], 避开 [264..279] 统计字段)
J1900 处理: tx[0..7] = rx_seq*7+3; tx[8..15] = 本地周期号;
tx[264..279] = 抖动/失配/原始 seq; 其余字节原样回显
TwinCAT 校验: nProcessedSeq = aIn1[0..7]
nReturnedSeq = (nProcessedSeq - 3) / 7 (MOD 7 = 0 才有效)
逐字节比对 aIn1[i] == (nReturnedSeq + i) mod 256
```
- **反推校验**:J1900 的 f(x)=x·7+3 可逆,PLC 从回传值反推原始发送周期,无需预设管线延迟(实测 lag=4);
- **J1900 侧自检**:ticker 对图案区 [16..255] 做 `(tc_seq+i) mod 256` 独立校验(`mism` 计数),与 PLC 侧校验互为对侧证据。
### 7.2 与 8B 方法的取舍
| 维度 | 8B(XOR 历史环) | 800B(seq+图案反推) |
|---|---|---|
| 每字节独立性 | 强(每字节独立 XOR) | 中(图案含周期序号,错位立现) |
| 延迟测量 | 直接(nMatchLag) | 直接(nCycles nReturnedSeq) |
| 大载荷成本 | 历史环 16×8B 比对/周期 | 768 字节比对/周期 |
| 适合 | 小载荷通路证明 | 应用级带宽验证 |
### 7.3 ADS 快照判读(800B 特有)
外部 ADS 校验脚本用 `nReturnedSeq` 重建预期图案时,因 `nCycles/nReturnedSeq/aIn1` 分次读取,可能出现 `nReturnedSeq > nCycles`(显示 lag 为负)与全图案"失配"的假象——**以 PLC 内部 1kHz 实时计算的 `bSync` 为唯一权威判据**;ADS 全数组读取仅用于非零覆盖率统计(800B 时应 ≈770+/800)。
### 7.4 800B 稳定性判据
| 判据 | 通过条件 | 实测 |
|---|---|---|
| J1900 周期完整 | 周期数 = 时长 × 频率 | 600000 / 600.0s ✅ |
| J1900 `mism` | 0 | ✅ |
| PLC `nMismatch` | 运行窗口内零增长 | 295s 窗口 delta=0;90s 确认 delta=0 ✅ |
| 传输错误 | tx/rx = 0 | ✅ |
| 非零覆盖率 | ≈770/800 | 775776 ✅ |

129
docs/d2000-dev-plan.md Normal file
View File

@ -0,0 +1,129 @@
# 飞腾 D2000 ↔ EL6695 ↔ TwinCAT 通讯链路 — 开发计划
> **版本**: 1.0
> **日期**: 2026-07-28
> **状态**: 待启动
> **前置成果**: J1900 链路全部方法论(8B/800B 设计、验证体系、脚本集、Rust 代码库)
---
## 1. 目标与拓扑
在现有 J1900 链路之外,新增一条**并列**通讯链路:飞腾 D2000(ARM64)经 EL6695#2 与 TwinCAT PC 实时数据交换,复用已验证的配置方法与验证体系。
```
TwinCAT PC (169.254.176.217)
├─ [现有链路] EK1100 → ... → EL6695#1 → J1900 (x86_64, Debian13+RT) ✅ 已验收
└─ [新增链路] E-bus 扩展或独立网口 → EL6695#2 → 飞腾 D2000 (aarch64) 🆕
跳板机#2 网口 (SSH 管理通道)
```
### 待确认事项(启动前)
| # | 问题 | 影响 |
|---|---|---|
| Q1 | EL6695#2 挂在哪里?(现有 E-bus 末端 EL9011 前扩展,还是 TwinCAT 第二网口独立段?) | TwinCAT 工程拓扑/网卡规划 |
| Q2 | D2000 的 OS 与内核(麒麟 V10? UOS? Ubuntu? 内核版本? 是否有 PREEMPT_RT?) | RT 性能预期、移植工作量 |
| Q3 | D2000 管理通道:跳板机#2 的地址、SSH 凭据、能否免密 | 自动化部署方式 |
| Q4 | D2000 接 EL6695 的网口(板载 GbE? 独立网卡? 驱动?) | ethercrab 裸 socket 可用性 |
---
## 2. 阶段计划
### 阶段 1:环境与可达性(0.5 天)
| # | 任务 | 产出 |
|---|---|---|
| 1.1 | 经跳板机#2 SSH 到 D2000,采集:`uname -a`、`/etc/os-release`、`/proc/version`、CPU/核数、网口列表(`ip link`)、中断分布(`/proc/interrupts`) | 环境档案 |
| 1.2 | 确认 PREEMPT_RT(`uname -v` 含 rt?)、`chrt`/`mlockall` 可用性 | RT 基线判定 |
| 1.3 | 网络打通:D2000 → EL6695#2 网线;D2000 → 跳板机 管理网 | 拓扑照片/记录 |
| 1.4 | 空口探测:tcpdump/裸 socket 读 ethX,确认能收发二层帧 | 网卡可用性 |
### 阶段 2:Rust 交叉编译 aarch64(0.5 天)
| # | 任务 | 备注 |
|---|---|---|
| 2.1 | `rustup target add aarch64-unknown-linux-musl`;`cargo build --target aarch64-unknown-linux-musl --release` | ethercrab 为纯 Rust,理论上零改动;libc 裸 socket(AF_PACKET)在 aarch64 内核同样支持 |
| 2.2 | 链接器:沿用 `RUSTFLAGS="-C linker=rust-lld"`(musl 静态,免去 D2000 上 glibc 版本问题) | musl 静态二进制通吃 Kylin/UOS/Debian |
| 2.3 | 部署 scp → D2000,跑 `--mode probe`(只读扫描) | 首次上机验证 |
| 2.4 | 若 AF_PACKET 在目标内核异常,备选 `aarch64-unknown-linux-gnu` + 检查目标 glibc | 风险预案 |
### 阶段 3:EL6695 链路开通(0.51 天,方法全部现成)
| # | 任务 | 复用资产 |
|---|---|---|
| 3.1 | TwinCAT 侧:复制 8B 工程为 `TwinCAT_EL6695_Primary_D2000`(或在 EL6695#2 所在段新建拓扑),激活确认 SM2/SM3 | `tc-deploy-retry.ps1` 模式 |
| 3.2 | D2000 侧:`el6695_rt ethX --mode verify` CoE 审计 → ConfigOwner 到 OP | 与 J1900 完全相同的流程 |
| 3.3 | **严格遵守顺序:TwinCAT 激活时 D2000 侧空闲;先激活,后启 ticker**(800B 踩坑 F1) | 教训文档化 |
| 3.4 | ticker 8B XOR 模式联通,PLC bSync=True | `GVL_Sync` 验证体系 |
### 阶段 4:数据验证与实时性测量(0.5 天)
| # | 任务 | 判据 |
|---|---|---|
| 4.1 | 8B 逐字节验证(图案+XOR+历史环) | bSync=True,lag 稳定 |
| 4.2 | 10 分钟稳定性 | 60 万周期零错误,nMismatch 零增长 |
| 4.3 | D2000 实时性基线:周期抖动/exec/FPRD/FPWR 统计 | 与 J1900 对照(7µs 抖动基准) |
| 4.4 | 如需 800B:换 `el6695_rt_800b` 对应构建(同源码,常量切换) | 800B 工程现成 |
### 阶段 5:D2000 RT 调优(按需,0.51 天)
| # | 措施 | 场景 |
|---|---|---|
| 5.1 | IRQ 亲和:管理网口 IRQ 钉到非 RT 核 | 必做 |
| 5.2 | 核隔离 `isolcpus` + ticker 绑核(程序已支持 `--core`) | 推荐 |
| 5.3 | 若无 RT 补丁且抖动不可接受:评估编译 aarch64 PREEMPT_RT 内核(源码 kernel.org + rt patch,交叉编译) | 兜底 |
| 5.4 | 抖动向因分析:hwlatdetect/cyclictest 对照 | 诊断工具 |
### 阶段 6:文档与归档(0.5 天)
- 新增 `docs/d2000-bringup.md`(环境档案、移植记录、与 J1900 的性能对照)
- 更新三份主文档的适用范围章节;git 提交
---
## 3. 复用率评估
| 资产 | 复用程度 |
|---|---|
| el6695_rt 源码(main.rs/bridge.rs) | ~100%(唯一改动:默认网口名/可能的字节序检查——ARM 与 x86 同小端,无影响) |
| TwinCAT 工程与 PLC 验证程序 | 100%(复制副本改拓扑) |
| 部署/诊断/监视脚本(20+ 个) | ~90%(J1900 地址替换为 D2000 地址) |
| 配置方法论(SM/PDO/blob/链接/激活顺序) | 100% |
## 4. 风险登记
| # | 风险 | 概率 | 缓解 |
|---|---|---|---|
| R1 | D2000 OS 无 PREEMPT_RT,抖动大(几十~几百 µs) | 高 | 阶段 5.3 自编译 RT 内核;或先测后判,1kHz 场景通常可接受 |
| R2 | 麒麟/UOS 老内核(4.19)与 ethercrab 兼容性 | 低 | musl 静态;AF_PACKET 自 2.2 内核即有 |
| R3 | D2000 网口驱动不支持混杂/裸包或性能差 | 低 | 阶段 1.4 提前探测;备 USB 网卡 |
| R4 | 跳板机#2 到 D2000 的 SSH 不稳定 | 中 | 串口兜底;长会话用 nohup+日志(既有模式) |
| R5 | 双链路并存时 TwinCAT 两张网卡/两段总线资源冲突 | 低 | Q1 拓扑确认后评审 |
## 5. 里程碑
| 里程碑 | 判据 | 状态 |
|---|---|---|
| M1 | D2000 SSH 通,环境档案完成(Q1Q4 全部答案) | ⬜ 待硬件接入 |
| M2 | aarch64 二进制构建成功 | ✅ 2026-08-06(见下) |
| M3 | 链路 OP,8B bSync=True | ⬜ |
| M4 | 10 分钟零错误 + 抖动基线报告 | ⬜ |
| M5 | 文档归档 + git 提交 | ⬜ |
### M2 完成记录(2026-08-06)
- `rustup target add aarch64-unknown-linux-musl` 曾因清华镜像缺该日期组件 404 且 `RUSTUP_DIST_SERVER` 环境变量被缓存清单吞掉;**根因:`~/.rustup/toolchains/<tc>/lib/rustlib/multirust-channel-manifest.toml` 缓存清单内含 tuna 绝对 URL**,替换为 `https://static.rust-lang.org/dist/` 后安装成功。
- `cargo build --release --target aarch64-unknown-linux-musl` **零代码改动通过**(el6695-rt + xfc_probe),产物 ELF `e_machine=0xB7 (AArch64)`,musl 静态,约 1MB。
- 构建配置固化:`.cargo/config.toml` 已为两个 musl target 指定 `rust-lld`,不再需要 `RUSTFLAGS` 环境变量。
- 部署脚本:`scripts/d2000-deploy.ps1`(构建→架构校验→scp→chmod),JumpHost/TargetHost 待 D2000 接入后填实际值。
## 6. 修订历史
| 日期 | 内容 |
|---|---|
| 2026-07-28 | 初版计划 |

69
docs/jitter-analysis.md Normal file
View File

@ -0,0 +1,69 @@
# J1900 实时性能偶发最大抖动分析
> **日期**: 2026-07-28
> **对象**: J1900 (Debian 13, Linux 6.6.135-rt74 PREEMPT_RT) 上 el6695_rt ticker 的周期抖动
> **数据来源**: 1kHz 8B 600s 测试、800B 10 分钟测试、4kHz 测试、800B 5 小时(1800 万周期)测试
---
## 1. 现象
周期抖动(实际周期相对名义周期的偏差)的**均值极小(57µs)**,但偶发数百微秒的最大值:
| 测试 | 样本数 | jitter 均值 | jitter max |
|---|---|---|---|
| 1kHz 8B, 600s | 600,000 | 7.29µs | **10.0µs** |
| 800B, 10 分钟 | 600,000 | 4.89µs | **499.1µs** |
| 4kHz, 120s | 352,917 | 90µs(含滑相) | **1549µs** |
| **800B, 5 小时** | **18,000,000** | **7.39µs** | **41.8µs** |
关键观察:尖峰幅度**不是系统固有属性**——5 小时静默运行(1800 万样本)最大仅 41.8µs,而活动密集的 10 分钟测试出现 499µs。尖峰与并发系统活动强相关。
## 2. 诊断证据
### 2.1 系统环境(J1900 实测)
| 项 | 值 | 影响 |
|---|---|---|
| 根文件系统 | **NFS**:`192.168.68.1:/j1900-node2/rootfs`(TwinCAT PC 做 NFS 服务器) | 一切文件访问走网络 |
| /tmp | tmpfs(内存) | ticker 日志不落盘 ✓ |
| 设备中断 | eth0-rx 30 万次全在 CPU0;ahci/ttyS0 也在 CPU0/1 | ticker 核 CPU2 无设备 IRQ ✓ |
| 调度 | ticker 绑定核 2,SCHED_FIFO 80,mlockall,栈预缺页 | 基础保障已做 |
| dmesg | 无热事件、无异常;sshd 栈深度提示(良性) | 排除过热 |
### 2.2 相关性
尖峰最频繁的测试(10 分钟、4kHz)期间,Windows 侧正密集进行 TwinCAT 构建/激活与 SSH 会话;5 小时静默期几乎无尖峰。
## 3. 原因排序
| # | 原因 | 机理 | 符合度 |
|---|---|---|---|
| 1 | **NFS 根文件系统停顿** | Windows NFS 服务器忙(VS 构建、大文件 I/O)→ J1900 侧触盘进程阻塞 → 网络 softirq、内存回收压力波及 RT 核 | ★★★★ 主因(静默期 max 仅 41.8µs) |
| 2 | **SMI 系统管理中断** | J1900(Bay Trail Atom)BIOS 的 USB legacy / 嵌入式控制器 / 热管理 SMI,100500µs 不可屏蔽、OS 不可见 | ★★★ 次嫌疑(偶发形态吻合,需 hwlatdetect 证实) |
| 3 | **深 C 状态退出** | Bay Trail C6 退出延迟 ~100200µs;周期睡眠偶入深态 | ★★(均值仅 57µs,多数时间为浅态) |
## 4. 验证方法
```bash
# 区分硬件(SMI) vs 软件(NFS/OS)延迟 — 内核 6.6 自带 rtla:
rtla hwlatdetect --cpus 2 --duration 60
# 判定: hwlatdetect 报数百微秒 => SMI/硬件; 干净 => NFS/OS 软件因素
# 辅助: cyclictest 对照(无 EtherCAT 负载时测量裸调度抖动)
cyclictest -m -p 80 -i 1000 -l 60000 -a 2
```
## 5. 缓解建议(按收益排序)
| # | 措施 | 预期效果 |
|---|---|---|
| 1 | gateway 目录与日志移出 NFS(tmpfs 或本地存储) | 消除主因 |
| 2 | BIOS 关闭 USB Legacy、不需要的 EC 功能 | 消 SMI 尖峰 |
| 3 | 内核启动参数 `isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3` | 完全隔离 RT 核 |
| 4 | `intel_idle.max_cstate=1` 限制最深 C 状态 | 消 C6 退出尖峰(功耗换延迟) |
| 5 | IRQ 亲和性复核(保持设备 IRQ 远离核 2/3) | 已满足,固化 |
## 6. 结论
J1900 基础实时性良好(均值 57µs,p99 < 10µs,1800 万周期零丢帧)。偶发数百微秒尖峰的主因是 **NFS 根文件系统在 Windows 服务器繁忙时的停顿**;SMI 为次要嫌疑,可用 `rtla hwlatdetect` 一测定论对当前 EL6695 数据通道无实质影响(尖峰只占 ~0.0001%, 1kHz 预算 1ms exec 最大 400µs 仍有富余)。

View File

@ -0,0 +1,287 @@
# TwinCAT ↔ J1900 实时数据通讯验证报告EL6695 桥接)
> **版本**: 1.0
> **日期**: 2026-08-12
> **结论**: ✅ 全部通过4kHz 3 小时 soak 零错误8kHz RTT min 0.75ms / mean 1.04ms
> **关联文档**: `8b-bridge-design.md`、`8b-verification-methodology.md`、`500us-latency-plan.md`、`jitter-analysis.md`
---
## 1. 摘要
本报告覆盖 TwinCAT PC 与 J1900 嵌入式平台之间经 EL6695 EtherCAT 桥接的实时数据通讯验证,
包括数据完整性、长期稳定性、周期扩展1kHz→8kHz、双侧 DC-Synchron、RTT 延迟优化与
断电恢复能力。
**核心结果**
| 验证项 | 结果 |
|---|---|
| 数据完整性800B 载荷,双向交叉校验) | 3 小时 4537 万周期零错误 |
| 长期稳定性4kHz soak 11400s | bSync=True ×190 采样点nMismatch delta=0 |
| J1900 侧自校验 | 33.6M 帧 payload mismatch=0tx/rx err=0 |
| RTT8kHz + tx_rx RT 提权) | min 0.75ms / mean 1.04ms / max 1.38ms |
| 单向延迟(推导) | 典型 0.30.5ms,最坏 ~0.6ms |
| 死链路检测 | lag 指标 + bSync 双重敏感(实测 5 秒内可检出) |
| DC-Synchron双侧 | 从站侧固件 sync_mode=2 接受180s 同步计数器全零 |
| 断电恢复 | TwinCAT 自恢复 RunJ1900 重建 /tmp 后 <2 分钟恢复全链路 |
---
## 2. 系统架构与测试环境
### 2.1 硬件拓扑
```
TwinCAT PC (Windows, TwinCAT 3.1.4026.24)
└─ EtherCAT 主站网卡 "以太网 2" (TwinCAT-Intel PCI, MAC 00-15-17-8A-B3-DA)
└─ EK1100 → EL2202 → EL1252 → EL2262 → EL2008 → EL6851-0010 → EL6022
→ EL6695 (桥接端子, 主站侧 X2)
└─ EL6695 从站侧 X1 ←直连→ J1900 eth1
J1900 (Linux 6.6.135-rt74 PREEMPT_RT, Celeron J1900 4 核)
```
| 项 | 值 |
|---|---|
| TwinCAT AmsNetId | `169.254.176.217.1.1` |
| J1900 管理 IP | `192.168.68.100`DHCP曾漂移至 .101MAC `00:e2:69:34:71:2c` |
| SSH 跳板 | `tonycao@192.168.58.8` |
| EL6695 从站侧站址 | `0x1000` |
| J1900 内核 | 6.6.135-rt74PREEMPT_RTio_uring 可用) |
### 2.2 软件与工程
| 项 | 位置 |
|---|---|
| TwinCAT 工程800B 变体) | `C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary_800B\` |
| J1900 源码 | `C:\Users\tonycao\work\ethercat-linux`Rustethercrab 0.7 本地 patch |
| 构建目标 | `x86_64-unknown-linux-musl`(静态链接,零运行时依赖) |
| 关键 commits | `f0163d8`(800B) → `aabfe31`(DC-Synchron) → `cdb90c3`(800B 异步 FPWR) → `253f4b3`(J1900 DC) → `4083df0`(8kHz+RT 提权) |
| 配置备份 | tsproj `.bak-1k-dc` / `.bak-4k`TcTTO `.bak-1k` / `.bak-4k` |
### 2.3 载荷布局800B
| 偏移 | 内容 | 方向 |
|---|---|---|
| `[0..7]` | 序列号PLC 写 `tc_seq=nCycles`J1900 回写 `f(tc_seq)=tc_seq*7+3` | 双向 |
| `[8..15]` | J1900 本地周期计数 | 上行 |
| `[16..799]` | 图案区:`(seq + i) mod 256`J1900 原样回传) | 双向 |
| `[264..267]` | J1900 周期抖动 i32统计区不参与校验 | 上行 |
| `[268..271]` | J1900 payload mismatch 计数 | 上行 |
| `[272..279]` | 原始 tc_seq 保留(供 PLC 交叉验证) | 上行 |
---
## 3. 验证方法学
### 3.1 数据完整性证明链
单向"收到数据"不能证明通讯正确。本方案用**双向交叉校验 + 数学变换**构成闭环证明:
1. **PLC 侧写入**`aOut1[i] = (nCycles + i) mod 256`(图案区),`aOut1[0..7] = tc_seq`
2. **J1900 侧处理**(证明"读取+计算+写回"三个动作真实发生):
- 校验收到图案 `[16..256)``tc_seq` 一致(不一致计入 `payload_mismatch`
- 对序列号做不可由硬件桥接伪造的数学变换:`f(x) = x*7 + 3`
- 回写 `aIn1[0..7] = f(tc_seq)`,图案区原样回传,`[272..279]` 保留原始 tc_seq
3. **PLC 侧验证**
- 从 `aIn1[0..7]` 反解 `nReturnedSeq = (x-3)/7`(整除性本身是一道校验)
- `bSync := 全部 aIn1[i] == (nReturnedSeq + i) mod 256`768 字节逐字节)
**设计要点**
- `f(x)=7x+3` 保证回传数据不可能由桥接端子透传/环回伪造
- 用 `nReturnedSeq`(回传帧自带的序号)而非本地 `nCycles` 做图案比对,
使校验对传输滞后免疫——校验的是"回传帧内部自洽性"
- J1900 侧独立再做一遍正向校验,两侧互为佐证
### 3.2 新鲜度liveness证明
自洽性校验的盲区:回传通道冻结时数据仍自洽。对策:
- **`nMatchLag = nCycles - nReturnedSeq`**PLC 内每周期原子计算):
回传帧的产生时刻距当前的周期数。通道健康时稳定在小范围;
通道死亡时以任务频率无界增长
- **实测死链路响应**:杀掉 J1900 ticker 后 5 秒内 lag 从 6 → 8.1 亿且以 4000/s 增长,
bSync 同步翻 FalseEL6695 从站侧失联使输入映像清零)
- **反向验证**:重启 ticker 后 lag 立即回落至 46 周期
### 3.3 方法学盲区(如实声明)
| 盲区 | 说明 | 缓解 |
|---|---|---|
| 桥转发冻结而两侧 ESC 保持 OP | aIn1 冻结时自洽校验仍通过,任何黑盒测试无法检出 | 概率极低;需侵入式手段 |
| J1900 侧图案校验仅覆盖 `[16..256)`240/800 字节) | PLC 侧覆盖 768 字节,互补 | 可扩大 J1900 校验范围 |
| J1900 处理帧率低于发送帧率(设计性跳帧) | echo 恒为最新帧lag 指标证明),无陈旧累积 | 见 5.4 |
---
## 4. 指标详解
### 4.1 PLC 侧GVL_SyncADS 可读)
| 指标 | 类型 | 定义 | 意义与解读 |
|---|---|---|---|
| `nCycles` | ULINT | PLC 任务周期计数 | 任务存活与速率基准;速率 = Δ/Δt偏离任务频率即异常 |
| `bSync` | BOOL | 当前周期 768 字节图案全部匹配 | **瞬时**正确性。单次 False 可能是瞬态;持续 False = 链路/配置故障 |
| `nMismatch` | DINT | 累计失配字节数(仅 diff>0 时累加) | **历史**错误总量。soak 中看 delta=0注意 int32错误率 3M/s 时约 11 分钟回绕 |
| `nReturnedSeq` | ULINT | 回传帧携带的原始序号(反解后) | 回传链路的"时间戳";与 nCycles 之差即 lag |
| `nMatchLag` | DINT | `nCycles - nReturnedSeq`,每周期原子更新 | **新鲜度核心指标**:回传 RTT 的周期数表示。健康值=小范围稳定;无界增长=回传通道死 |
**ADS 采样注意**`nCycles` 与 `nReturnedSeq` 分两次读取会产生撕裂(高速任务下可读出负 lag 假象);
必须直接读原子计算的 `nMatchLag`
### 4.2 J1900 侧ticker S 行遥测)
| 字段 | 定义 | 异常含义 |
|---|---|---|
| `cyc` | J1900 处理循环计数 | 速率 = 实际处理能力上限的度量 |
| `raw` / `proc` | 收到的 tc_seq / 回写的 f(tc_seq) | 与 PLC 侧 nCycles 对账,验证双向一致 |
| `txe` / `rxe` | FPWR/FPRD 事务错误 | 持续增长 = 链路劣化或 ESC 掉态 |
| `late` | 异步 FPWR 响应未在下一周期收齐的次数 | 少量(<0.1%正常暴涨 = 总线拥塞或 RT 调度劣化 |
| `stale` | tc_seq 未变化(读到旧帧)占比 | spin 模式下少量正常跳帧100% = TwinCAT 侧停发 |
| `mono` | tc_seq 回退次数 | 非零 = 帧乱序/映像撕裂,严重异常 |
| `cerr` | J1900 内部计算自洽错误 | 必须恒为 0软件自检 |
| `mism` | J1900 侧图案失配累计 | 必须冻结delta=0 |
| `dcph` | EL6695 从站侧 DC 系统时间 mod 周期 | DC 时钟相位观测 |
| `dcdr` | DC 时钟相对 CLOCK_MONOTONIC 的累计漂移 | 无持续漂移补偿时有界 wander实测 ±几十 ppm |
### 4.3 时延类统计OnlineStats结束报告
| 统计 | 定义 | 实测3h soak, 4kHz spin |
|---|---|---|
| `exec_time` | 单周期处理耗时(读→变换→写就绪) | p50=25µs, p99=44µs, max=300µs |
| `period_jitter` | spin 循环相邻 fresh 检测的间隔偏差 | p50=12µs, mean=88µs受跳帧节奏支配非调度抖动 |
| `fprd_rtt` / `fpwr_rtt` | 单次事务往返 | 800B FPRD ≈ 160µs决定 spin 检测粒度) |
### 4.4 RTT 分解模型8kHz125µs/周期)
实测 RTT mean 1.04ms ≈ 8.3 周期,分解:
| 环节 | 典型耗时 | 性质 | 可优化性 |
|---|---|---|---|
| TwinCAT 输出量化(任务边界→帧发出) | ~62µs0125 | 整周期 | 仅能提频 |
| 正向:线缆+EL6695 桥固件转发 | ~150µs | 连续 | 桥固件决定,不可控 |
| J1900 spin 检测FPRD 轮询粒度 160µs | ~125µs0250 | 量化 | 受 800B 事务往返物理下限约束 |
| J1900 处理 + 异步 FPWR 上线 | ~45µs | 连续 | 已优化至极限2425µs exec |
| 回程:线缆+桥转发 | ~150µs | 连续 | 同上 |
| TwinCAT 输入量化(帧到达→任务读取) | ~62µs0125 | 整周期 | 仅能提频 |
| 整周期簿记lag 按整周期计) | ~125250µs | 量化 | — |
**单向延迟推导**TwinCAT→J1900去掉回程三项典型 **0.30.5ms**,最坏 ~0.6ms。
适用设定值级控制、配方下发、同步基准传递;紧伺服闭环受限于完整 RTT~1ms闭环带宽 ~100Hz 量级)。
---
## 5. 测试结果
### 5.1 配置演进总表
| 阶段 | TwinCAT 任务 | J1900 模式 | 结果 | commit |
|---|---|---|---|---|
| 8B 原理验证 | 1kHz | ticker | 通过XOR 逐字节证明) | `309aaba` |
| 800B + SM 精确匹配 | 1kHz | ticker | 5h 零错误18M 周期) | `f0163d8` |
| TwinCAT 侧 DC-Synchron | 1kHz | ticker | 激活干净90k 周期零错误 | `aabfe31` |
| spin-lock + 异步 FPWR | 4kHz | spin | J1900 2040/s数据零错误 | `cdb90c3` |
| J1900 侧 DC-Synchron | 4kHz | spin+dc | 180s 同步计数器全零 | `253f4b3` |
| **3 小时 soak** | **4kHz** | **spin+dc** | **全绿(见 5.2** | — |
| RTT 优化 | 8kHz | spin+dc+RT 提权 | RTT min 0.75ms / mean 1.04ms | `4083df0` |
### 5.2 3 小时 soak4kHz + spin + DC2026-08-11 22:12 → 01:21
**PLC 侧**ADS 每 60s 采样 ×190
| 指标 | 结果 |
|---|---|
| bSync | True ×190/190 |
| nMismatch delta | 0 ×190/190 |
| ADS 错误 | 0 |
| 任务速率 | 4000.9/s 恒定 |
| 总周期 | 45,370,909 |
**J1900 侧**`--duration 11400` 整):
| 指标 | 结果 |
|---|---|
| 处理周期 | 33,711,687~2957/s74% 帧率,设计性跳帧) |
| payload 校验 | checked=33,598,819mismatch=0 |
| tx_err / rx_err / comp_err | 0 / 0 / 0 |
| stale | 0.3%(跳帧读旧,正常) |
| late | 15,7410.05% |
| exec_time | p50=25µsp99=44µs |
| DC 漂移 dcdr | 3.2h 累计 ~-9.8ms(≈-0.85ppm,有界 wander |
### 5.3 DC-Synchron 验证细节
| 发现 | 证据 |
|---|---|
| ethercrab init 已分发从站侧 DC 系统时间 | 0x0920 offset 非零、0x0930 漂移补偿激活、0x0910 以真实速率推进 |
| EL6695 从站侧 ESC SYNC 寄存器0x0980+ECAT 侧不可读 | FPRD WKC=0写入 WKC=1固件经 PDI 自管) |
| 正确启用路径是 CoE | `0x1C32/0x1C33:01 sync_mode=2` 写入被固件接受且回读保持 |
| DC 模式下运行无副作用 | 180ssm_event_missed=0cycle_exceeded=0sync_error=0SM2+SM3 |
**未证实项**SYNC0 脉冲全程实际激活状态(收尾计数器复读失败);
dcph 全范围游走表明 J1900 处理时序锁的是帧到达spin而非 SYNC0 网格。
表述应为"DC 配置下数据通路无异常",而非"DC 对性能有贡献"。
### 5.4 RTT 优化实验矩阵
| 配置 | RTT min | RTT mean | RTT max | J1900 处理率 | 结论 |
|---|---|---|---|---|---|
| 4kHz AF_PACKET | 1.0ms | 1.45ms | 1.75ms | 2957/s (74%) | 基线 |
| 8kHz AF_PACKET | 0.75ms | 1.09ms | 1.5ms | ~4000/s (50%) | 两端量化减半生效 |
| 8kHz io_uring 后端 | 1.13ms | 1.42ms | 1.75ms | 2043/s | ❌ 阻塞式设计更差,弃用 |
| **8kHz + tx_rx RT 提权** | **0.75ms** | **1.04ms** | **1.38ms** | ~31004000/s | ✅ 当前最优 |
**结论**p50=1.0ms、min=0.75ms"1ms 内"在最优/中位情形达成mean 超出 4%。
剩余耗时为结构性桥固件转发、FPRD 检测粒度、两端量化),
16kHz 提频预期收益仅 ~60µs 且引入 TcTTO 整数 µs 取整、PLC exec 预算、EL6695 能力三重风险,不建议。
### 5.5 鲁棒性测试
| 场景 | 结果 |
|---|---|
| ticker 误退(默认 60s 时长) | PLC 侧 bSync→False、nMismatch 增长——故障可观测;`--duration 0` 为正确用法 |
| 死链路注入(杀 ticker | lag 5s 内 6→8.1 亿bSync=False重启后 46 周期恢复 |
| 双实例抢网卡(误操作注入) | 错误计数txe/rxe立即暴露err_streak 超限自动退出 |
| 整机断电重启 | TwinCAT 随 boot project 自恢复 Run+PLC Run4kHz 配置保持J1900 /tmp 清空需重建(脚本化 <2 分钟DHCP 地址漂移 .101.100 ARP 扫描定位 |
---
## 6. 已知限制与残余风险
1. **J1900 处理帧率 < 发送帧率**8kHz 时 ~50%):受 800B FPRD 事务往返 ~160µs 物理约束。
echo 恒为最新帧lag 指标证明),数据正确性不受影响,但每帧独立处理的场景不适用
2. **无持续 DC 漂移补偿**ethercrab 仅静态补偿dcdr 以 ±几十 ppm wander。
事件驱动spin交换不受影响若需 J1900 硬锁 DC 网格需加周期性 FRMW 补偿
3. **TwinCAT 试用许可 7 天过期**长期运行需周期性重新激活过期症状851 端口缺失、卡 Config 态)
4. **J1900 DHCP 地址漂移**:建议静态化或按 MAC 扫描定位(已备脚本化流程)
5. **方法学盲区**:桥转发冻结而 ESC 保持 OP 的失效模式黑盒不可检出(见 3.3
## 7. 结论与建议
**结论**TwinCAT 4kHz(250µs)/8kHz(125µs) + EL6695 桥 + J1900 spin-lock + 双侧 DC-Synchron
配置下800B 双向实时通讯经 3 小时 4500 万周期验证零数据错误;
RTT 达 min 0.75ms / p50 1.0ms / mean 1.04ms,单向延迟典型 0.30.5ms。
数据完整性、新鲜度、故障可观测性、断电恢复均经实证。
**建议**
1. 生产化时将图案区替换为真实过程数据(帧结构/序号校验/延迟特性已验证可直接复用)
2. soak 采样脚本已内置 lag 监控(`tc-ads-soak.ps1`),后续长测自动覆盖新鲜度
3. 若需 mean RTT 严格 <1ms评估 LRW 单帧交换 FMMU 配置改动中等前先量化收益
4. J1900 管理口建议配置静态 IP消除 DHCP 漂移运维成本
## 8. 附录:复现命令
```sh
# J1900 侧(经跳板)
ssh -J tonycao@192.168.58.8 root@192.168.68.100
/tmp/el6695_rt_dc eth1 --duration 0 --cycle-us 125 --spin --dc # 8kHz 常驻
/tmp/kill_el6695.sh # 停止全部实例
# TwinCAT 侧(非提权可读)
scripts\tc-ads-state.ps1 # 运行状态
scripts\tc-ads-quick.ps1 # 速率 + bSync
scripts\tc-ads-lag.ps1 # nMatchLag 新鲜度采样
scripts\tc-ads-mismatch-delta.ps1 # nMismatch 冻结检查
scripts\tc-ads-soak.ps1 -Minutes N -LogFile X # 长测采样WMI 方式后台启动)
scripts\tc-deploy-retry-800b.ps1 # 提权部署(构建+激活+重启)
```

301
monitor_5h.csv Normal file
View File

@ -0,0 +1,301 @@
timestamp,nCycles,bSync,nReturnedSeq,nMismatch,nProcMismatch
2026-07-28 02:13:04,1825622,False,0,648246528,844072
2026-07-28 02:14:04,1885662,False,0,694359552,904116
2026-07-28 02:15:04,1945680,False,0,740451072,964130
2026-07-28 02:16:04,2005694,False,0,786541824,1024144
2026-07-28 02:17:04,2065708,True,2065707,789764352,1028339
2026-07-28 02:18:04,2125730,True,2125727,789764352,1028339
2026-07-28 02:19:04,2185753,True,2185751,789764352,1028339
2026-07-28 02:20:04,2245766,True,2245766,789764352,1028339
2026-07-28 02:21:04,2305794,True,2305794,789764352,1028339
2026-07-28 02:22:04,2365820,True,2365819,789764352,1028339
2026-07-28 02:23:04,2425842,True,2425840,789764352,1028339
2026-07-28 02:24:04,2485861,True,2485861,789764352,1028339
2026-07-28 02:25:04,2545882,True,2545880,789764352,1028339
2026-07-28 02:26:04,2605900,True,2605898,789764352,1028339
2026-07-28 02:27:04,2665917,True,2665914,789764352,1028339
2026-07-28 02:28:04,2725936,True,2725934,789764352,1028339
2026-07-28 02:29:04,2785961,True,2785960,789764352,1028339
2026-07-28 02:30:04,2845983,True,2845981,789764352,1028339
2026-07-28 02:31:04,2906006,True,2906006,789764352,1028339
2026-07-28 02:32:05,2966030,True,2966028,789764352,1028339
2026-07-28 02:33:05,3026050,True,3026048,789764352,1028339
2026-07-28 02:34:05,3086068,True,3086066,789764352,1028339
2026-07-28 02:35:05,3146087,True,3146087,789764352,1028339
2026-07-28 02:36:05,3206113,True,3206111,789764352,1028339
2026-07-28 02:37:05,3266139,True,3266139,789764352,1028339
2026-07-28 02:38:05,3326161,True,3326159,789764352,1028339
2026-07-28 02:39:05,3386183,True,3386181,789764352,1028339
2026-07-28 02:40:05,3446206,True,3446206,789764352,1028339
2026-07-28 02:41:05,3506232,True,3506232,789764352,1028339
2026-07-28 02:42:05,3566251,True,3566249,789764352,1028339
2026-07-28 02:43:05,3626266,True,3626264,789764352,1028339
2026-07-28 02:44:05,3686283,True,3686281,789764352,1028339
2026-07-28 02:45:05,3746311,True,3746310,789764352,1028339
2026-07-28 02:46:05,3806331,True,3806329,789764352,1028339
2026-07-28 02:47:05,3866346,True,3866344,789764352,1028339
2026-07-28 02:48:05,3926364,True,3926364,789764352,1028339
2026-07-28 02:49:05,3986384,True,3986382,789764352,1028339
2026-07-28 02:50:05,4046400,True,4046400,789764352,1028339
2026-07-28 02:51:05,4106421,True,4106419,789764352,1028339
2026-07-28 02:52:05,4166439,True,4166439,789764352,1028339
2026-07-28 02:53:05,4226466,True,4226466,789764352,1028339
2026-07-28 02:54:05,4286494,True,4286493,789764352,1028339
2026-07-28 02:55:05,4346518,True,4346518,789764352,1028339
2026-07-28 02:56:05,4406539,True,4406537,789764352,1028339
2026-07-28 02:57:05,4466565,True,4466565,789764352,1028339
2026-07-28 02:58:05,4526588,True,4526586,789764352,1028339
2026-07-28 02:59:05,4586605,True,4586603,789764352,1028339
2026-07-28 03:00:05,4646620,True,4646618,789764352,1028339
2026-07-28 03:01:05,4706637,True,4706636,789764352,1028339
2026-07-28 03:02:05,4766661,True,4766661,789764352,1028339
2026-07-28 03:03:05,4826684,True,4826681,789764352,1028339
2026-07-28 03:04:05,4886701,True,4886699,789764352,1028339
2026-07-28 03:05:05,4946724,True,4946724,789764352,1028339
2026-07-28 03:06:05,5006746,True,5006744,789764352,1028339
2026-07-28 03:07:05,5066761,True,5066759,789764352,1028339
2026-07-28 03:08:05,5126775,True,5126773,789764352,1028339
2026-07-28 03:09:05,5186795,True,5186795,789764352,1028339
2026-07-28 03:10:05,5246822,True,5246822,789764352,1028339
2026-07-28 03:11:05,5306841,True,5306839,789764352,1028339
2026-07-28 03:12:05,5366855,True,5366852,789764352,1028339
2026-07-28 03:13:05,5426873,True,5426870,789764352,1028339
2026-07-28 03:14:05,5486893,True,5486891,789764352,1028339
2026-07-28 03:15:05,5546919,True,5546919,789764352,1028339
2026-07-28 03:16:05,5606940,True,5606938,789764352,1028339
2026-07-28 03:17:05,5666962,True,5666962,789764352,1028339
2026-07-28 03:18:05,5727000,True,5727000,789764352,1028339
2026-07-28 03:19:05,5787020,True,5787018,789764352,1028339
2026-07-28 03:20:06,5847036,True,5847034,789764352,1028339
2026-07-28 03:21:06,5907050,True,5907047,789764352,1028339
2026-07-28 03:22:06,5967067,True,5967067,789764352,1028339
2026-07-28 03:23:06,6027086,True,6027084,789764352,1028339
2026-07-28 03:24:06,6087101,True,6087099,789764352,1028339
2026-07-28 03:25:06,6147119,True,6147117,789764352,1028339
2026-07-28 03:26:06,6207138,True,6207136,789764352,1028339
2026-07-28 03:27:06,6267161,True,6267161,789764352,1028339
2026-07-28 03:28:06,6327180,True,6327178,789764352,1028339
2026-07-28 03:29:06,6387197,True,6387195,789764352,1028339
2026-07-28 03:30:06,6447216,True,6447215,789764352,1028339
2026-07-28 03:31:06,6507241,True,6507241,789764352,1028339
2026-07-28 03:32:06,6567266,True,6567266,789764352,1028339
2026-07-28 03:33:06,6627289,True,6627287,789764352,1028339
2026-07-28 03:34:06,6687315,True,6687315,789764352,1028339
2026-07-28 03:35:06,6747338,True,6747336,789764352,1028339
2026-07-28 03:36:06,6807356,True,6807354,789764352,1028339
2026-07-28 03:37:06,6867377,True,6867375,789764352,1028339
2026-07-28 03:38:06,6927395,True,6927393,789764352,1028339
2026-07-28 03:39:06,6987416,True,6987416,789764352,1028339
2026-07-28 03:40:06,7047439,True,7047436,789764352,1028339
2026-07-28 03:41:06,7107458,True,7107456,789764352,1028339
2026-07-28 03:42:06,7167475,True,7167473,789764352,1028339
2026-07-28 03:43:06,7227497,True,7227497,789764352,1028339
2026-07-28 03:44:06,7287521,True,7287519,789764352,1028339
2026-07-28 03:45:06,7347541,True,7347539,789764352,1028339
2026-07-28 03:46:06,7407560,True,7407558,789764352,1028339
2026-07-28 03:47:06,7467578,True,7467577,789764352,1028339
2026-07-28 03:48:06,7527598,True,7527596,789764352,1028339
2026-07-28 03:49:06,7587620,True,7587619,789764352,1028339
2026-07-28 03:50:06,7647641,True,7647639,789764352,1028339
2026-07-28 03:51:06,7707657,True,7707655,789764352,1028339
2026-07-28 03:52:06,7767680,True,7767678,789764352,1028339
2026-07-28 03:53:06,7827700,True,7827700,789764352,1028339
2026-07-28 03:54:06,7887729,True,7887729,789764352,1028339
2026-07-28 03:55:06,7947752,True,7947750,789764352,1028339
2026-07-28 03:56:06,8007768,True,8007766,789764352,1028339
2026-07-28 03:57:06,8067790,True,8067788,789764352,1028339
2026-07-28 03:58:06,8127817,True,8127816,789764352,1028339
2026-07-28 03:59:06,8187839,True,8187838,789764352,1028339
2026-07-28 04:00:06,8247866,True,8247866,789764352,1028339
2026-07-28 04:01:06,8307889,True,8307887,789764352,1028339
2026-07-28 04:02:06,8367908,True,8367906,789764352,1028339
2026-07-28 04:03:06,8427927,True,8427927,789764352,1028339
2026-07-28 04:04:06,8487947,True,8487945,789764352,1028339
2026-07-28 04:05:06,8547969,True,8547969,789764352,1028339
2026-07-28 04:06:06,8607993,True,8607992,789764352,1028339
2026-07-28 04:07:06,8668018,True,8668017,789764352,1028339
2026-07-28 04:08:06,8728040,True,8728040,789764352,1028339
2026-07-28 04:09:07,8788059,True,8788057,789764352,1028339
2026-07-28 04:10:07,8848075,True,8848073,789764352,1028339
2026-07-28 04:11:07,8908092,True,8908090,789764352,1028339
2026-07-28 04:12:07,8968110,True,8968108,789764352,1028339
2026-07-28 04:13:07,9028132,True,9028132,789764352,1028339
2026-07-28 04:14:07,9088155,True,9088153,789764352,1028339
2026-07-28 04:15:07,9148175,True,9148174,789764352,1028339
2026-07-28 04:16:07,9208198,True,9208195,789764352,1028339
2026-07-28 04:17:07,9268217,True,9268215,789764352,1028339
2026-07-28 04:18:07,9328235,True,9328233,789764352,1028339
2026-07-28 04:19:07,9388256,True,9388256,789764352,1028339
2026-07-28 04:20:07,9448276,True,9448274,789764352,1028339
2026-07-28 04:21:07,9508295,True,9508293,789764352,1028339
2026-07-28 04:22:07,9568317,True,9568316,789764352,1028339
2026-07-28 04:23:07,9628343,True,9628343,789764352,1028339
2026-07-28 04:24:07,9688364,True,9688362,789764352,1028339
2026-07-28 04:25:07,9748382,True,9748379,789764352,1028339
2026-07-28 04:26:07,9808397,True,9808395,789764352,1028339
2026-07-28 04:27:07,9868412,True,9868410,789764352,1028339
2026-07-28 04:28:07,9928428,True,9928426,789764352,1028339
2026-07-28 04:29:07,9988442,True,9988440,789764352,1028339
2026-07-28 04:30:07,10048458,True,10048456,789764352,1028339
2026-07-28 04:31:07,10108475,True,10108473,789764352,1028339
2026-07-28 04:32:07,10168490,True,10168488,789764352,1028339
2026-07-28 04:33:07,10228506,True,10228504,789764352,1028339
2026-07-28 04:34:07,10288522,True,10288520,789764352,1028339
2026-07-28 04:35:07,10348545,True,10348545,789764352,1028339
2026-07-28 04:36:07,10408568,True,10408566,789764352,1028339
2026-07-28 04:37:07,10468588,True,10468586,789764352,1028339
2026-07-28 04:38:07,10528606,True,10528604,789764352,1028339
2026-07-28 04:39:07,10588623,True,10588621,789764352,1028339
2026-07-28 04:40:07,10648642,True,10648641,789764352,1028339
2026-07-28 04:41:07,10708667,True,10708667,789764352,1028339
2026-07-28 04:42:07,10768686,True,10768684,789764352,1028339
2026-07-28 04:43:07,10828711,True,10828710,789764352,1028339
2026-07-28 04:44:07,10888733,True,10888730,789764352,1028339
2026-07-28 04:45:07,10948755,True,10948754,789764352,1028339
2026-07-28 04:46:07,11008776,True,11008774,789764352,1028339
2026-07-28 04:47:07,11068798,True,11068798,789764352,1028339
2026-07-28 04:48:07,11128823,True,11128821,789764352,1028339
2026-07-28 04:49:07,11188847,True,11188847,789764352,1028339
2026-07-28 04:50:07,11248870,True,11248868,789764352,1028339
2026-07-28 04:51:07,11308890,True,11308888,789764352,1028339
2026-07-28 04:52:07,11368912,True,11368910,789764352,1028339
2026-07-28 04:53:07,11428935,True,11428934,789764352,1028339
2026-07-28 04:54:07,11488961,True,11488959,789764352,1028339
2026-07-28 04:55:07,11548978,True,11548976,789764352,1028339
2026-07-28 04:56:07,11608993,True,11608991,789764352,1028339
2026-07-28 04:57:07,11669013,True,11669011,789764352,1028339
2026-07-28 04:58:07,11729031,True,11729030,789764352,1028339
2026-07-28 04:59:07,11789060,True,11789060,789764352,1028339
2026-07-28 05:00:08,11849083,True,11849081,789764352,1028339
2026-07-28 05:01:08,11909100,True,11909098,789764352,1028339
2026-07-28 05:02:08,11969117,True,11969114,789764352,1028339
2026-07-28 05:03:08,12029135,True,12029133,789764352,1028339
2026-07-28 05:04:08,12089154,True,12089152,789764352,1028339
2026-07-28 05:05:08,12149172,True,12149170,789764352,1028339
2026-07-28 05:06:08,12209191,True,12209189,789764352,1028339
2026-07-28 05:07:08,12269215,True,12269215,789764352,1028339
2026-07-28 05:08:08,12329245,True,12329245,789764352,1028339
2026-07-28 05:09:08,12389273,True,12389273,789764352,1028339
2026-07-28 05:10:08,12449292,True,12449290,789764352,1028339
2026-07-28 05:11:08,12509311,True,12509309,789764352,1028339
2026-07-28 05:12:08,12569337,True,12569337,789764352,1028339
2026-07-28 05:13:08,12629364,True,12629364,789764352,1028339
2026-07-28 05:14:08,12689386,True,12689384,789764352,1028339
2026-07-28 05:15:08,12749405,True,12749403,789764352,1028339
2026-07-28 05:16:08,12809427,True,12809427,789764352,1028339
2026-07-28 05:17:08,12869455,True,12869455,789764352,1028339
2026-07-28 05:18:08,12929478,True,12929476,789764352,1028339
2026-07-28 05:19:08,12989502,True,12989502,789764352,1028339
2026-07-28 05:20:08,13049528,True,13049526,789764352,1028339
2026-07-28 05:21:08,13109545,True,13109542,789764352,1028339
2026-07-28 05:22:08,13169562,True,13169560,789764352,1028339
2026-07-28 05:23:08,13229578,True,13229576,789764352,1028339
2026-07-28 05:24:08,13289595,True,13289593,789764352,1028339
2026-07-28 05:25:08,13349611,True,13349609,789764352,1028339
2026-07-28 05:26:08,13409629,True,13409629,789764352,1028339
2026-07-28 05:27:08,13469651,True,13469649,789764352,1028339
2026-07-28 05:28:08,13529673,True,13529673,789764352,1028339
2026-07-28 05:29:08,13589702,True,13589701,789764352,1028339
2026-07-28 05:30:08,13649725,True,13649723,789764352,1028339
2026-07-28 05:31:08,13709741,True,13709740,789764352,1028339
2026-07-28 05:32:08,13769765,True,13769763,789764352,1028339
2026-07-28 05:33:08,13829783,True,13829781,789764352,1028339
2026-07-28 05:34:08,13889802,True,13889800,789764352,1028339
2026-07-28 05:35:08,13949825,True,13949825,789764352,1028339
2026-07-28 05:36:08,14009845,True,14009843,789764352,1028339
2026-07-28 05:37:08,14069859,True,14069857,789764352,1028339
2026-07-28 05:38:08,14129878,True,14129876,789764352,1028339
2026-07-28 05:39:08,14189894,True,14189891,789764352,1028339
2026-07-28 05:40:08,14249915,True,14249913,789764352,1028339
2026-07-28 05:41:08,14309933,True,14309931,789764352,1028339
2026-07-28 05:42:08,14369958,True,14369958,789764352,1028339
2026-07-28 05:43:08,14429982,True,14429982,789764352,1028339
2026-07-28 05:44:08,14490000,True,14489998,789764352,1028339
2026-07-28 05:45:08,14550017,True,14550015,789764352,1028339
2026-07-28 05:46:08,14610035,True,14610033,789764352,1028339
2026-07-28 05:47:08,14670056,True,14670056,789764352,1028339
2026-07-28 05:48:09,14730086,True,14730085,789764352,1028339
2026-07-28 05:49:09,14790110,True,14790109,789764352,1028339
2026-07-28 05:50:09,14850133,True,14850133,789764352,1028339
2026-07-28 05:51:09,14910155,True,14910153,789764352,1028339
2026-07-28 05:52:09,14970174,True,14970172,789764352,1028339
2026-07-28 05:53:09,15030193,True,15030191,789764352,1028339
2026-07-28 05:54:09,15090216,True,15090216,789764352,1028339
2026-07-28 05:55:09,15150245,True,15150245,789764352,1028339
2026-07-28 05:56:09,15210267,True,15210265,789764352,1028339
2026-07-28 05:57:09,15270285,True,15270282,789764352,1028339
2026-07-28 05:58:09,15330308,True,15330308,789764352,1028339
2026-07-28 05:59:09,15390329,True,15390327,789764352,1028339
2026-07-28 06:00:09,15450348,True,15450348,789764352,1028339
2026-07-28 06:01:09,15510376,True,15510376,789764352,1028339
2026-07-28 06:02:09,15570397,True,15570395,789764352,1028339
2026-07-28 06:03:09,15630422,True,15630421,789764352,1028339
2026-07-28 06:04:09,15690443,True,15690441,789764352,1028339
2026-07-28 06:05:09,15750461,True,15750459,789764352,1028339
2026-07-28 06:06:09,15810489,True,15810488,789764352,1028339
2026-07-28 06:07:09,15870512,True,15870510,789764352,1028339
2026-07-28 06:08:09,15930533,True,15930533,789764352,1028339
2026-07-28 06:09:09,15990556,True,15990554,789764352,1028339
2026-07-28 06:10:09,16050574,True,16050572,789764352,1028339
2026-07-28 06:11:09,16110587,True,16110585,789764352,1028339
2026-07-28 06:12:09,16170604,True,16170604,789764352,1028339
2026-07-28 06:13:09,16230627,True,16230627,789764352,1028339
2026-07-28 06:14:09,16290650,True,16290648,789764352,1028339
2026-07-28 06:15:09,16350673,True,16350672,789764352,1028339
2026-07-28 06:16:09,16410698,True,16410696,789764352,1028339
2026-07-28 06:17:09,16470717,True,16470715,789764352,1028339
2026-07-28 06:18:09,16530735,True,16530733,789764352,1028339
2026-07-28 06:19:09,16590757,True,16590757,789764352,1028339
2026-07-28 06:20:09,16650780,True,16650778,789764352,1028339
2026-07-28 06:21:09,16710798,True,16710798,789764352,1028339
2026-07-28 06:22:09,16770825,True,16770824,789764352,1028339
2026-07-28 06:23:09,16830844,True,16830842,789764352,1028339
2026-07-28 06:24:09,16890861,True,16890859,789764352,1028339
2026-07-28 06:25:09,16950878,True,16950875,789764352,1028339
2026-07-28 06:26:09,17010896,True,17010894,789764352,1028339
2026-07-28 06:27:09,17070912,True,17070910,789764352,1028339
2026-07-28 06:28:09,17130929,True,17130927,789764352,1028339
2026-07-28 06:29:09,17190947,True,17190945,789764352,1028339
2026-07-28 06:30:09,17250966,True,17250964,789764352,1028339
2026-07-28 06:31:09,17310985,True,17310983,789764352,1028339
2026-07-28 06:32:09,17371003,True,17371001,789764352,1028339
2026-07-28 06:33:09,17431023,True,17431021,789764352,1028339
2026-07-28 06:34:09,17491047,True,17491046,789764352,1028339
2026-07-28 06:35:09,17551074,True,17551074,789764352,1028339
2026-07-28 06:36:10,17611098,True,17611096,789764352,1028339
2026-07-28 06:37:10,17671122,True,17671122,789764352,1028339
2026-07-28 06:38:10,17731145,True,17731143,789764352,1028339
2026-07-28 06:39:10,17791173,True,17791172,789764352,1028339
2026-07-28 06:40:10,17851193,True,17851192,789764352,1028339
2026-07-28 06:41:10,17911221,True,17911221,789764352,1028339
2026-07-28 06:42:10,17971243,True,17971241,789764352,1028339
2026-07-28 06:43:10,18031261,True,18031258,789764352,1028339
2026-07-28 06:44:10,18091285,True,18091285,789764352,1028339
2026-07-28 06:45:10,18151306,True,18151304,789764352,1028339
2026-07-28 06:46:10,18211322,True,18211320,789764352,1028339
2026-07-28 06:47:10,18271342,True,18271340,789764352,1028339
2026-07-28 06:48:10,18331360,True,18331358,789764352,1028339
2026-07-28 06:49:10,18391383,True,18391383,789764352,1028339
2026-07-28 06:50:10,18451405,True,18451403,789764352,1028339
2026-07-28 06:51:10,18511423,True,18511421,789764352,1028339
2026-07-28 06:52:10,18571443,True,18571442,789764352,1028339
2026-07-28 06:53:10,18631472,True,18631472,789764352,1028339
2026-07-28 06:54:10,18691494,True,18691492,789764352,1028339
2026-07-28 06:55:10,18751520,True,18751520,789764352,1028339
2026-07-28 06:56:10,18811545,True,18811545,789764352,1028339
2026-07-28 06:57:10,18871568,True,18871566,789764352,1028339
2026-07-28 06:58:10,18931586,True,18931584,789764352,1028339
2026-07-28 06:59:10,18991609,True,18991608,789764352,1028339
2026-07-28 07:00:10,19051633,True,19051632,789764352,1028339
2026-07-28 07:01:10,19111652,True,19111649,789764352,1028339
2026-07-28 07:02:10,19171669,True,19171667,789764352,1028339
2026-07-28 07:03:10,19231688,True,19231686,789764352,1028339
2026-07-28 07:04:10,19291705,True,19291703,789764352,1028339
2026-07-28 07:05:10,19351730,True,19351730,789764352,1028339
2026-07-28 07:06:10,19411758,True,19411758,789764352,1028339
2026-07-28 07:07:10,19471781,True,19471779,789764352,1028339
2026-07-28 07:08:10,19531799,True,19531797,789764352,1028339
2026-07-28 07:09:10,19591815,True,19591815,789764352,1028339
2026-07-28 07:10:10,19651838,True,19651837,789764352,1028339
2026-07-28 07:11:10,19711862,True,19711862,789764352,1028339
2026-07-28 07:12:10,19771883,True,19771881,789764352,1028339
1 timestamp nCycles bSync nReturnedSeq nMismatch nProcMismatch
2 2026-07-28 02:13:04 1825622 False 0 648246528 844072
3 2026-07-28 02:14:04 1885662 False 0 694359552 904116
4 2026-07-28 02:15:04 1945680 False 0 740451072 964130
5 2026-07-28 02:16:04 2005694 False 0 786541824 1024144
6 2026-07-28 02:17:04 2065708 True 2065707 789764352 1028339
7 2026-07-28 02:18:04 2125730 True 2125727 789764352 1028339
8 2026-07-28 02:19:04 2185753 True 2185751 789764352 1028339
9 2026-07-28 02:20:04 2245766 True 2245766 789764352 1028339
10 2026-07-28 02:21:04 2305794 True 2305794 789764352 1028339
11 2026-07-28 02:22:04 2365820 True 2365819 789764352 1028339
12 2026-07-28 02:23:04 2425842 True 2425840 789764352 1028339
13 2026-07-28 02:24:04 2485861 True 2485861 789764352 1028339
14 2026-07-28 02:25:04 2545882 True 2545880 789764352 1028339
15 2026-07-28 02:26:04 2605900 True 2605898 789764352 1028339
16 2026-07-28 02:27:04 2665917 True 2665914 789764352 1028339
17 2026-07-28 02:28:04 2725936 True 2725934 789764352 1028339
18 2026-07-28 02:29:04 2785961 True 2785960 789764352 1028339
19 2026-07-28 02:30:04 2845983 True 2845981 789764352 1028339
20 2026-07-28 02:31:04 2906006 True 2906006 789764352 1028339
21 2026-07-28 02:32:05 2966030 True 2966028 789764352 1028339
22 2026-07-28 02:33:05 3026050 True 3026048 789764352 1028339
23 2026-07-28 02:34:05 3086068 True 3086066 789764352 1028339
24 2026-07-28 02:35:05 3146087 True 3146087 789764352 1028339
25 2026-07-28 02:36:05 3206113 True 3206111 789764352 1028339
26 2026-07-28 02:37:05 3266139 True 3266139 789764352 1028339
27 2026-07-28 02:38:05 3326161 True 3326159 789764352 1028339
28 2026-07-28 02:39:05 3386183 True 3386181 789764352 1028339
29 2026-07-28 02:40:05 3446206 True 3446206 789764352 1028339
30 2026-07-28 02:41:05 3506232 True 3506232 789764352 1028339
31 2026-07-28 02:42:05 3566251 True 3566249 789764352 1028339
32 2026-07-28 02:43:05 3626266 True 3626264 789764352 1028339
33 2026-07-28 02:44:05 3686283 True 3686281 789764352 1028339
34 2026-07-28 02:45:05 3746311 True 3746310 789764352 1028339
35 2026-07-28 02:46:05 3806331 True 3806329 789764352 1028339
36 2026-07-28 02:47:05 3866346 True 3866344 789764352 1028339
37 2026-07-28 02:48:05 3926364 True 3926364 789764352 1028339
38 2026-07-28 02:49:05 3986384 True 3986382 789764352 1028339
39 2026-07-28 02:50:05 4046400 True 4046400 789764352 1028339
40 2026-07-28 02:51:05 4106421 True 4106419 789764352 1028339
41 2026-07-28 02:52:05 4166439 True 4166439 789764352 1028339
42 2026-07-28 02:53:05 4226466 True 4226466 789764352 1028339
43 2026-07-28 02:54:05 4286494 True 4286493 789764352 1028339
44 2026-07-28 02:55:05 4346518 True 4346518 789764352 1028339
45 2026-07-28 02:56:05 4406539 True 4406537 789764352 1028339
46 2026-07-28 02:57:05 4466565 True 4466565 789764352 1028339
47 2026-07-28 02:58:05 4526588 True 4526586 789764352 1028339
48 2026-07-28 02:59:05 4586605 True 4586603 789764352 1028339
49 2026-07-28 03:00:05 4646620 True 4646618 789764352 1028339
50 2026-07-28 03:01:05 4706637 True 4706636 789764352 1028339
51 2026-07-28 03:02:05 4766661 True 4766661 789764352 1028339
52 2026-07-28 03:03:05 4826684 True 4826681 789764352 1028339
53 2026-07-28 03:04:05 4886701 True 4886699 789764352 1028339
54 2026-07-28 03:05:05 4946724 True 4946724 789764352 1028339
55 2026-07-28 03:06:05 5006746 True 5006744 789764352 1028339
56 2026-07-28 03:07:05 5066761 True 5066759 789764352 1028339
57 2026-07-28 03:08:05 5126775 True 5126773 789764352 1028339
58 2026-07-28 03:09:05 5186795 True 5186795 789764352 1028339
59 2026-07-28 03:10:05 5246822 True 5246822 789764352 1028339
60 2026-07-28 03:11:05 5306841 True 5306839 789764352 1028339
61 2026-07-28 03:12:05 5366855 True 5366852 789764352 1028339
62 2026-07-28 03:13:05 5426873 True 5426870 789764352 1028339
63 2026-07-28 03:14:05 5486893 True 5486891 789764352 1028339
64 2026-07-28 03:15:05 5546919 True 5546919 789764352 1028339
65 2026-07-28 03:16:05 5606940 True 5606938 789764352 1028339
66 2026-07-28 03:17:05 5666962 True 5666962 789764352 1028339
67 2026-07-28 03:18:05 5727000 True 5727000 789764352 1028339
68 2026-07-28 03:19:05 5787020 True 5787018 789764352 1028339
69 2026-07-28 03:20:06 5847036 True 5847034 789764352 1028339
70 2026-07-28 03:21:06 5907050 True 5907047 789764352 1028339
71 2026-07-28 03:22:06 5967067 True 5967067 789764352 1028339
72 2026-07-28 03:23:06 6027086 True 6027084 789764352 1028339
73 2026-07-28 03:24:06 6087101 True 6087099 789764352 1028339
74 2026-07-28 03:25:06 6147119 True 6147117 789764352 1028339
75 2026-07-28 03:26:06 6207138 True 6207136 789764352 1028339
76 2026-07-28 03:27:06 6267161 True 6267161 789764352 1028339
77 2026-07-28 03:28:06 6327180 True 6327178 789764352 1028339
78 2026-07-28 03:29:06 6387197 True 6387195 789764352 1028339
79 2026-07-28 03:30:06 6447216 True 6447215 789764352 1028339
80 2026-07-28 03:31:06 6507241 True 6507241 789764352 1028339
81 2026-07-28 03:32:06 6567266 True 6567266 789764352 1028339
82 2026-07-28 03:33:06 6627289 True 6627287 789764352 1028339
83 2026-07-28 03:34:06 6687315 True 6687315 789764352 1028339
84 2026-07-28 03:35:06 6747338 True 6747336 789764352 1028339
85 2026-07-28 03:36:06 6807356 True 6807354 789764352 1028339
86 2026-07-28 03:37:06 6867377 True 6867375 789764352 1028339
87 2026-07-28 03:38:06 6927395 True 6927393 789764352 1028339
88 2026-07-28 03:39:06 6987416 True 6987416 789764352 1028339
89 2026-07-28 03:40:06 7047439 True 7047436 789764352 1028339
90 2026-07-28 03:41:06 7107458 True 7107456 789764352 1028339
91 2026-07-28 03:42:06 7167475 True 7167473 789764352 1028339
92 2026-07-28 03:43:06 7227497 True 7227497 789764352 1028339
93 2026-07-28 03:44:06 7287521 True 7287519 789764352 1028339
94 2026-07-28 03:45:06 7347541 True 7347539 789764352 1028339
95 2026-07-28 03:46:06 7407560 True 7407558 789764352 1028339
96 2026-07-28 03:47:06 7467578 True 7467577 789764352 1028339
97 2026-07-28 03:48:06 7527598 True 7527596 789764352 1028339
98 2026-07-28 03:49:06 7587620 True 7587619 789764352 1028339
99 2026-07-28 03:50:06 7647641 True 7647639 789764352 1028339
100 2026-07-28 03:51:06 7707657 True 7707655 789764352 1028339
101 2026-07-28 03:52:06 7767680 True 7767678 789764352 1028339
102 2026-07-28 03:53:06 7827700 True 7827700 789764352 1028339
103 2026-07-28 03:54:06 7887729 True 7887729 789764352 1028339
104 2026-07-28 03:55:06 7947752 True 7947750 789764352 1028339
105 2026-07-28 03:56:06 8007768 True 8007766 789764352 1028339
106 2026-07-28 03:57:06 8067790 True 8067788 789764352 1028339
107 2026-07-28 03:58:06 8127817 True 8127816 789764352 1028339
108 2026-07-28 03:59:06 8187839 True 8187838 789764352 1028339
109 2026-07-28 04:00:06 8247866 True 8247866 789764352 1028339
110 2026-07-28 04:01:06 8307889 True 8307887 789764352 1028339
111 2026-07-28 04:02:06 8367908 True 8367906 789764352 1028339
112 2026-07-28 04:03:06 8427927 True 8427927 789764352 1028339
113 2026-07-28 04:04:06 8487947 True 8487945 789764352 1028339
114 2026-07-28 04:05:06 8547969 True 8547969 789764352 1028339
115 2026-07-28 04:06:06 8607993 True 8607992 789764352 1028339
116 2026-07-28 04:07:06 8668018 True 8668017 789764352 1028339
117 2026-07-28 04:08:06 8728040 True 8728040 789764352 1028339
118 2026-07-28 04:09:07 8788059 True 8788057 789764352 1028339
119 2026-07-28 04:10:07 8848075 True 8848073 789764352 1028339
120 2026-07-28 04:11:07 8908092 True 8908090 789764352 1028339
121 2026-07-28 04:12:07 8968110 True 8968108 789764352 1028339
122 2026-07-28 04:13:07 9028132 True 9028132 789764352 1028339
123 2026-07-28 04:14:07 9088155 True 9088153 789764352 1028339
124 2026-07-28 04:15:07 9148175 True 9148174 789764352 1028339
125 2026-07-28 04:16:07 9208198 True 9208195 789764352 1028339
126 2026-07-28 04:17:07 9268217 True 9268215 789764352 1028339
127 2026-07-28 04:18:07 9328235 True 9328233 789764352 1028339
128 2026-07-28 04:19:07 9388256 True 9388256 789764352 1028339
129 2026-07-28 04:20:07 9448276 True 9448274 789764352 1028339
130 2026-07-28 04:21:07 9508295 True 9508293 789764352 1028339
131 2026-07-28 04:22:07 9568317 True 9568316 789764352 1028339
132 2026-07-28 04:23:07 9628343 True 9628343 789764352 1028339
133 2026-07-28 04:24:07 9688364 True 9688362 789764352 1028339
134 2026-07-28 04:25:07 9748382 True 9748379 789764352 1028339
135 2026-07-28 04:26:07 9808397 True 9808395 789764352 1028339
136 2026-07-28 04:27:07 9868412 True 9868410 789764352 1028339
137 2026-07-28 04:28:07 9928428 True 9928426 789764352 1028339
138 2026-07-28 04:29:07 9988442 True 9988440 789764352 1028339
139 2026-07-28 04:30:07 10048458 True 10048456 789764352 1028339
140 2026-07-28 04:31:07 10108475 True 10108473 789764352 1028339
141 2026-07-28 04:32:07 10168490 True 10168488 789764352 1028339
142 2026-07-28 04:33:07 10228506 True 10228504 789764352 1028339
143 2026-07-28 04:34:07 10288522 True 10288520 789764352 1028339
144 2026-07-28 04:35:07 10348545 True 10348545 789764352 1028339
145 2026-07-28 04:36:07 10408568 True 10408566 789764352 1028339
146 2026-07-28 04:37:07 10468588 True 10468586 789764352 1028339
147 2026-07-28 04:38:07 10528606 True 10528604 789764352 1028339
148 2026-07-28 04:39:07 10588623 True 10588621 789764352 1028339
149 2026-07-28 04:40:07 10648642 True 10648641 789764352 1028339
150 2026-07-28 04:41:07 10708667 True 10708667 789764352 1028339
151 2026-07-28 04:42:07 10768686 True 10768684 789764352 1028339
152 2026-07-28 04:43:07 10828711 True 10828710 789764352 1028339
153 2026-07-28 04:44:07 10888733 True 10888730 789764352 1028339
154 2026-07-28 04:45:07 10948755 True 10948754 789764352 1028339
155 2026-07-28 04:46:07 11008776 True 11008774 789764352 1028339
156 2026-07-28 04:47:07 11068798 True 11068798 789764352 1028339
157 2026-07-28 04:48:07 11128823 True 11128821 789764352 1028339
158 2026-07-28 04:49:07 11188847 True 11188847 789764352 1028339
159 2026-07-28 04:50:07 11248870 True 11248868 789764352 1028339
160 2026-07-28 04:51:07 11308890 True 11308888 789764352 1028339
161 2026-07-28 04:52:07 11368912 True 11368910 789764352 1028339
162 2026-07-28 04:53:07 11428935 True 11428934 789764352 1028339
163 2026-07-28 04:54:07 11488961 True 11488959 789764352 1028339
164 2026-07-28 04:55:07 11548978 True 11548976 789764352 1028339
165 2026-07-28 04:56:07 11608993 True 11608991 789764352 1028339
166 2026-07-28 04:57:07 11669013 True 11669011 789764352 1028339
167 2026-07-28 04:58:07 11729031 True 11729030 789764352 1028339
168 2026-07-28 04:59:07 11789060 True 11789060 789764352 1028339
169 2026-07-28 05:00:08 11849083 True 11849081 789764352 1028339
170 2026-07-28 05:01:08 11909100 True 11909098 789764352 1028339
171 2026-07-28 05:02:08 11969117 True 11969114 789764352 1028339
172 2026-07-28 05:03:08 12029135 True 12029133 789764352 1028339
173 2026-07-28 05:04:08 12089154 True 12089152 789764352 1028339
174 2026-07-28 05:05:08 12149172 True 12149170 789764352 1028339
175 2026-07-28 05:06:08 12209191 True 12209189 789764352 1028339
176 2026-07-28 05:07:08 12269215 True 12269215 789764352 1028339
177 2026-07-28 05:08:08 12329245 True 12329245 789764352 1028339
178 2026-07-28 05:09:08 12389273 True 12389273 789764352 1028339
179 2026-07-28 05:10:08 12449292 True 12449290 789764352 1028339
180 2026-07-28 05:11:08 12509311 True 12509309 789764352 1028339
181 2026-07-28 05:12:08 12569337 True 12569337 789764352 1028339
182 2026-07-28 05:13:08 12629364 True 12629364 789764352 1028339
183 2026-07-28 05:14:08 12689386 True 12689384 789764352 1028339
184 2026-07-28 05:15:08 12749405 True 12749403 789764352 1028339
185 2026-07-28 05:16:08 12809427 True 12809427 789764352 1028339
186 2026-07-28 05:17:08 12869455 True 12869455 789764352 1028339
187 2026-07-28 05:18:08 12929478 True 12929476 789764352 1028339
188 2026-07-28 05:19:08 12989502 True 12989502 789764352 1028339
189 2026-07-28 05:20:08 13049528 True 13049526 789764352 1028339
190 2026-07-28 05:21:08 13109545 True 13109542 789764352 1028339
191 2026-07-28 05:22:08 13169562 True 13169560 789764352 1028339
192 2026-07-28 05:23:08 13229578 True 13229576 789764352 1028339
193 2026-07-28 05:24:08 13289595 True 13289593 789764352 1028339
194 2026-07-28 05:25:08 13349611 True 13349609 789764352 1028339
195 2026-07-28 05:26:08 13409629 True 13409629 789764352 1028339
196 2026-07-28 05:27:08 13469651 True 13469649 789764352 1028339
197 2026-07-28 05:28:08 13529673 True 13529673 789764352 1028339
198 2026-07-28 05:29:08 13589702 True 13589701 789764352 1028339
199 2026-07-28 05:30:08 13649725 True 13649723 789764352 1028339
200 2026-07-28 05:31:08 13709741 True 13709740 789764352 1028339
201 2026-07-28 05:32:08 13769765 True 13769763 789764352 1028339
202 2026-07-28 05:33:08 13829783 True 13829781 789764352 1028339
203 2026-07-28 05:34:08 13889802 True 13889800 789764352 1028339
204 2026-07-28 05:35:08 13949825 True 13949825 789764352 1028339
205 2026-07-28 05:36:08 14009845 True 14009843 789764352 1028339
206 2026-07-28 05:37:08 14069859 True 14069857 789764352 1028339
207 2026-07-28 05:38:08 14129878 True 14129876 789764352 1028339
208 2026-07-28 05:39:08 14189894 True 14189891 789764352 1028339
209 2026-07-28 05:40:08 14249915 True 14249913 789764352 1028339
210 2026-07-28 05:41:08 14309933 True 14309931 789764352 1028339
211 2026-07-28 05:42:08 14369958 True 14369958 789764352 1028339
212 2026-07-28 05:43:08 14429982 True 14429982 789764352 1028339
213 2026-07-28 05:44:08 14490000 True 14489998 789764352 1028339
214 2026-07-28 05:45:08 14550017 True 14550015 789764352 1028339
215 2026-07-28 05:46:08 14610035 True 14610033 789764352 1028339
216 2026-07-28 05:47:08 14670056 True 14670056 789764352 1028339
217 2026-07-28 05:48:09 14730086 True 14730085 789764352 1028339
218 2026-07-28 05:49:09 14790110 True 14790109 789764352 1028339
219 2026-07-28 05:50:09 14850133 True 14850133 789764352 1028339
220 2026-07-28 05:51:09 14910155 True 14910153 789764352 1028339
221 2026-07-28 05:52:09 14970174 True 14970172 789764352 1028339
222 2026-07-28 05:53:09 15030193 True 15030191 789764352 1028339
223 2026-07-28 05:54:09 15090216 True 15090216 789764352 1028339
224 2026-07-28 05:55:09 15150245 True 15150245 789764352 1028339
225 2026-07-28 05:56:09 15210267 True 15210265 789764352 1028339
226 2026-07-28 05:57:09 15270285 True 15270282 789764352 1028339
227 2026-07-28 05:58:09 15330308 True 15330308 789764352 1028339
228 2026-07-28 05:59:09 15390329 True 15390327 789764352 1028339
229 2026-07-28 06:00:09 15450348 True 15450348 789764352 1028339
230 2026-07-28 06:01:09 15510376 True 15510376 789764352 1028339
231 2026-07-28 06:02:09 15570397 True 15570395 789764352 1028339
232 2026-07-28 06:03:09 15630422 True 15630421 789764352 1028339
233 2026-07-28 06:04:09 15690443 True 15690441 789764352 1028339
234 2026-07-28 06:05:09 15750461 True 15750459 789764352 1028339
235 2026-07-28 06:06:09 15810489 True 15810488 789764352 1028339
236 2026-07-28 06:07:09 15870512 True 15870510 789764352 1028339
237 2026-07-28 06:08:09 15930533 True 15930533 789764352 1028339
238 2026-07-28 06:09:09 15990556 True 15990554 789764352 1028339
239 2026-07-28 06:10:09 16050574 True 16050572 789764352 1028339
240 2026-07-28 06:11:09 16110587 True 16110585 789764352 1028339
241 2026-07-28 06:12:09 16170604 True 16170604 789764352 1028339
242 2026-07-28 06:13:09 16230627 True 16230627 789764352 1028339
243 2026-07-28 06:14:09 16290650 True 16290648 789764352 1028339
244 2026-07-28 06:15:09 16350673 True 16350672 789764352 1028339
245 2026-07-28 06:16:09 16410698 True 16410696 789764352 1028339
246 2026-07-28 06:17:09 16470717 True 16470715 789764352 1028339
247 2026-07-28 06:18:09 16530735 True 16530733 789764352 1028339
248 2026-07-28 06:19:09 16590757 True 16590757 789764352 1028339
249 2026-07-28 06:20:09 16650780 True 16650778 789764352 1028339
250 2026-07-28 06:21:09 16710798 True 16710798 789764352 1028339
251 2026-07-28 06:22:09 16770825 True 16770824 789764352 1028339
252 2026-07-28 06:23:09 16830844 True 16830842 789764352 1028339
253 2026-07-28 06:24:09 16890861 True 16890859 789764352 1028339
254 2026-07-28 06:25:09 16950878 True 16950875 789764352 1028339
255 2026-07-28 06:26:09 17010896 True 17010894 789764352 1028339
256 2026-07-28 06:27:09 17070912 True 17070910 789764352 1028339
257 2026-07-28 06:28:09 17130929 True 17130927 789764352 1028339
258 2026-07-28 06:29:09 17190947 True 17190945 789764352 1028339
259 2026-07-28 06:30:09 17250966 True 17250964 789764352 1028339
260 2026-07-28 06:31:09 17310985 True 17310983 789764352 1028339
261 2026-07-28 06:32:09 17371003 True 17371001 789764352 1028339
262 2026-07-28 06:33:09 17431023 True 17431021 789764352 1028339
263 2026-07-28 06:34:09 17491047 True 17491046 789764352 1028339
264 2026-07-28 06:35:09 17551074 True 17551074 789764352 1028339
265 2026-07-28 06:36:10 17611098 True 17611096 789764352 1028339
266 2026-07-28 06:37:10 17671122 True 17671122 789764352 1028339
267 2026-07-28 06:38:10 17731145 True 17731143 789764352 1028339
268 2026-07-28 06:39:10 17791173 True 17791172 789764352 1028339
269 2026-07-28 06:40:10 17851193 True 17851192 789764352 1028339
270 2026-07-28 06:41:10 17911221 True 17911221 789764352 1028339
271 2026-07-28 06:42:10 17971243 True 17971241 789764352 1028339
272 2026-07-28 06:43:10 18031261 True 18031258 789764352 1028339
273 2026-07-28 06:44:10 18091285 True 18091285 789764352 1028339
274 2026-07-28 06:45:10 18151306 True 18151304 789764352 1028339
275 2026-07-28 06:46:10 18211322 True 18211320 789764352 1028339
276 2026-07-28 06:47:10 18271342 True 18271340 789764352 1028339
277 2026-07-28 06:48:10 18331360 True 18331358 789764352 1028339
278 2026-07-28 06:49:10 18391383 True 18391383 789764352 1028339
279 2026-07-28 06:50:10 18451405 True 18451403 789764352 1028339
280 2026-07-28 06:51:10 18511423 True 18511421 789764352 1028339
281 2026-07-28 06:52:10 18571443 True 18571442 789764352 1028339
282 2026-07-28 06:53:10 18631472 True 18631472 789764352 1028339
283 2026-07-28 06:54:10 18691494 True 18691492 789764352 1028339
284 2026-07-28 06:55:10 18751520 True 18751520 789764352 1028339
285 2026-07-28 06:56:10 18811545 True 18811545 789764352 1028339
286 2026-07-28 06:57:10 18871568 True 18871566 789764352 1028339
287 2026-07-28 06:58:10 18931586 True 18931584 789764352 1028339
288 2026-07-28 06:59:10 18991609 True 18991608 789764352 1028339
289 2026-07-28 07:00:10 19051633 True 19051632 789764352 1028339
290 2026-07-28 07:01:10 19111652 True 19111649 789764352 1028339
291 2026-07-28 07:02:10 19171669 True 19171667 789764352 1028339
292 2026-07-28 07:03:10 19231688 True 19231686 789764352 1028339
293 2026-07-28 07:04:10 19291705 True 19291703 789764352 1028339
294 2026-07-28 07:05:10 19351730 True 19351730 789764352 1028339
295 2026-07-28 07:06:10 19411758 True 19411758 789764352 1028339
296 2026-07-28 07:07:10 19471781 True 19471779 789764352 1028339
297 2026-07-28 07:08:10 19531799 True 19531797 789764352 1028339
298 2026-07-28 07:09:10 19591815 True 19591815 789764352 1028339
299 2026-07-28 07:10:10 19651838 True 19651837 789764352 1028339
300 2026-07-28 07:11:10 19711862 True 19711862 789764352 1028339
301 2026-07-28 07:12:10 19771883 True 19771881 789764352 1028339

29
scripts/d2000-deploy.ps1 Normal file
View File

@ -0,0 +1,29 @@
#Requires -Version 5.1
# Build + deploy el6695-rt for Phytium D2000 (aarch64-unknown-linux-musl).
# Usage: .\d2000-deploy.ps1 [-JumpHost user@host] [-TargetHost user@ip] [-RemoteDir /root/gateway]
param(
[string]$JumpHost = 'tonycao@192.168.58.8', # TODO: replace with jump host #2 for D2000
[string]$TargetHost = 'root@192.168.68.100', # TODO: replace with D2000 address
[string]$RemoteName = 'el6695_rt_d2000',
[string]$RemoteDir = '/root/gateway',
[string]$Variant = '800b' # payload variant marker for filename only
)
$ErrorActionPreference = 'Stop'
$repo = 'C:\Users\tonycao\work\ethercat-linux'
$bin = Join-Path $repo 'target\aarch64-unknown-linux-musl\release\el6695-rt'
Write-Host '=== D2000 (aarch64) build ==='
cargo build --release --target aarch64-unknown-linux-musl --manifest-path (Join-Path $repo 'Cargo.toml')
if ($LASTEXITCODE -ne 0) { throw 'build failed' }
# verify ELF arch = AArch64 (0xB7)
$d = [System.IO.File]::ReadAllBytes($bin)
if ($d[18] -ne 0xB7 -or $d[19] -ne 0x00) { throw 'not an AArch64 binary!' }
Write-Host ('aarch64 ELF OK, {0} bytes' -f $d.Length)
Write-Host '=== deploy ==='
$remote = "$RemoteDir/$RemoteName"
scp -o ConnectTimeout=10 -o BatchMode=yes -J $JumpHost $bin "${TargetHost}:$remote"
ssh -o BatchMode=yes -J $JumpHost $TargetHost "chmod +x $remote; $remote --help 2>&1 | head -3 || true"
Write-Host "deployed to ${TargetHost}:$remote"

23
scripts/tc-ads-lag.ps1 Normal file
View File

@ -0,0 +1,23 @@
param([int]$Samples = 20, [int]$IntervalMs = 500)
$ErrorActionPreference = 'Stop'
$adsDll = 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
Add-Type -Path $adsDll
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
function ReadSym($name, $type) {
$h = $c.CreateVariableHandle($name)
$v = $c.ReadAny($h, $type)
$c.DeleteVariableHandle($h)
return $v
}
$lags = New-Object System.Collections.Generic.List[int]
for ($k = 0; $k -lt $Samples; $k++) {
$lag = ReadSym 'GVL_Sync.nMatchLag' ([int])
$bs = ReadSym 'GVL_Sync.bSync' ([bool])
$lags.Add($lag)
Write-Host ("{0} lag={1,4} cyc bSync={2}" -f (Get-Date -Format 'HH:mm:ss'), $lag, $bs)
if ($k -lt $Samples - 1) { Start-Sleep -Milliseconds $IntervalMs }
}
$s = $lags | Sort-Object
Write-Host ("min={0} p50={1} max={2} mean={3:F2}" -f $s[0], $s[[int]($Samples/2)], $s[$Samples-1], (($lags | Measure-Object -Average).Average))
$c.Dispose()

View File

@ -0,0 +1,15 @@
#Requires -RunAsAdministrator
Add-Type -Path 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
$h = $c.CreateVariableHandle('GVL_Sync.nMismatch')
$h2 = $c.CreateVariableHandle('GVL_Sync.bSync')
$m1 = $c.ReadAny($h, [int]); $b1 = $c.ReadAny($h2, [bool])
Write-Host ("t+15s : bSync=$b1 nMismatch=$m1")
Start-Sleep 30
$m2 = $c.ReadAny($h, [int]); $b2 = $c.ReadAny($h2, [bool])
Write-Host ("t+45s : bSync=$b2 nMismatch=$m2 delta=$($m2-$m1)")
Start-Sleep 30
$m3 = $c.ReadAny($h, [int]); $b3 = $c.ReadAny($h2, [bool])
Write-Host ("t+75s : bSync=$b3 nMismatch=$m3 delta=$($m3-$m2)")
$c.Dispose()

13
scripts/tc-ads-quick.ps1 Normal file
View File

@ -0,0 +1,13 @@
$ErrorActionPreference = 'Continue'
Add-Type -Path 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
$h = $c.CreateVariableHandle('GVL_Sync.nCycles')
$n1 = $c.ReadAny($h, [uint64])
Start-Sleep -Milliseconds 5000
$n2 = $c.ReadAny($h, [uint64])
$c.DeleteVariableHandle($h)
Write-Host ("nCycles rate = {0} Hz" -f [math]::Round(($n2 - $n1) / 5.0, 1))
$bs = $c.ReadAny($c.CreateVariableHandle('GVL_Sync.bSync'), [bool])
Write-Host "bSync = $bs"
$c.Dispose()

15
scripts/tc-ads-setrun.ps1 Normal file
View File

@ -0,0 +1,15 @@
$ErrorActionPreference = 'Continue'
Add-Type -Path 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 10000)
Write-Host "before: $($c.ReadState().AdsState)"
try {
$c.WriteControl((New-Object TwinCAT.Ads.StateInfo([TwinCAT.Ads.AdsState]::Run, 0)))
Write-Host "WriteControl Run sent"
} catch { Write-Host "err: $_" }
$c.Dispose()
Start-Sleep 10
$c2 = New-Object TwinCAT.Ads.TcAdsClient
$c2.Connect('169.254.176.217.1.1', 10000)
Write-Host "after: $($c2.ReadState().AdsState)"
$c2.Dispose()

32
scripts/tc-ads-soak.ps1 Normal file
View File

@ -0,0 +1,32 @@
param([int]$Minutes = 190, [string]$LogFile = "$env:TEMP\soak.log")
$ErrorActionPreference = 'Stop'
$adsDll = 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
Add-Type -Path $adsDll
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
function ReadSym($name, $type) {
$h = $c.CreateVariableHandle($name)
$v = $c.ReadAny($h, $type)
$c.DeleteVariableHandle($h)
return $v
}
$prevMism = $null
$prevCyc = $null
for ($k = 0; $k -lt $Minutes; $k++) {
try {
$nc = ReadSym 'GVL_Sync.nCycles' ([uint64])
$bs = ReadSym 'GVL_Sync.bSync' ([bool])
$nm = ReadSym 'GVL_Sync.nMismatch' ([int])
$lag = ReadSym 'GVL_Sync.nMatchLag' ([int])
$dmism = if ($null -ne $prevMism) { $nm - $prevMism } else { 0 }
$rate = if ($null -ne $prevCyc) { ([int64]$nc - $prevCyc) / 60.0 } else { 0 }
$prevMism = $nm
$prevCyc = [int64]$nc
$line = "{0} k={1,3} nCycles={2,-9} rate={3,7:F1}/s bSync={4} lag={5,4} nMismatch={6} dMism={7}" -f (Get-Date -Format 'HH:mm:ss'), $k, $nc, $rate, $bs, $lag, $nm, $dmism
} catch {
$line = "{0} k={1,3} ADS ERROR: {2}" -f (Get-Date -Format 'HH:mm:ss'), $k, $_.Exception.Message
}
Add-Content -Path $LogFile -Value $line
Start-Sleep -Seconds 60
}
$c.Dispose()

13
scripts/tc-ads-state.ps1 Normal file
View File

@ -0,0 +1,13 @@
$ErrorActionPreference = 'Continue'
Add-Type -Path 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
foreach ($port in @(10000, 851)) {
try {
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', $port)
$st = $c.ReadState()
Write-Host "port $port : AdsState=$($st.AdsState) DeviceState=$($st.DeviceState)"
$c.Dispose()
} catch {
Write-Host "port $port : ERR $($_.Exception.Message)"
}
}

View File

@ -0,0 +1,48 @@
#Requires -RunAsAdministrator
#Requires -Version 5.1
$ErrorActionPreference = 'Continue'
$adsDll = 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
Add-Type -Path $adsDll -ErrorAction Stop
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
function ReadSym($name, $type) {
$h = $c.CreateVariableHandle($name)
$v = $c.ReadAny($h, $type)
$c.DeleteVariableHandle($h)
return $v
}
function ReadBytes($name, $len) {
$h = $c.CreateVariableHandle($name)
$arr = $c.ReadAny($h, [byte[]], @($len))
$c.DeleteVariableHandle($h)
return $arr
}
$nc = ReadSym 'GVL_Sync.nCycles' ([uint64])
$bs = ReadSym 'GVL_Sync.bSync' ([bool])
$nrs = ReadSym 'GVL_Sync.nReturnedSeq' ([uint64])
$nm = ReadSym 'GVL_Sync.nMismatch' ([int])
$in = ReadBytes 'GVL_Sync.aIn1' 800
$out = ReadBytes 'GVL_Sync.aOut1' 800
$nzIn = ($in | Where-Object { $_ -ne 0 }).Count
$nzOut = ($out | Where-Object { $_ -ne 0 }).Count
Write-Host ("nCycles={0} bSync={1} nReturnedSeq={2} lag={3} nMismatch={4}" -f $nc, $bs, $nrs, ($nc - $nrs), $nm)
Write-Host ("aIn1 nonzero = {0}/800" -f $nzIn)
Write-Host ("aOut1 nonzero = {0}/800" -f $nzOut)
# per-byte pattern check against nReturnedSeq: expected[i] = (nReturnedSeq + i) mod 256, pattern region
$bad = 0
$badIdx = @()
for ($i = 16; $i -lt 800; $i++) {
if ($i -ge 264 -and $i -le 279) { continue }
$exp = [byte](($nrs + $i) % 256)
if ($in[$i] -ne $exp) { $bad++; if ($badIdx.Count -lt 8) { $badIdx += $i } }
}
Write-Host ("pattern[16..799 excl 264..279] mismatches = {0} {1}" -f $bad, $(if ($badIdx.Count) { 'at ' + ($badIdx -join ',') } else { '' }))
# show window samples at several offsets
foreach ($off in @(0, 16, 128, 256, 288, 512, 768)) {
$seg = [BitConverter]::ToString($in[$off..($off + 7)])
Write-Host ("aIn1[{0,3}..{1,3}] = {2}" -f $off, ($off + 7), $seg)
}
$c.Dispose()

View File

@ -0,0 +1,24 @@
$ErrorActionPreference = 'Stop'
$adsDll = 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
Add-Type -Path $adsDll
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
function ReadSym($name, $type) {
$h = $c.CreateVariableHandle($name)
$v = $c.ReadAny($h, $type)
$c.DeleteVariableHandle($h)
return $v
}
$prev = $null
for ($k = 1; $k -le 10; $k++) {
$nc = ReadSym 'GVL_Sync.nCycles' ([uint64])
$bs = ReadSym 'GVL_Sync.bSync' ([bool])
$nrs = ReadSym 'GVL_Sync.nReturnedSeq' ([uint64])
$nm = ReadSym 'GVL_Sync.nMismatch' ([int])
$lag = [int64]$nc - [int64]$nrs
$delta = if ($null -ne $prev) { $nm - $prev } else { 0 }
$prev = $nm
Write-Host ("{0,2} nCycles={1,-8} lag={2,4} bSync={3} nMismatch={4} delta={5}" -f $k, $nc, $lag, $bs, $nm, $delta)
if ($k -lt 10) { Start-Sleep -Seconds 60 }
}
$c.Dispose()

View File

@ -0,0 +1,61 @@
#Requires -RunAsAdministrator
#Requires -Version 5.1
$ErrorActionPreference = 'Continue'
function Invoke-Com {
param([scriptblock]$Fn, [int]$Retries = 30, [int]$DelayMs = 2000)
for ($i = 0; $i -lt $Retries; $i++) {
try { return & $Fn } catch {
$hr = $_.Exception.HResult
if ($hr -eq -2147418111) { Start-Sleep -Milliseconds $DelayMs; continue }
throw
}
}
throw "COM call rejected $Retries times (RPC_E_CALL_REJECTED)"
}
Write-Host "=== Deploy 8B with RPC retry ==="
$dte = Invoke-Com { New-Object -ComObject TcXaeShell.DTE.17.0 }
Invoke-Com { $dte.MainWindow.Visible = $true } | Out-Null
try { Invoke-Com { $dte.SuppressUI = $false } | Out-Null } catch {}
Invoke-Com { $dte.Solution.Open('C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary_4kHz\TwinCATProject1.sln') } | Out-Null
Write-Host "solution opened"
$sm = $null
for ($i = 0; $i -lt 40; $i++) {
try { $sm = Invoke-Com { $dte.Solution.Projects.Item(1).Object } -Retries 3 -DelayMs 3000; if ($sm) { break } } catch { Write-Host " waiting sysmgr..." }
}
if (-not $sm) { Write-Host "FATAL: no sysmgr"; exit 1 }
Write-Host "sysmgr OK"
Invoke-Com { $sm.SetTargetNetId('169.254.176.217.1.1') } | Out-Null
Write-Host "building..."
try { Invoke-Com { $dte.ExecuteCommand('Build.BuildSolution') } -Retries 10 | Out-Null } catch { Write-Host "build cmd: $_" }
# wait for build done (BuildState 3 = done)
$done = $false
for ($i = 0; $i -lt 90; $i++) {
try {
$bs = Invoke-Com { $dte.Solution.SolutionBuild.BuildState } -Retries 5 -DelayMs 3000
$bi = Invoke-Com { $dte.Solution.SolutionBuild.LastBuildInfo } -Retries 5 -DelayMs 1000
if ($bs -eq 3) { Write-Host "build done (LastBuildInfo=$bi) after $($i*2)s"; $done = $true; break }
} catch { Write-Host " build poll err: $_" }
Start-Sleep -Seconds 2
}
if (-not $done) { Write-Host "WARN: build state never reached done" }
# dump build errors if any
try {
$ew = $dte.ToolWindows.ErrorList
Write-Host "error list check skipped"
} catch {}
Write-Host "activating..."
Invoke-Com { $sm.ActivateConfiguration() } -Retries 30 -DelayMs 3000 | Out-Null
Write-Host "activated"
Start-Sleep 10
Write-Host "restarting runtime..."
Invoke-Com { $sm.StartRestartTwinCAT() } -Retries 30 -DelayMs 3000 | Out-Null
Write-Host "restart issued"
Start-Sleep 12
try { Write-Host "IsTwinCATStarted=$($sm.IsTwinCATStarted)" } catch {}
Write-Host "=== done ==="

View File

@ -0,0 +1,61 @@
#Requires -RunAsAdministrator
#Requires -Version 5.1
$ErrorActionPreference = 'Continue'
function Invoke-Com {
param([scriptblock]$Fn, [int]$Retries = 30, [int]$DelayMs = 2000)
for ($i = 0; $i -lt $Retries; $i++) {
try { return & $Fn } catch {
$hr = $_.Exception.HResult
if ($hr -eq -2147418111) { Start-Sleep -Milliseconds $DelayMs; continue }
throw
}
}
throw "COM call rejected $Retries times (RPC_E_CALL_REJECTED)"
}
Write-Host "=== Deploy 8B with RPC retry ==="
$dte = Invoke-Com { New-Object -ComObject TcXaeShell.DTE.17.0 }
Invoke-Com { $dte.MainWindow.Visible = $true } | Out-Null
try { Invoke-Com { $dte.SuppressUI = $false } | Out-Null } catch {}
Invoke-Com { $dte.Solution.Open('C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary_800B\TwinCATProject1.sln') } | Out-Null
Write-Host "solution opened"
$sm = $null
for ($i = 0; $i -lt 40; $i++) {
try { $sm = Invoke-Com { $dte.Solution.Projects.Item(1).Object } -Retries 3 -DelayMs 3000; if ($sm) { break } } catch { Write-Host " waiting sysmgr..." }
}
if (-not $sm) { Write-Host "FATAL: no sysmgr"; exit 1 }
Write-Host "sysmgr OK"
Invoke-Com { $sm.SetTargetNetId('169.254.176.217.1.1') } | Out-Null
Write-Host "building..."
try { Invoke-Com { $dte.ExecuteCommand('Build.BuildSolution') } -Retries 10 | Out-Null } catch { Write-Host "build cmd: $_" }
# wait for build done (BuildState 3 = done)
$done = $false
for ($i = 0; $i -lt 90; $i++) {
try {
$bs = Invoke-Com { $dte.Solution.SolutionBuild.BuildState } -Retries 5 -DelayMs 3000
$bi = Invoke-Com { $dte.Solution.SolutionBuild.LastBuildInfo } -Retries 5 -DelayMs 1000
if ($bs -eq 3) { Write-Host "build done (LastBuildInfo=$bi) after $($i*2)s"; $done = $true; break }
} catch { Write-Host " build poll err: $_" }
Start-Sleep -Seconds 2
}
if (-not $done) { Write-Host "WARN: build state never reached done" }
# dump build errors if any
try {
$ew = $dte.ToolWindows.ErrorList
Write-Host "error list check skipped"
} catch {}
Write-Host "activating..."
Invoke-Com { $sm.ActivateConfiguration() } -Retries 30 -DelayMs 3000 | Out-Null
Write-Host "activated"
Start-Sleep 10
Write-Host "restarting runtime..."
Invoke-Com { $sm.StartRestartTwinCAT() } -Retries 30 -DelayMs 3000 | Out-Null
Write-Host "restart issued"
Start-Sleep 12
try { Write-Host "IsTwinCATStarted=$($sm.IsTwinCATStarted)" } catch {}
Write-Host "=== done ==="

30
scripts/tc-monitor-5h.ps1 Normal file
View File

@ -0,0 +1,30 @@
#Requires -RunAsAdministrator
# 5-hour stability monitor: logs PLC sync metrics to CSV every 60s.
$ErrorActionPreference = 'Continue'
Add-Type -Path 'C:\Program Files (x86)\Beckhoff\TwinCAT\3.1\Components\Base\v170\TwinCAT.Ads.dll'
$logFile = 'C:\Users\tonycao\work\ethercat-linux\monitor_5h.csv'
'timestamp,nCycles,bSync,nReturnedSeq,nMismatch,nProcMismatch' | Out-File $logFile -Encoding utf8
$start = Get-Date
for ($i = 0; $i -lt 300; $i++) {
try {
$c = New-Object TwinCAT.Ads.TcAdsClient
$c.Connect('169.254.176.217.1.1', 851)
$h1 = $c.CreateVariableHandle('GVL_Sync.nCycles')
$h2 = $c.CreateVariableHandle('GVL_Sync.bSync')
$h3 = $c.CreateVariableHandle('GVL_Sync.nReturnedSeq')
$h4 = $c.CreateVariableHandle('GVL_Sync.nMismatch')
$h5 = $c.CreateVariableHandle('GVL_Sync.nProcMismatch')
$nc = $c.ReadAny($h1, [uint64])
$bs = $c.ReadAny($h2, [bool])
$nrs = $c.ReadAny($h3, [uint64])
$nm = $c.ReadAny($h4, [int])
$npm = $c.ReadAny($h5, [uint32])
$c.Dispose()
$ts = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
"$ts,$nc,$bs,$nrs,$nm,$npm" | Out-File $logFile -Append -Encoding utf8
} catch {
$ts = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
"$ts,ERROR,,,," | Out-File $logFile -Append -Encoding utf8
}
Start-Sleep -Seconds 60
}

View File

@ -0,0 +1,18 @@
#Requires -RunAsAdministrator
$ErrorActionPreference = 'Continue'
# Open project in visible XAE and leave it running for interactive inspection.
$dte = New-Object -ComObject TcXaeShell.DTE.17.0
$dte.MainWindow.Visible = $true
try { $dte.SuppressUI = $false } catch {}
$dte.Solution.Open('C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary_800B\TwinCATProject1.sln')
Start-Sleep 25
$sm = $null
for ($i = 0; $i -lt 20; $i++) {
try { $sm = $dte.Solution.Projects.Item(1).Object; if ($sm) { break } } catch {}
Start-Sleep 3
}
if (-not $sm) { Write-Host 'no sysmgr'; exit 1 }
try { $sm.SetTargetNetId('169.254.176.217.1.1'); Write-Host 'target set' } catch { Write-Host "target: $_" }
Start-Sleep 3
try { Write-Host "IsTwinCATStarted=$($sm.IsTwinCATStarted)" } catch {}
Write-Host 'XAE left open. Inspect: TIID -> 设备 1 (EtherCAT) -> 模块 3 (EK1100) -> 模块 10 (EL6695) -> IO Inputs / IO Outputs -> Online tab'

View File

@ -0,0 +1,62 @@
#Requires -RunAsAdministrator
$ErrorActionPreference = "Continue"
$dte = New-Object -ComObject TcXaeShell.DTE.17.0
$dte.MainWindow.Visible = $true
try { $dte.SuppressUI = $false } catch {}
$dte.Solution.Open("C:\Users\tonycao\work\twincat3-auto-cli\TwinCAT_EL6695_Primary_800B\TwinCATProject1.sln")
Start-Sleep 20
$sm = $null
for ($i = 0; $i -lt 20; $i++) {
try { $sm = $dte.Solution.Projects.Item(1).Object; if ($sm) { break } } catch {}
Start-Sleep 3
}
if (-not $sm) { Write-Host "no sysmgr"; exit 1 }
try { $sm.SetTargetNetId("169.254.176.217.1.1") } catch {}
Start-Sleep 2
$box = "TIID^设备 1 (EtherCAT)^模块 3 (EK1100)^模块 10 (EL6695)"
$cands = @(
"$box^IO Outputs^IO Outputs",
"$box^IO Outputs^IO Outputs[0]",
"$box^IO Inputs^IO Inputs",
"$box^IO Inputs^IO Inputs[0]"
)
foreach ($c in $cands) {
$it = $null
try { $it = $sm.TryLookupTreeItem($c) } catch { Write-Host "err $c : $_" }
if ($it) {
$sz = ""
try { $sz = $it.BitSize } catch {}
Write-Host "OK: $c name=$($it.Name) type=$($it.ItemType) bitsize=$sz"
} else {
Write-Host "MISS: $c"
}
}
$varAin = "TIPC^PlcSync^PlcSync Instance^PlcTask Inputs^GVL_Sync.aIn1"
$varAout = "TIPC^PlcSync^PlcSync Instance^PlcTask Outputs^GVL_Sync.aOut1"
foreach ($v in @($varAin, $varAout)) {
$it = $null
try { $it = $sm.TryLookupTreeItem($v) } catch {}
if ($it) { Write-Host "PLC OK: $v" } else { Write-Host "PLC MISS: $v" }
}
Write-Host "--- unlink existing ---"
try { $sm.UnlinkVariables($varAin, "$box^IO Inputs^IO Inputs") ; Write-Host "unlinked aIn1" } catch { Write-Host "unlink aIn1: $_" }
try { $sm.UnlinkVariables($varAout, "$box^IO Outputs^IO Outputs"); Write-Host "unlinked aOut1" } catch { Write-Host "unlink aOut1: $_" }
Write-Host "--- link whole arrays ---"
try { $sm.LinkVariables($varAin, "$box^IO Inputs^IO Inputs"); Write-Host "linked aIn1" } catch { Write-Host "link aIn1: $_" }
try { $sm.LinkVariables($varAout, "$box^IO Outputs^IO Outputs"); Write-Host "linked aOut1" } catch { Write-Host "link aOut1: $_" }
$xml = $sm.ProduceMappingInfo()
[System.IO.File]::WriteAllText("C:\Users\tonycao\work\ethercat-linux\tmp_mapping2.xml", $xml, [System.Text.Encoding]::UTF8)
Write-Host "mapping saved"
Write-Host "--- save + activate + restart ---"
try { $dte.Solution.SaveAll(); Write-Host "saved" } catch { Write-Host "save: $_" }
try { $sm.ActivateConfiguration(); Write-Host "activated" } catch { Write-Host "activate: $_" }
Start-Sleep 10
try { $sm.StartRestartTwinCAT(); Write-Host "restart issued" } catch { Write-Host "restart: $_" }
Start-Sleep 10
Write-Host "done"

View File

@ -38,18 +38,35 @@ pub fn twin_layout_blob(obj: u16) -> Vec<u8> {
/// Size-parameterised TwinCAT line-format blob.
///
/// For payloads of 1024 bytes this is byte-identical to `twin_layout_blob`.
/// For small payloads (<= 30 bytes, fitting one 240-bit entry) a single
/// entry references the real object at the exact bit length.
/// For payloads up to 240 bits a single entry references the real object at
/// the exact bit length. Larger payloads use the TwinCAT line format:
/// 1 real entry @240bit + continuation entries @240bit + 1 continuation
/// entry at the remainder bit length.
pub fn twin_layout_blob_sized(obj: u16, payload_bytes: usize) -> Vec<u8> {
if payload_bytes == 1024 {
return twin_layout_blob(obj);
}
let bits = (payload_bytes * 8).min(240) as u8;
let total_bits = payload_bytes * 8;
let idx_lo = (obj & 0xFF) as u8;
let idx_hi = (obj >> 8) as u8;
let mut blob = Vec::with_capacity(6);
blob.extend_from_slice(&1u16.to_le_bytes());
blob.extend_from_slice(&[bits, 1, idx_lo, idx_hi]);
if total_bits <= 240 {
let mut blob = Vec::with_capacity(6);
blob.extend_from_slice(&1u16.to_le_bytes());
blob.extend_from_slice(&[total_bits as u8, 1, idx_lo, idx_hi]);
return blob;
}
let full = total_bits / 240;
let rem = (total_bits % 240) as u8;
let n = if rem > 0 { full + 1 } else { full };
let mut blob = Vec::with_capacity(2 + n * 4);
blob.extend_from_slice(&(n as u16).to_le_bytes());
blob.extend_from_slice(&[240, 1, idx_lo, idx_hi]);
for _ in 1..full {
blob.extend_from_slice(&[240, 0, 0, 0]);
}
if rem > 0 {
blob.extend_from_slice(&[rem, 0, 0, 0]);
}
blob
}

View File

@ -19,7 +19,7 @@ use std::time::{Duration, Instant};
use el6695_rt::{bridge, regs, rt, stats};
use ethercrab::{
std::{ethercat_now, tx_rx_task},
std::{ethercat_now, tx_rx_task, tx_rx_task_io_uring},
MainDevice, MainDeviceConfig, PduStorage, Timeouts,
};
use stats::OnlineStats;
@ -31,15 +31,15 @@ const PDI_LEN: usize = 3072;
// Secondary side SM layout (ESI-defined process data SMs)
const SM_TX_ADDR: u16 = 0x1C00; // SM2: outputs (J1900 writes data to TwinCAT)
const SM_TX_LEN: usize = 8;
const SM_TX_LEN: usize = 800;
const SM_RX_ADDR: u16 = 0x8E00; // SM3: inputs (J1900 reads data from TwinCAT)
// Full PDO: 2B diagnostic word + 8B payload (single-entry twin_layout blob
// expands 0x6000:01 to 8 bytes).
const SM_RX_LEN: usize = 8; // SM3 buffer (no diag word, pure 8B payload)
const SM_RX_PLD_LEN: usize = 8; // actual payload after stripping diagnostic word
const SM_RX_LEN: usize = 800; // SM3 buffer (no diag word, pure 8B payload)
const SM_RX_PLD_LEN: usize = 800; // actual payload after stripping diagnostic word
const RX_PREFIX: usize = 0; // no diag word prefix (0x1C13=[0x1A08] only)
const PAYLOAD_LEN: usize = 8;
const PAYLOAD_LEN: usize = 800;
/// Abort after this many consecutive cycles with a TX or RX error.
const MAX_ERR_STREAK: u64 = 1000;
@ -98,6 +98,11 @@ struct Config {
/// payload changes (new TwinCAT frame), then process immediately.
/// Phase-locks J1900 processing to the primary master's frame arrival.
spin: bool,
/// Enable DC SYNC0 on the EL6695 secondary (cycle = --cycle-us) and print
/// per-second DC phase/drift telemetry in the report line.
dc: bool,
/// Use the io_uring-based blocking TX/RX loop instead of AF_PACKET poll.
iouring: bool,
}
fn parse_args() -> Result<Config, String> {
@ -116,6 +121,8 @@ fn parse_args() -> Result<Config, String> {
el2262: false,
el1252_every: 1,
spin: false,
dc: false,
iouring: false,
};
let mut args = std::env::args().skip(1).peekable();
if let Some(a) = args.peek() {
@ -146,6 +153,8 @@ fn parse_args() -> Result<Config, String> {
"--prio" => cfg.prio = val("--prio")?.parse().map_err(|_| "bad --prio")?,
"--quiet" => cfg.quiet = true,
"--spin" => cfg.spin = true,
"--dc" => cfg.dc = true,
"--iouring" => cfg.iouring = true,
"--el2202" => cfg.el2202 = true,
"--el2202-dual" => {
cfg.el2202 = true;
@ -231,10 +240,21 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
);
let iface = cfg.iface.clone();
let use_iouring = cfg.iouring;
let _txrx = std::thread::spawn(move || {
let task = tx_rx_task(&iface, tx, rx).expect("create tx_rx task");
if let Err(e) = futures_lite::future::block_on(task) {
eprintln!("tx_rx task ended with error: {:?}", e);
if let Err(e) = rt::promote_current_thread(1, 79, false) {
eprintln!("tx_rx RT promotion failed (continuing): {}", e);
}
if use_iouring {
match tx_rx_task_io_uring(&iface, tx, rx) {
Ok(_) => {}
Err(e) => eprintln!("io_uring tx_rx task ended with error: {:?}", e),
}
} else {
let task = tx_rx_task(&iface, tx, rx).expect("create tx_rx task");
if let Err(e) = futures_lite::future::block_on(task) {
eprintln!("tx_rx task ended with error: {:?}", e);
}
}
});
@ -525,6 +545,148 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
}
println!(" >>> OP achieved! <<<");
// ---- Optional DC SYNC0 on the EL6695 secondary ----
// ethercrab init already distributed the system time (offset/propagation
// delay/static drift). Here we only start the SYNC0 unit on --cycle-us
// grid and verify the clock actually advances.
let mut dc_ref: Option<(u64, u64)> = None; // (dc_time, mono_time) at setup
if cfg.dc {
let t1 = ethercrab::Command::fprd(station, regs::DC_SYSTEM_TIME)
.receive::<u64>(&maindevice)
.await
.unwrap_or(0);
std::thread::sleep(Duration::from_millis(100));
let t2 = ethercrab::Command::fprd(station, regs::DC_SYSTEM_TIME)
.receive::<u64>(&maindevice)
.await
.unwrap_or(0);
let adv = t2.wrapping_sub(t1);
println!("DC system time: {} -> {} (adv {} ns in 100ms)", t1, t2, adv);
if adv < 50_000_000 || adv > 500_000_000 {
println!("DC: clock not advancing sanely; SYNC0 setup skipped");
} else {
// Diagnostics: which DC register groups actually exist?
for (reg, name) in [
(0x0900u16, "rx_time_port0(u32)"),
(0x0920u16, "sys_time_offset(u64)"),
(0x0928u16, "prop_delay(u32)"),
(0x0930u16, "drift... (u16)"),
(0x0980u16, "sync_activation(u8)"),
(0x0981u16, "sync_active_byte(u8)"),
(0x09A0u16, "sync0_cycle(u32)"),
(0x09AEu16, "sync0_status(u8)"),
] {
match ethercrab::Command::fprd(station, reg)
.receive_slice(&maindevice, 8)
.await
{
Ok(pdu) => {
let raw: &[u8] = &pdu;
println!(" DC probe 0x{:04X} {}: {:02X?}", reg, name, &raw[..8.min(raw.len())]);
}
Err(e) => println!(" DC probe 0x{:04X} {}: ERR {:?}", reg, name, e),
}
}
// CoE view: 0x1C32 (SM2 outputs) / 0x1C33 (SM3 inputs) sync params
for idx in [0x1C32u16, 0x1C33] {
let n = sd.sdo_read::<u8>(idx, 0).await.unwrap_or(0xFF);
println!(" CoE 0x{:04X}: {} subentries", idx, n);
for (sub, name) in [(0x01u8, "sync_mode"), (0x04, "modes_supported"), (0x0B, "sm_event_missed"), (0x0C, "cycle_exceeded")] {
match sd.sdo_read::<u16>(idx, sub).await {
Ok(x) => println!(" CoE 0x{:04X}:{:02X} {} = 0x{:04X}", idx, sub, name, x),
Err(e) => println!(" CoE 0x{:04X}:{:02X} {} ERR {:?}", idx, sub, name, e),
}
}
match sd.sdo_read::<u32>(idx, 0x02).await {
Ok(x) => println!(" CoE 0x{:04X}:02 cycle_time_ns = {}", idx, x),
Err(e) => println!(" CoE 0x{:04X}:02 cycle_time_ns ERR {:?}", idx, e),
}
match sd.sdo_read::<u8>(idx, 0x20).await {
Ok(x) => println!(" CoE 0x{:04X}:20 sync_error = {}", idx, x),
Err(e) => println!(" CoE 0x{:04X}:20 sync_error ERR {:?}", idx, e),
}
}
let start = t2.wrapping_add(100_000_000);
let w1 = ethercrab::Command::fpwr(station, regs::DC_SYNC_START_TIME)
.send(&maindevice, start)
.await;
println!(" DC write start_time(0x0990): {:?}", w1.as_ref().map(|_| "OK").map_err(|e| format!("{:?}", e)));
let w2 = ethercrab::Command::fpwr(station, regs::DC_SYNC0_CYCLE)
.send(&maindevice, cycle_ns as u32)
.await;
println!(" DC write sync0_cycle(0x09A0): {:?}", w2.as_ref().map(|_| "OK").map_err(|e| format!("{:?}", e)));
let w3 = regs::rmw_u8(
&maindevice,
station,
regs::DC_SYNC_ACTIVATION,
regs::DC_SYNC0_ACTIVATE,
0,
)
.await;
println!(" DC rmw activation(0x0980): {:?}", w3.as_ref().map(|v| format!("0x{:02X}", v)).map_err(|e| format!("{:?}", e)));
// rmw needs a read (fails here); try a blind full-byte write instead
let w4 = ethercrab::Command::fpwr(station, regs::DC_SYNC_ACTIVATION)
.send(&maindevice, regs::DC_SYNC0_ACTIVATE)
.await;
println!(" DC blind write activation(0x0980)=0x01: {:?}", w4.as_ref().map(|_| "OK").map_err(|e| format!("{:?}", e)));
std::thread::sleep(Duration::from_millis(150));
// Try selecting DC-Synchron SYNC0 via firmware object (mode 2).
// May be rejected in OP; worth observing the abort code either way.
let mut coe_dc_ok = true;
for idx in [0x1C32u16, 0x1C33] {
match sd.sdo_write::<u16>(idx, 0x01, 2).await {
Ok(_) => println!(" CoE write 0x{:04X}:01 sync_mode=2 OK", idx),
Err(e) => {
coe_dc_ok = false;
println!(" CoE write 0x{:04X}:01 sync_mode=2 ERR {:?}", idx, e);
}
}
match sd.sdo_read::<u16>(idx, 0x01).await {
Ok(x) => println!(" CoE-after 0x{:04X}:01 sync_mode = {}", idx, x),
Err(e) => {
coe_dc_ok = false;
println!(" CoE-after 0x{:04X}:01 sync_mode ERR {:?}", idx, e);
}
}
}
if coe_dc_ok {
let t_dc = ethercrab::Command::fprd(station, regs::DC_SYSTEM_TIME)
.receive::<u64>(&maindevice)
.await
.unwrap_or(0);
dc_ref = Some((t_dc, rt::now_ns()));
println!("DC-Synchron: firmware sync_mode=2 accepted, DC telemetry on");
}
let act = ethercrab::Command::fprd(station, regs::DC_SYNC_ACTIVATION)
.receive::<u8>(&maindevice)
.await;
let cyc0 = ethercrab::Command::fprd(station, regs::DC_SYNC0_CYCLE)
.receive::<u32>(&maindevice)
.await;
let t3 = ethercrab::Command::fprd(station, regs::DC_SYSTEM_TIME)
.receive::<u64>(&maindevice)
.await
.unwrap_or(0);
let (al, alc) = bridge::drive_state(&maindevice, station, 0x0008).await;
println!(" AL after DC writes: {}", bridge::verdict(al, alc));
println!(
"DC SYNC0: act={:?} cycle={:?} start=+100ms (t3={} start_delta={}ns)",
act.as_ref().map(|v| format!("0x{:02X}", v)).map_err(|e| format!("{:?}", e)),
cyc0.as_ref().map_err(|e| format!("{:?}", e)),
t3,
t3.wrapping_sub(start) as i64
);
if matches!(act, Ok(a) if a & regs::DC_SYNC0_ACTIVATE != 0)
&& matches!(cyc0, Ok(c) if c == cycle_ns as u32)
{
dc_ref = Some((t3, rt::now_ns()));
println!("DC SYNC0: active on EL6695 secondary");
} else {
println!("DC SYNC0: readback mismatch; running without DC telemetry");
}
}
}
// ---- Optional EL2202 oscilloscope waveform (toggle one DO per cycle) ----
const EL2202_OUT_ADDR: u16 = 0x0F00;
let mut el2202: Option<(u16, u16)> = None;
@ -755,6 +917,17 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
let mut prev_rx8 = [0u8; 8]; // spin-lock: previous payload (change detect)
let mut last_fresh: Option<Instant> = None; // spin-lock: last fresh-data instant
// Async pipelined FPWR: two alternating leaked tx slots so the in-flight
// write future can hold a 'static buffer while the response is reaped
// at the start of the next cycle.
let tx_slot_a = Box::leak(Box::new([0u8; PAYLOAD_LEN]));
let tx_slot_b = Box::leak(Box::new([0u8; PAYLOAD_LEN]));
let mut tx_slot_toggle = false;
let mut pending_tx: Option<
std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), ethercrab::error::Error>> + '_>>,
> = None;
let mut tx_late: u64 = 0;
let mut aborted: Option<String> = None;
loop {
@ -823,6 +996,21 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
let t0 = Instant::now();
cyc += 1;
// Reap async FPWR kicked last cycle: the spin-detect above gave the
// response ~one full cycle to arrive, so this should never block.
if let Some(mut f) = pending_tx.take() {
match futures_lite::future::poll_once(f.as_mut()).await {
Some(Ok(_)) => {}
Some(Err(_)) => {
tx_err += 1;
}
None => {
tx_late += 1;
let _ = f.await;
}
}
}
if !spin_mode {
// 1. Read SM3 (TwinCAT data via bridge)
let t_rx0 = Instant::now();
@ -913,14 +1101,47 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
tx_buf[264..268].copy_from_slice(&(jitter_ns as i32).to_le_bytes());
}
let t_tx0 = Instant::now();
let tx_ok = ethercrab::Command::fpwr(station, SM_TX_ADDR)
.send(&maindevice, &tx_buf[..PAYLOAD_LEN])
.await
.is_ok();
tx_rtt_st.push(t_tx0.elapsed().as_nanos() as i64);
if !tx_ok {
tx_err += 1;
if spin_mode {
// Async pipelined FPWR: kick the frame onto the wire now
// (poll_once drives it to sendable), reap response next
// cycle. Frees ~240us of blocking write time per cycle.
let p = if tx_slot_toggle {
tx_slot_b.as_mut_ptr()
} else {
tx_slot_a.as_mut_ptr()
};
tx_slot_toggle = !tx_slot_toggle;
let slot: &'static mut [u8] =
unsafe { std::slice::from_raw_parts_mut(p, PAYLOAD_LEN) };
slot.copy_from_slice(&tx_buf[..PAYLOAD_LEN]);
let mut fut = Box::pin(
ethercrab::Command::fpwr(station, SM_TX_ADDR).send(&maindevice, &slot[..]),
);
// Kick: drive the write future for up to ~20us so the frame
// is marked sendable and the TX thread puts it on the wire
// NOW. poll_once alone does not reach sendable state.
let t_kick = Instant::now();
let _ = futures_lite::future::or(
async { fut.as_mut().await.is_ok() },
async {
while t_kick.elapsed().as_micros() < 20 {
futures_lite::future::yield_now().await;
}
false
},
)
.await;
pending_tx = Some(fut);
} else {
let t_tx0 = Instant::now();
let tx_ok = ethercrab::Command::fpwr(station, SM_TX_ADDR)
.send(&maindevice, &tx_buf[..PAYLOAD_LEN])
.await
.is_ok();
tx_rtt_st.push(t_tx0.elapsed().as_nanos() as i64);
if !tx_ok {
tx_err += 1;
}
}
}
@ -1050,20 +1271,37 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
}
let proc_val = tc_seq.wrapping_mul(7).wrapping_add(3);
let stale_pct = if cyc > 0 { (rx_stale * 100 / cyc) } else { 0 };
let mut dc_info = String::new();
if let Some((dc0, mono0)) = dc_ref {
if let Ok(dc_now) = ethercrab::Command::fprd(station, regs::DC_SYSTEM_TIME)
.receive::<u64>(&maindevice)
.await
{
let mono_now = rt::now_ns();
let drift = (dc_now.wrapping_sub(dc0)) as i64 - (mono_now - mono0) as i64;
dc_info = format!(
" dcph={} dcdr={}ns",
dc_now % cycle_ns,
drift
);
}
}
println!(
"S tms={} cyc={} raw={} proc={} txe={} rxe={} stale={}% mono={} cerr={} mism={} e0={} e1={}",
"S tms={} cyc={} raw={} proc={} txe={} rxe={} late={} stale={}% mono={} cerr={} mism={} e0={} e1={}{}",
start.elapsed().as_millis(),
cyc,
tc_seq,
proc_val,
tx_err,
rx_err,
tx_late,
stale_pct,
rx_nonmono,
comp_err,
payload_mismatch,
pos_edges,
pos1_edges,
dc_info,
);
last_report = Instant::now();
}
@ -1073,6 +1311,26 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
}
}
// Reap any in-flight async FPWR before reporting
if let Some(f) = pending_tx.take() {
let _ = f.await;
}
// ---- DC post-run: re-read firmware sync counters accumulated during the run ----
if cfg.dc {
println!("\n=== DC post-run sync counters (0x1C32 SM2 / 0x1C33 SM3) ===");
for idx in [0x1C32u16, 0x1C33] {
let mode = sd.sdo_read::<u16>(idx, 0x01).await.unwrap_or(0xFFFF);
let missed = sd.sdo_read::<u16>(idx, 0x0B).await.unwrap_or(0xFFFF);
let exceeded = sd.sdo_read::<u16>(idx, 0x0C).await.unwrap_or(0xFFFF);
let serr = sd.sdo_read::<u8>(idx, 0x20).await.unwrap_or(0xFF);
println!(
" 0x{:04X}: sync_mode={} sm_event_missed={} cycle_exceeded={} sync_error={}",
idx, mode, missed, exceeded, serr
);
}
}
// ---- Final report ----
println!("\n=== timing stats ===");
jitter_st.report(1000, "us");

18
uia_dump.py Normal file
View File

@ -0,0 +1,18 @@
import sys, time
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
from pywinauto import Application
app = Application(backend='uia').connect(process=11748, timeout=10)
wins = app.windows()
print('windows:', len(wins))
for w in wins:
try:
t = w.window_text()
if not t:
continue
print('---', t)
tabs = [x.window_text() for x in w.descendants(control_type='TabItem')]
if tabs:
print(' tabs:', tabs)
except Exception:
pass

15
uia_find_dc.py Normal file
View File

@ -0,0 +1,15 @@
import sys, time
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
from pywinauto import Application
app = Application(backend='uia').connect(process=int(sys.argv[1]), timeout=10)
win = app.top_window()
# look for DC-related text and combo boxes anywhere
texts = win.descendants(control_type='Text')
interesting = [t.window_text() for t in texts if t.window_text() and any(k in t.window_text() for k in ['FreeRun', 'DC', 'Synchron', 'EtherCAT', 'Sync'])]
print('interesting texts:', interesting[:40])
combos = win.descendants(control_type='ComboBox')
print('combos:', [c.window_text() for c in combos])
listitems = win.descendants(control_type='ListItem')
li = [l.window_text() for l in listitems if l.window_text()]
print('listitems:', li[:30])

44
uia_open_el6695.py Normal file
View File

@ -0,0 +1,44 @@
import sys, time
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
from pywinauto import Application
from pywinauto.mouse import click, double_click
app = Application(backend='uia').connect(process=int(sys.argv[1]), timeout=10)
win = app.top_window()
tree = win.descendants(control_type='Tree')[0]
def expand_children(item):
try:
item.expand(); time.sleep(0.4)
except Exception:
pass
return list(item.children())
nodes = []
for root in tree.children():
nodes.extend(expand_children(root))
for name in ['TwinCATProject1', 'I/O', '设备', '设备 1 (EtherCAT)', '模块 3 (EK1100)']:
nxt = None
for n in nodes:
try:
if n.window_text() == name:
nxt = n
break
except Exception:
pass
if nxt is None:
print('missing:', name); sys.exit(1)
nodes = expand_children(nxt)
target = None
for n in nodes:
if n.window_text() == '模块 10 (EL6695)':
target = n
break
if not target:
print('EL6695 not found'); sys.exit(1)
r = target.rectangle()
double_click(coords=(int((r.left + r.right) / 2), int((r.top + r.bottom) / 2)))
time.sleep(4)
win.set_focus()
tabs = [t.window_text() for t in win.descendants(control_type='TabItem')]
print('tabs:', tabs)