enet-validator: e:net protocol reverse + host-side emulation (AECH claim / encrypted DMX / RDM discovery, hostdemo)
This commit is contained in:
commit
e248a979e8
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
*.log
|
||||
*.pcap
|
||||
*.tsv
|
||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "enet-validator"
|
||||
version = "0.1.0"
|
||||
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "enet-validator"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# 闆跺閮ㄤ緷璧栵細std::net::UdpSocket + std::fs 瓒冲
|
||||
|
||||
[profile.release]
|
||||
opt-level = 2
|
||||
1
samples/ack_84.hex
Normal file
1
samples/ack_84.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001002a00000002004275746c010018030000010000000000
|
||||
1
samples/aere_594.hex
Normal file
1
samples/aere_594.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000600017ba8c000161cf12e3900717ba84145524559bb367fffffff0107346701250015000a00000001017ba8c000ffffff00161cf12e39000000000000000000001a0000000101fa0c000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023010000000024000000240000001c001c001d001d8361bf1d00f00af00a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/aerep_113.hex
Normal file
1
samples/aerep_113.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000600017ba8c000161cf12e3900717ba84145524559bb367ffffffd0107348809000015000a00000001017ba8c000ffffff00161cf12e39000000000000000000001a0000000101fa0c000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023010000000023000000230000001c001c001d001d5904e1c100f00af00a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/aerep_115.hex
Normal file
1
samples/aerep_115.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000600017ba8c000161cf12e3900717ba84145524559bb367ffffffd0107348809000015000a00000001017ba8c000ffffff00161cf12e39000000000000000000001a0000000101fa0c000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023010000000023000000230000001c001c001d001d51cf405000f00af00a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/aerep_117.hex
Normal file
1
samples/aerep_117.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000600017ba8c000161cf12e3900717ba84145524559bb367ffffffd0107348809000015000a00000001017ba8c000ffffff00161cf12e39000000000000000000001a0000000101fa0c000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023010000000023000000230000001c001c001d001dfce6923d00f00af00a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/announce_144.hex
Normal file
1
samples/announce_144.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100660000000100717ba8c0300ed559bb361a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/announce_686.hex
Normal file
1
samples/announce_686.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740100000000001c7c000000000000010084020000150000000000000000000000000000000000000000000000000000000000000000001a000000000001717ba8c000ffffff300ed559bb3600000000000000000000000000000001ce43050500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043a0f4f7ec175641812b909bdc99f2ff4000000000010003000000000000000000000000000100030000000000000000000000000074006f006e007900630061006f004400450053004b0054004f0050002d0050004500440032004c00560044002d00660037006600340061003000340033002d0031003700650063002d0034003100350036002d0038003100320062002d0039003000390062006400630039003900660032006600660000000000000000000000911f343cf708bf342e8e519da944bd8a5a4a24433989ea070902001819410343003a005c00550073006500720073005c0074006f006e007900630061006f005c0041007000700044006100740061005c004c006f00630061006c005c0065006300750065005c00530059004d00500048004f004c004900470048005400200035002e0035005c00430075007200720065006e007400530068006f0077000000000000000000000000000000000000000000d00df1e81900000081710b0e000000008ee4f7f6190000001e00000000000000010000005a00ffffea07090717182f16010200000000000000000000000002000000000000000000000000201c0000000000000000000001000000000000000000000001000000000000000100000001000000
|
||||
1
samples/announce_live.hex
Normal file
1
samples/announce_live.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740100000000001c7c000000000000010084020000150000000000000000000000000000000000000000000000000000000000000000001a000000000001717ba8c000ffffff300ed559bb3600000000000000000000000000000001ce43050500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043a0f4f7ec175641812b909bdc99f2ff4000000000010003000000000000000000000000000100030000000000000000000000000074006f006e007900630061006f004400450053004b0054004f0050002d0050004500440032004c00560044002d00660037006600340061003000340033002d0031003700650063002d0034003100350036002d0038003100320062002d0039003000390062006400630039003900660032006600660000000000000000000000911f0000000000000000000000000000000000000000d00701010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e1bf21b20c0000001e00000000000000010000005a00ffffea07090a163432e003020000000000000000000000000200000000000000000000000098080000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/claim_h112.hex
Normal file
1
samples/claim_h112.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000500717ba8c0300ed559bb36000000004145434841cad262b4c0d9aea3e3c1cc0898bffec74c63aca7b51e5727a7ae330ca7d61e162e57e9fa86044d898e5f16a8249dd5d6ab360eedef3a8cab7a3a0d12a6a1fb0da2b5d940d50340481523195508af0309d95c6071c73027f4ca3e80e2545f3b9bb5327e4cf63be3ec6408056ec49400a7f6c6d7cc2136a3cb37043849418945728ee5769783f351da3f2cf1ffa99445455bb87a01585bd3949ab415f20299abdac393e0de1e69240274fd8cf445a422a2bef3ed880e44193212e1fccede5fd8a681163c16b46626f294788d5bcfdf4ef69272330eac22cb7e79adcc3c6c55231525e5722e670bed58ccfbf496b7be633393063c6c2bc9fa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/claim_h114.hex
Normal file
1
samples/claim_h114.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000500717ba8c0300ed559bb3600000000414543484bbd9d079aea853e547835e96c208e8685db41b02e35c7ba4998a96619b7e95beb13e3aa6ee5bde275b5413f897ac67277b5e64f87493016f5e2bd01411e259c8ed9fd593de4189f1d5f3a78f38830cfd236ee63d099d4a2e2cdb48b5f2e2a1f9bb5327e27bca0578fa4b0af7c6d35939c4731280eb8226db1ee2f0a30c2ccf4055a77702f8d7761bee3e94ed7815265015ac68967ac9429aa47d9398d9626cdd8bca921f5c4640fd0d0692aced495309bca09d5fb438c59c64dc6e8b33dd428bc1c751bd7306f3cf6b6b89742695bd6b1a99ec38d51d1952f505ba7fe7b5c92fb416eb25e7a586a5accde458af5165df2f0370a9f119b6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/claim_h116.hex
Normal file
1
samples/claim_h116.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100280200000500717ba8c0300ed559bb360000000041454348e0eb87d5c73e7a5a269b01faa87737503368c0728c0b3a39fdb50e4452f3eaa602687e8ed195928e64ea645915624a7800ba6ca8daf9bdd5b14dd8122f360833b15af840f9a87e9dd6320ea1a6e1239a6b9644978641cb09aaa8dc834a22993e9bb5327ebd805e2de231a8bb6a0a7e573bced2a8ea1c2ed02a3da799f170ac6f8fee9550cbfae056d3e0bf7539682f6117ec5cb76f231f8e7a0c597fd8c53b7284382c7c742e8b40ee7cd50a5db897c10d45820413f4006bcf07d6bfa0607d0da44cf0003cb3ede8611cc9f34d1430c17598719342365e9037c0ad6c57cc9f8dfec76510c796855a5899a3f72a0d1ebad6144a08eeeaca6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
1
samples/discovery_req_222.hex
Normal file
1
samples/discovery_req_222.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740100000000001c7c0000000000000100b40000000100746f6e7963616f4445534b544f502d504544324c56442d66376634613034332d1a000000000002717ba8c000ffffff300ed559bb3600000000000000000000000000000001ce430505000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000900
|
||||
1
samples/discovery_resp_259.hex
Normal file
1
samples/discovery_resp_259.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740100000000001c7c0000000000000100d90000000f004275746c657220533200000000001b00cc0119453a7ffffff30107346701250015000a00000001017ba8c000ffffff00161cf12e396b3aa8c0300ed559bb351a0000000101fa0c00020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002301000000001e0000001e000000240024002400240020002000720d720d1e0000000a0000000000000000000a00
|
||||
1
samples/discovery_resp_259_orig.hex
Normal file
1
samples/discovery_resp_259_orig.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740100000000001c7c0000000000000100d90000000f004275746c657220533200000000000000aaaa5dbbddfa55aa55ae77ebddaf570015000a00000001017ba8c000ffffff00161cf12e39000000000000000000001a0000000101fa0c000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023010000000024000000240000001c001c001d001d001c001c00f00af00a00000000000000000000000000000000
|
||||
1
samples/discovery_resp_259_rdm.hex
Normal file
1
samples/discovery_resp_259_rdm.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740100000000001c7c0000000000000100d90000000f004275746c657220533200000000000000aaaa5dbbddfa55aa55ae77ebddaf570015000a00000001017ba8c000ffffff00161cf12e39000000000000000000001a0000000101fa0c000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023010000000024000000240000001c001c001d001d001c001c00f00af00a00000000000000000000000000000000
|
||||
1
samples/dmx_a_1126.hex
Normal file
1
samples/dmx_a_1126.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003c040000010097010000000001011a8c191563d67616000000000100000001000002000201000000abaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302015554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302012c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
|
||||
1
samples/euce_582.hex
Normal file
1
samples/euce_582.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
4555434500000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000746f6e7963616f4445534b544f502d504544324c56442d66376634613034332d313765632d343135362d383132622d3930396264633939663266660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300ed559bb36
|
||||
1
samples/event_b_1126.hex
Normal file
1
samples/event_b_1126.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003c040000010000000000000002001903000000000000cc00000001000100000025000000010000000124ffffffffffff453a7ffffffe96010000001000010c80007ffffffffffffffffffe158e1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302018b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302017574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
|
||||
1
samples/heartbeat_78.hex
Normal file
1
samples/heartbeat_78.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c0000000000000100240000000300717ba8c0300ed559bb36
|
||||
50
samples/pid_map.txt
Normal file
50
samples/pid_map.txt
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
0080 454e65740400000000001c7c00000000000001005200000002004275746c000098000000000000002800cc0126453a7fffffff01073467012584000000002100800e4e46532d314b32447878784278200b32
|
||||
0504 454e65740400000000001c7c00000000000001003b0000000200017ba8c001000d010000000000001100aaab55af57be75aaddab5daa55ae57efd7
|
||||
8013 454e65740400000000001c7c00000000000001004500000002004275746c0000a2000000000000001b00cc0119453a7fffffff0107346701258e00000000218013010106ee
|
||||
8074 454e65740400000000001c7c00000000000001004500000002004275746c0000b9000000000000001b00cc0119453a7fffffff010734670125a50000000021807401000765
|
||||
0060 454e65740400000000001c7c00000000000001005700000002004275746c000021010000000000002d00cc012b453a7fffffff0107348809000c00000000210060130100020005090001000100010101006f00001106f6
|
||||
8021 454e65740400000000001c7c00000000000001004500000002004275746c0000a4000000000000001b00cc0119453a7fffffff0107346701259000000000218021010106fe
|
||||
8064 454e65740400000000001c7c00000000000001004600000002004275746c0000b3000000000000001c00cc011a453a7fffffff0107346701259f000000002180640200000751
|
||||
00c0 454e65740400000000001c7c00000000000001005700000002004275746c000096000000000000002d00cc012b453a7fffffff01073467012582000000002100c0134e46532d314b324478787842782056312e30310c90
|
||||
8051 454e65740400000000001c7c00000000000001004500000002004275746c0000ac000000000000001b00cc0119453a7fffffff010734670125980000000021805101000735
|
||||
8072 454e65740400000000001c7c00000000000001004500000002004275746c0000b7000000000000001b00cc0119453a7fffffff010734670125a30000000021807201000761
|
||||
8063 454e65740400000000001c7c00000000000001004600000002004275746c0000b2000000000000001c00cc011a453a7fffffff0107346701259e0000000021806302154d07b1
|
||||
8084 454e65740400000000001c7c00000000000001004600000002004275746c0000bf000000000000001c00cc011a453a7fffffff010734670125aa0000000021808402005c07d8
|
||||
8050 454e65740400000000001c7c00000000000001004500000002004275746c0000ab000000000000001b00cc0119453a7fffffff010734670125970000000021805001010734
|
||||
8071 454e65740400000000001c7c00000000000001004500000002004275746c0000b6000000000000001b00cc0119453a7fffffff010734670125a2000000002180710100075f
|
||||
0082 454e65740400000000001c7c00000000000001005000000002004275746c00009a000000000000002600cc0124453a7fffffff01073467012586000000002100820c4e4653314b324478787842780ae5
|
||||
8041 454e65740400000000001c7c00000000000001004500000002004275746c0000a8000000000000001b00cc0119453a7fffffff010734670125940000000021804101fe081f
|
||||
0003 454e65740400000000001c7c00000000000001005700000002004275746c00005c000000000000002d00cc012b453a7fffffff010734670125510000000021006013010001030509000100010002010100650000170736
|
||||
0401 454e65740400000000001c7c00000000000001004800000002004275746c00009c000000000000001e00cc011c453a7fffffff0107346701258800000000210401040000000d066c
|
||||
8090 454e65740400000000001c7c00000000000001004500000002004275746c0000c2000000000000001b00cc0119453a7fffffff010734670125ad00000000218090013207bb
|
||||
8053 454e65740400000000001c7c00000000000001004500000002004275746c0000ae000000000000001b00cc0119453a7fffffff0107346701259a0000000021805301000739
|
||||
8010 454e65740400000000001c7c00000000000001004500000002004275746c0000a0000000000000001b00cc0119453a7fffffff0107346701258c00000000218010010106e9
|
||||
8042 454e65740400000000001c7c00000000000001004500000002004275746c0000a9000000000000001b00cc0119453a7fffffff010734670125950000000021804201ff0822
|
||||
8061 454e65740400000000001c7c00000000000001004800000002004275746c0000b0000000000000001e00cc011c453a7fffffff0107346701259c00000000218061040000a3650857
|
||||
8081 454e65740400000000001c7c00000000000001004800000002004275746c00007c010000000000001e00cc011c453a7fffffff0107348809005e00000000218081040008a67b085e
|
||||
0405 454e65740400000000001c7c00000000000001004800000002004275746c00009f000000000000001e00cc011c453a7fffffff0107346701258b00000000210405040000006906cf
|
||||
00f0 454e65740400000000001c7c0000000000000100460000000200017ba8c000002e020000000000001c00cc011a453a7fffffff0107346701250f000000002100f00200650732
|
||||
0002 454e65740400000000001c7c00000000000001004600000002004275746c000020010000000000001c00cc011a453a7fffffff0107348809000b0000000011000202000005cf
|
||||
0403 454e65740400000000001c7c00000000000001004500000002004275746c00009e000000000000001b00cc0119453a7fffffff0107346701258a00000000210403017f06dc
|
||||
8060 454e65740400000000001c7c00000000000001004700000002004275746c0000af000000000000001d00cc011b453a7fffffff0107346701259b000000002180600309334707ce
|
||||
8022 454e65740400000000001c7c00000000000001004500000002004275746c0000a5000000000000001b00cc0119453a7fffffff010734670125910000000021802201010700
|
||||
8065 454e65740400000000001c7c00000000000001004800000002004275746c0000b4000000000000001e00cc011c453a7fffffff010734670125a00000000021806504000000000757
|
||||
8085 454e65740400000000001c7c00000000000001004800000002004275746c0000c0000000000000001e00cc011c453a7fffffff010734670125ab0000000021808504000000040786
|
||||
8011 454e65740400000000001c7c00000000000001004500000002004275746c0000a1000000000000001b00cc0119453a7fffffff0107346701258d00000000218011013c0726
|
||||
8070 454e65740400000000001c7c00000000000001004500000002004275746c0000b5000000000000001b00cc0119453a7fffffff010734670125a1000000002180700100075d
|
||||
8040 454e65740400000000001c7c00000000000001004500000002004275746c0000a7000000000000001b00cc0119453a7fffffff010734670125930000000021804001010720
|
||||
8023 454e65740400000000001c7c00000000000001004500000002004275746c0000a6000000000000001b00cc0119453a7fffffff010734670125920000000021802301000701
|
||||
8083 454e65740400000000001c7c00000000000001004600000002004275746c0000be000000000000001c00cc011a453a7fffffff010734670125a9000000002180830208f60878
|
||||
8052 454e65740400000000001c7c00000000000001004500000002004275746c0000ad000000000000001b00cc0119453a7fffffff010734670125990000000021805201010738
|
||||
1000 454e65740400000000001c7c00000000000001004500000002004275746c000097000000000000001b00cc0119453a7fffffff01073467012583000000002110000100065f
|
||||
0081 454e65740400000000001c7c00000000000001005f00000002004275746c000099000000000000003500cc0133453a7fffffff01073467012585000000002100811b496e76656e74726f6e6963732848616e677a686f75292c496e632e1111
|
||||
8043 454e65740400000000001c7c00000000000001004500000002004275746c0000aa000000000000001b00cc0119453a7fffffff010734670125960000000021804301ff0824
|
||||
8087 454e65740400000000001c7c00000000000001004800000002004275746c0000c1000000000000001e00cc011c453a7fffffff010734670125ac0000000021808704000000d30858
|
||||
0050 454e65740400000000001c7c00000000000001004e0000000200017ba8c00000fd010000000000002400cc0122453a7fffffff0899500026c9de000000002100500a020002010081008204030932
|
||||
8073 454e65740400000000001c7c00000000000001004500000002004275746c0000b8000000000000001b00cc0119453a7fffffff010734670125a40000000021807301000763
|
||||
8082 454e65740400000000001c7c00000000000001004500000002004275746c0000bd000000000000001b00cc0119453a7fffffff010734670125a80000000021808201210797
|
||||
0400 454e65740400000000001c7c00000000000001004800000002004275746c00009b000000000000001e00cc011c453a7fffffff010734670125870000000021040004000000c80725
|
||||
8080 454e65740400000000001c7c00000000000001004700000002004275746c0000ba000000000000001d00cc011b453a7fffffff010734670125a6000000002180800300006907df
|
||||
8062 454e65740400000000001c7c00000000000001004500000002004275746c0000b1000000000000001b00cc0119453a7fffffff0107346701259d00000000218062017f07ca
|
||||
0402 454e65740400000000001c7c00000000000001004800000002004275746c00009d000000000000001e00cc011c453a7fffffff010734670125890000000021040204000933840721
|
||||
8020 454e65740400000000001c7c00000000000001004500000002004275746c0000a3000000000000001b00cc0119453a7fffffff0107346701258f00000000218020010006fb
|
||||
1
samples/rdm_coldpulse_0.hex
Normal file
1
samples/rdm_coldpulse_0.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c0100bc010000000000001100aaaa5dbbddfa55aa55ae77ebddaf57fadf
|
||||
1
samples/rdm_coldpulse_1.hex
Normal file
1
samples/rdm_coldpulse_1.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c010036000000000000001100aaab55af57be75ef77ab55af75ae57ebd7
|
||||
1
samples/rdm_coldpulse_2.hex
Normal file
1
samples/rdm_coldpulse_2.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b0000000200017ba8c00100fb000000000000001100aaab55af57be75aaddab5daa55ae57efd7
|
||||
1
samples/rdm_coldpulse_3.hex
Normal file
1
samples/rdm_coldpulse_3.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c01009c050000000000001100fefefeffffffddffbfbfbfbfbfbf57efd7
|
||||
1
samples/rdm_coldpulse_4.hex
Normal file
1
samples/rdm_coldpulse_4.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b0000000200017ba8c001001b000000000000001100fefefefefefffffff7f7bfbfff35ddedd7
|
||||
1
samples/rdm_disc_req_1126.hex
Normal file
1
samples/rdm_disc_req_1126.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003c04000001000000000000000200090f000000000000cc00000001000000000025000000010000000124ffffffffffff453a7ffffffd86000000001000010c089950001767089950001f660e6c050500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043a0f4f7ec175641812b909bdc99f2ff4000000000010003000000000000000000000000000100030000000000000000000000000074006f006e007900630061006f004400450053004b0054004f0050002d0050004500440032004c00560044002d00660037006600340061003000340033002d0031003700650063002d0034003100350036002d0038003100320062002d0039003000390062006400630039003900660032006600660000000000000000000000911f343cf708bf342e8e519da944bd8a5a4a24433989ea070902001819410343003a005c00550073006500720073005c0074006f006e007900630061006f005c0041007000700044006100740061005c004c006f00630061006c005c0065006300750065005c00530059004d00500048004f004c004900470048005400200035002e0035005c00430075007200720065006e007400530068006f0077000000000000000000000000000000000000000000d00df1e819000000f6d1c94c090000003737b635230000001e00000000000000010000005a00ffffea0709080a1a2245020200000000000000000000000002000000000000000000000000000000000000000000000000010000000100000000000000010000000000000001000000010000002f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
|
||||
1
samples/rdm_info_119.hex
Normal file
1
samples/rdm_info_119.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001004d00000002004275746c00007e0f0000000000002300cc0121453a7ffffffd010734670125c00000000021020109000280000000000000071f
|
||||
178
samples/rdm_pool.txt
Normal file
178
samples/rdm_pool.txt
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
59 454e65740400000000001c7c00000000000001003b00000002004275746c01000b0f0000000000001100aaaa5dbbddfa55aa55ae77ebddaf57fadf
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c00002f0f0000000000001c00cc011a453a7ffffffd0899500026c99900000000110002020000076e
|
||||
87 454e65740400000000001c7c00000000000001005700000002004275746c0000310f0000000000002d00cc012b453a7ffffffd0899500026c99a0000000021006013010001000100010009050020000000010000040836
|
||||
59 454e65740400000000001c7c00000000000001003b00000002004275746c0100790f0000000000001100aaff7fffffaa5feaf5bad7ab7dab57ab77
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00007e0f0000000000002300cc0121453a7ffffffd010734670125c00000000021020109000280000000000000071f
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000800f0000000000002300cc0121453a7ffffffd010734670125c1000000002102010901001b00000000000006ba
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000830f0000000000002300cc0121453a7ffffffd010734670125c200000000210201090208dd08bc094000000893
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000850f0000000000002300cc0121453a7ffffffd010734670125c30000000021020109030010000505c900000786
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000870f0000000000002300cc0121453a7ffffffd010734670125c4000000002102010904003200000000000006d7
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000890f0000000000002300cc0121453a7ffffffd010734670125c500000000210201090500040004087f00000736
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00008b0f0000000000002300cc0121453a7ffffffd010734670125c6000000002102010906000000000000000006a9
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00008d0f0000000000002300cc0121453a7ffffffd010734670125c70000000021020109070019000b0d20000006fc
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00008f0f0000000000002300cc0121453a7ffffffd010734670125c8000000002102010908000000000000000006ad
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000910f0000000000002300cc0121453a7ffffffd010734670125c9000000002102010909000b00000000000006ba
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000930f0000000000002300cc0121453a7ffffffd010734670125ca00000000210201090a000000000000000006b1
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000950f0000000000002300cc0121453a7ffffffd010734670125cb00000000210201090b002900000000000006dc
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000960f0000000000002300cc0121453a7ffffffd010734670125cc00000000210201090c007f0000000000000734
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000980f0000000000002300cc0121453a7ffffffd010734670125cd00000000210201090d0503002a163200000731
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000990f0000000000002300cc0121453a7ffffffd010734670125ce00000000210201090e0000000000c700000780
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00009b0f0000000000002300cc0121453a7ffffffd010734670125cf00000000210201090f00000000037e0000073c
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00009d0f0000000000002300cc0121453a7ffffffd010734670125d0000000002102010910000000000000000006bd
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000a00f0000000000002300cc0121453a7ffffffd010734670125d1000000002102010911001200000000000006d1
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000a20f0000000000002300cc0121453a7ffffffd010734670125d2000000002102010912007f0000000000000740
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000a30f0000000000002300cc0121453a7ffffffd010734670125d3000000002102010913051c0044163b00000779
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000a60f0000000000002300cc0121453a7ffffffd010734670125d4000000002102010914000000000015000006da
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000a70f0000000000002300cc0121453a7ffffffd010734670125d500000000210201091500000000004200000709
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000a90f0000000000002300cc0121453a7ffffffd010734670125d6000000002102010916000000000000000006c9
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000ab0f0000000000001c00cc011a453a7ffffffd0899500026c9d7000000002100f002000108ab
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000ac0f0000000000002300cc0121453a7ffffffd0899500026c9d80000000021020109000ce400000000000008bc
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000ae0f0000000000002300cc0121453a7ffffffd0899500026c9d90000000021020109010945000000000000081c
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000b00f0000000000002300cc0121453a7ffffffd0899500026c9da000000002102010902001800000000000007e8
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000b20f0000000000002300cc0121453a7ffffffd0899500026c9db00000000210201090320db00000000000008cd
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000b50f0000000000001c00cc011a453a7ffffffd010734880900dc000000002100f0020070080c
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000b70f0000000000001b00cc0119453a7ffffffd010734880900dd00000000218011013c0778
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000b90f0000000000001b00cc0119453a7ffffffd010734880900de000000002180200100074c
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000ba0f0000000000001b00cc0119453a7ffffffd010734880900df000000002180210101074f
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000bc0f0000000000001b00cc0119453a7ffffffd010734880900e00000000021802201010751
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000be0f0000000000001b00cc0119453a7ffffffd010734880900e10000000021802301000752
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000bf0f0000000000001b00cc0119453a7ffffffd010734880900e20000000021804001010771
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000c10f0000000000001b00cc0119453a7ffffffd010734880900e30000000021804101fe0870
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000c30f0000000000001b00cc0119453a7ffffffd010734880900e40000000021804201ff0873
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000c50f0000000000001b00cc0119453a7ffffffd010734880900e50000000021804301ff0875
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000c70f0000000000001b00cc0119453a7ffffffd010734880900e60000000021805001010785
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000c90f0000000000001b00cc0119453a7ffffffd010734880900e70000000021805101000786
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000cb0f0000000000001b00cc0119453a7ffffffd010734880900e80000000021805201010789
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000ce0f0000000000001b00cc0119453a7ffffffd010734880900e9000000002180530100078a
|
||||
71 454e65740400000000001c7c00000000000001004700000002004275746c0000cf0f0000000000001d00cc011b453a7ffffffd010734880900ea00000000218060030006d8087a
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c0000d10f0000000000001e00cc011c453a7ffffffd010734880900eb000000002180610400039c2a0869
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000d30f0000000000001b00cc0119453a7ffffffd010734880900ec0000000021806201d80874
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000d50f0000000000001c00cc011a453a7ffffffd010734880900ed0000000021806302005e07fe
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000d60f0000000000001c00cc011a453a7ffffffd010734880900ee0000000021806402000007a2
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c0000d80f0000000000001e00cc011c453a7ffffffd010734880900ef00000000218065040000000007a8
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000da0f0000000000001b00cc0119453a7ffffffd010734880900f000000000218070010007ae
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000db0f0000000000001b00cc0119453a7ffffffd010734880900f100000000218071010007b0
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000de0f0000000000001b00cc0119453a7ffffffd010734880900f200000000218072010007b2
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000e00f0000000000001b00cc0119453a7ffffffd010734880900f300000000218073010007b4
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000e10f0000000000001b00cc0119453a7ffffffd010734880900f400000000218074010007b6
|
||||
71 454e65740400000000001c7c00000000000001004700000002004275746c0000e30f0000000000001d00cc011b453a7ffffffd010734880900f50000000021808003000075083c
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c0000e50f0000000000001e00cc011c453a7ffffffd010734880900f6000000002180810400082b820880
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000e70f0000000000001b00cc0119453a7ffffffd010734880900f700000000218082011b07e2
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000e90f0000000000001c00cc011a453a7ffffffd010734880900f8000000002180830208fe08d1
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000eb0f0000000000001c00cc011a453a7ffffffd010734880900f90000000021808402000007cd
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c0000ed0f0000000000001e00cc011c453a7ffffffd010734880900fa00000000218085040000000407d7
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c0000ee0f0000000000001e00cc011c453a7ffffffd010734880900fb00000000218087040000000007d6
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c0000f10f0000000000001b00cc0119453a7ffffffd010734880900fc000000002180900134080e
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000f20f0000000000002300cc0121453a7ffffffd010734880900fd000000002102010900021700000000000006f7
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000f40f0000000000002300cc0121453a7ffffffd010734880900fe000000002102010901001b00000000000006fb
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0100f60f0000000000002300cc0121453a7ffffffd010734880900ff00000000210201090208fe08cc096100000926
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000f80f0000000000002300cc0121453a7ffffffd0107348809000000000000210201090300000000024e00000634
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000fa0f0000000000002300cc0121453a7ffffffd010734880900010000000021020109040034000000000000061a
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000fc0f0000000000002300cc0121453a7ffffffd01073488090002000000002102010905000400040405000005f9
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c0000fe0f0000000000002300cc0121453a7ffffffd01073488090003000000002102010906000000000000000005ea
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000000100000000000002300cc0121453a7ffffffd0107348809000400000000210201090700000000054e0000063f
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000003100000000000002300cc0121453a7ffffffd01073488090005000000002102010908000000000000000005ee
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000004100000000000002300cc0121453a7ffffffd0107348809000600000000210201090900630000000000000653
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000007100000000000002300cc0121453a7ffffffd0107348809000700000000210201090a000000000000000005f2
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000008100000000000002300cc0121453a7ffffffd0107348809000800000000210201090b00ec00000000000006e0
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00000a100000000000002300cc0121453a7ffffffd0107348809000900000000210201090c000000000000000005f6
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00000c100000000000002300cc0121453a7ffffffd0107348809000a00000000210201090d005c00081536000006a7
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00000e100000000000002300cc0121453a7ffffffd0107348809000b00000000210201090e00000000057c0000067b
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000010100000000000002300cc0121453a7ffffffd0107348809000c00000000210201090f000000001dbb000006d4
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000012100000000000002300cc0121453a7ffffffd0107348809000d000000002102010910000000000000000005fe
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000014100000000000001b00cc0119453a7ffffffd0107346701250e00000000210403017f065e
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000016100000000000001b00cc0119453a7ffffffd0899500026c90f00000000210403010106f8
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000019100000000000001b00cc0119453a7ffffffd0107348809001000000000210403017f0664
|
||||
65 454e65740400000000001c7c00000000000001004100000002004275746c01001f100000000000001700fefefefefefffffffdf5fdeff7ddf7ddd5f775f5d7ffdf
|
||||
66 454e65740400000000001c7c00000000000001004200000002004275746c010021100000000000001800fefefefffffffcfdddfd7ffbffb5f5aeae7bffdf7f75ffff
|
||||
66 454e65740400000000001c7c00000000000001004200000002004275746c010023100000000000001800fefefffffdfffff7ba357fd7ddf75d55bbdff7dfffef9ddf
|
||||
67 454e65740400000000001c7c00000000000001004300000002004275746c010025100000000000001900feffffffffffffffabd5353dff5fabf7ddf57debd7bd75ffff
|
||||
57 454e65740400000000001c7c00000000000001003900000002004275746c010028100000000000000f00fefeffffffffffffd5ffffffffdfff
|
||||
59 454e65740400000000001c7c00000000000001003b00000002004275746c01002c100000000000001100aaab55af57be75efffab5deff5aed7ffff
|
||||
62 454e65740400000000001c7c00000000000001003e00000002004275746c010030100000000000001400fefefefefefefefffeffabffffffffffae57efd7
|
||||
66 454e65740400000000001c7c00000000000001004200000002004275746c010032100000000000001800fefefeffffffffffffffff7d7f757ffff7bdfdeff7b5dfff
|
||||
54 454e65740400000000001c7c00000000000001003600000002004275746c010034100000000000000c00fefe75fffffffbbbbbafdfff
|
||||
60 454e65740400000000001c7c00000000000001003c00000002004275746c010036100000000000001200fefefefeffff5fffdff57d7ff7ff3fddedd7
|
||||
54 454e65740400000000001c7c00000000000001003600000002004275746c010038100000000000000c00fefefffff5fffbfbfbffffd7
|
||||
58 454e65740400000000001c7c00000000000001003a00000002004275746c01003e100000000000001000fefefefffdff5ffff7f7fddf7f57efd7
|
||||
66 454e65740400000000001c7c00000000000001004200000002004275746c010042100000000000001800fefefefefffffffffffffffdff75ff57f7fddf5f7357efd7
|
||||
56 454e65740400000000001c7c00000000000001003800000002004275746c010044100000000000000e00fff57dfdfffffddf7dfd5fff7fff
|
||||
61 454e65740400000000001c7c00000000000001003d00000002004275746c010046100000000000001300fefefefffffffdb7fdf375f5fffdfbbf57efd7
|
||||
60 454e65740400000000001c7c00000000000001003c00000002004275746c010048100000000000001200fefefefefeffffffeed7ffbfbfffcd777bd7
|
||||
53 454e65740400000000001c7c00000000000001003500000002004275746c01004c100000000000000b00fefefeffffffffffb5dfff
|
||||
64 454e65740400000000001c7c00000000000001004000000002004275746c010051100000000000001600fefefefefefefffffffff777f777f7ff7f55ae57efd7
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c010052100000000000001c00fefefefefefefeffffffffbeffffbfd7bef7badfabfdeb55ae57efd7
|
||||
53 454e65740400000000001c7c00000000000001003500000002004275746c010058100000000000000b00febfffbfbfffffffbff5fd
|
||||
50 454e65740400000000001c7c00000000000001003200000002004275746c01005c100000000000000800fefdfffffffffffd
|
||||
59 454e65740400000000001c7c00000000000001003b00000002004275746c01005f100000000000001100aaab55af57be75ef77ab55af75ae57ebd7
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000a7100000000000001c00cc011a453a7ffffffd01073467012557000000001100020200000615
|
||||
87 454e65740400000000001c7c00000000000001005700000002004275746c0000aa100000000000002d00cc012b453a7ffffffd01073467012558000000002100601301000103050900010001000201010065000017073b
|
||||
59 454e65740400000000001c7c00000000000001003b00000002004275746c0100be100000000000001100aaab55af57be75aaddab5daa55ae57efd7
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c000006110000000000001c00cc011a453a7ffffffd0107348809008800000000110002020000064a
|
||||
87 454e65740400000000001c7c00000000000001005700000002004275746c000008110000000000002d00cc012b453a7ffffffd010734880900890000000021006013010002000509000100010001010100700000110772
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c0000b6110000000000001c00cc011a453a7ffffffd0899500026c9df0000000011000202000007b4
|
||||
87 454e65740400000000001c7c00000000000001005700000002004275746c0000b7110000000000002d00cc012b453a7ffffffd0899500026c9e0000000002100601301000100010001000905002000000001000004087c
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c000006120000000000001c00cc011a453a7ffffffd01073467012508000000002100f00200650729
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000008120000000000001b00cc0119453a7ffffffd010734670125090000000021801001010664
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00000b120000000000001b00cc0119453a7ffffffd0107346701250a00000000218011013c06a1
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00000d120000000000001b00cc0119453a7fbffffd0107346701250b0000000021801301010669
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00000d120000000000001b00cc0119453a7ffffffd0107346701250b0000000021801301010669
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000010120000000000001b00cc0119453a7ffffffd0107346701250c0000000021802001000676
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000011120000000000001b00cc0119453a7ffffffd0107346701250d0000000021802101010679
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000013120000000000001b00cc0119453a7ffffffd0107346701250e000000002180220101067b
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000015120000000000001b00cc0119453a7ffffffd0107346701250f000000002180230100067c
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000017120000000000001b00cc0119453a7ffffffd01073467012510000000002180400101069b
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000019120000000000001b00cc0119453a7ffffffd010734670125110000000021804101fe079a
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00001b120000000000001b00cc0119453a7ffffffd010734670125120000000021804201ff079d
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00001d120000000000001b00cc0119453a7ffffffd010734670125130000000021804301ff079f
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00001f120000000000001b00cc0119453a7ffffffd0107346701251400000000218050010106af
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000021120000000000001b00cc0119453a7ffffffd0107346701251500000000218051010006b0
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000023120000000000001b00cc0119453a7ffffffd0107346701251600000000218052010106b3
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000025120000000000001b00cc0119453a7ffffffd0107346701251700000000218053010006b4
|
||||
71 454e65740400000000001c7c00000000000001004700000002004275746c000027120000000000001d00cc011b453a7ffffffd0107346701251800000000218060030933470749
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c000029120000000000001e00cc011c453a7ffffffd0107346701251900000000218061040000a36507d2
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00002b120000000000001b00cc0119453a7ffffffd0107346701251a00000000218062017f0745
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c00002e120000000000001c00cc011a453a7ffffffd0107346701251b0000000021806302050306d2
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c000030120000000000001c00cc011a453a7ffffffd0107346701251c0000000021806402000006cc
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c000031120000000000001e00cc011c453a7ffffffd0107346701251d00000000218065040000000006d2
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000033120000000000001b00cc0119453a7ffffffd0107346701251e00000000218070010006d8
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000035120000000000001b00cc0119453a7ffffffd0107346701251f00000000218071010006da
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000037120000000000001b00cc0119453a7ffffffd0107346701252000000000218072010006dc
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000039120000000000001b00cc0119453a7ffffffd0107346701252100000000218073010006de
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00003b120000000000001b00cc0119453a7ffffffd0107346701252200000000218074010006e0
|
||||
71 454e65740400000000001c7c00000000000001004700000002004275746c00003d120000000000001d00cc011b453a7ffffffd010734670125230000000021808003000069075a
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c000040120000000000001e00cc011c453a7ffffffd0107346701252400000000218081040009c587084a
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c000041120000000000001b00cc0119453a7ffffffd0107346701252500000000218082011b070c
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c000043120000000000001c00cc011a453a7ffffffd01073467012526000000002180830208e507e2
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c000045120000000000001c00cc011a453a7ffffffd010734670125270000000021808402000b0702
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c000047120000000000001e00cc011c453a7ffffffd010734670125280000000021808504000000040701
|
||||
72 454e65740400000000001c7c00000000000001004800000002004275746c000049120000000000001e00cc011c453a7ffffffd010734670125290000000021808704000000190719
|
||||
69 454e65740400000000001c7c00000000000001004500000002004275746c00004b120000000000001b00cc0119453a7ffffffd0107346701252a0000000021809001320736
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00004d120000000000002300cc0121453a7ffffffd0107346701252b0000000021020109000280000000000000068a
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00004f120000000000002300cc0121453a7ffffffd0107346701252c000000002102010901001b0000000000000625
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000051120000000000002300cc0121453a7ffffffd0107346701252d00000000210201090208dd08bc0940000007fe
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000053120000000000002300cc0121453a7ffffffd0107346701252e0000000021020109030010000505c9000006f1
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000055120000000000002300cc0121453a7ffffffd0107346701252f00000000210201090400320000000000000642
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000057120000000000002300cc0121453a7ffffffd0107346701253000000000210201090500040004087f000006a1
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000059120000000000002300cc0121453a7ffffffd0107346701253100000000210201090600000000000000000614
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00005b120000000000002300cc0121453a7ffffffd010734670125320000000021020109070019000b0d2000000667
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00005d120000000000002300cc0121453a7ffffffd0107346701253300000000210201090800000000000000000618
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00005e120000000000002300cc0121453a7ffffffd010734670125340000000021020109090010000000000000062a
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000060120000000000002300cc0121453a7ffffffd0107346701253500000000210201090a0000000000000000061c
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000062120000000000002300cc0121453a7ffffffd0107346701253600000000210201090b00290000000000000647
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000064120000000000002300cc0121453a7ffffffd0107346701253700000000210201090c007f000000000000069f
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000066120000000000002300cc0121453a7ffffffd0107346701253800000000210201090d04fa002a163200000792
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000068120000000000002300cc0121453a7ffffffd0107346701253900000000210201090e0000000000c7000006eb
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00006a120000000000002300cc0121453a7ffffffd0107346701253a00000000210201090f00000000037e000006a7
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00006c120000000000002300cc0121453a7ffffffd0107346701253b00000000210201091000000000000000000628
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00006e120000000000002300cc0121453a7ffffffd0107346701253c0000000021020109110012000000000000063c
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000070120000000000002300cc0121453a7ffffffd0107346701253d000000002102010912007f00000000000006ab
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000073120000000000002300cc0121453a7ffffffd0107346701253e000000002102010913051c0044163b000006e4
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000074120000000000002300cc0121453a7ffffffd0107346701253f00000000210201091400000000001500000645
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000076120000000000002300cc0121453a7ffffffd0107346701254000000000210201091500000000004200000674
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000078120000000000002300cc0121453a7ffffffd0107346701254100000000210201091600000000000000000634
|
||||
70 454e65740400000000001c7c00000000000001004600000002004275746c00007a120000000000001c00cc011a453a7ffffffd0899500026c942000000002100f00200010816
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00007c120000000000002300cc0121453a7ffffffd0899500026c9430000000021020109000ce40000000000000827
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c00007f120000000000002300cc0121453a7ffffffd0899500026c94400000000210201090109450000000000000787
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000080120000000000002300cc0121453a7ffffffd0899500026c94500000000210201090200180000000000000753
|
||||
77 454e65740400000000001c7c00000000000001004d00000002004275746c000082120000000000002300cc0121453a7ffffffd0899500026c94600000000210201090320dd000000000000083a
|
||||
1
samples/rdm_pulse_101.hex
Normal file
1
samples/rdm_pulse_101.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c01000b0f0000000000001100aaaa5dbbddfa55aa55ae77ebddaf57fadf
|
||||
1
samples/rdm_pulse_101_0.hex
Normal file
1
samples/rdm_pulse_101_0.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c01000b0f0000000000001100aaaa5dbbddfa55aa55ae77ebddaf57fadf
|
||||
1
samples/rdm_pulse_101_1.hex
Normal file
1
samples/rdm_pulse_101_1.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c01005f100000000000001100aaab55af57be75ef77ab55af75ae57ebd7
|
||||
1
samples/rdm_pulse_101_2.hex
Normal file
1
samples/rdm_pulse_101_2.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c0100be100000000000001100aaab55af57be75aaddab5daa55ae57efd7
|
||||
1
samples/rdm_pulse_101_3.hex
Normal file
1
samples/rdm_pulse_101_3.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c0100790f0000000000001100aaff7fffffaa5feaf5bad7ab7dab57ab77
|
||||
1
samples/rdm_pulse_101_4.hex
Normal file
1
samples/rdm_pulse_101_4.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003b00000002004275746c01002c100000000000001100aaab55af57be75efffab5deff5aed7ffff
|
||||
1
samples/rdm_req_1126.hex
Normal file
1
samples/rdm_req_1126.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001003c040000010000000000000002007f0f000000000000cc00000001000100000025000000010000000124ffffffffffff453a7ffffffccc010000001000010c80007ffffffffffffffffffe15c291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a0908070605040302014a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
|
||||
1
samples/rdm_resp_111.hex
Normal file
1
samples/rdm_resp_111.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001004500000002004275746c0000b70f0000000000001b00cc0119453a7ffffffd010734880900dd00000000218011013c0778
|
||||
1
samples/rdm_setack_68.hex
Normal file
1
samples/rdm_setack_68.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001004400000002004275746c00006efa0600000000001a00cc0118453a7fffffff010734880900b1000000003100f000077f
|
||||
1
samples/register_89.hex
Normal file
1
samples/register_89.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740200000000001c7c00000000000001002f0000000200017ba8c000161cf12e3900717ba8c0300ed559bb36
|
||||
1
samples/status_84.hex
Normal file
1
samples/status_84.hex
Normal file
|
|
@ -0,0 +1 @@
|
|||
454e65740400000000001c7c00000000000001002a00000002004275746c010062000000010000000000
|
||||
1226
src/aech.rs
Normal file
1226
src/aech.rs
Normal file
File diff suppressed because it is too large
Load Diff
1201
src/dmxhash.rs
Normal file
1201
src/dmxhash.rs
Normal file
File diff suppressed because it is too large
Load Diff
239
src/enet.rs
Normal file
239
src/enet.rs
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
// enet.rs - e:net 协议公共设施
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub const BIND: &str = "192.168.123.113:31772";
|
||||
pub const BUTLER: &str = "192.168.123.1:31772";
|
||||
pub const BCAST_SUB: &str = "192.168.123.255:31772";
|
||||
pub const BCAST_ALL: &str = "255.255.255.255:31772";
|
||||
|
||||
// ---------- 瀛楁璇诲啓 (灏忕) ----------
|
||||
pub fn g16(b: &[u8], o: usize) -> u16 { b[o] as u16 | (b[o + 1] as u16) << 8 }
|
||||
pub fn g32(b: &[u8], o: usize) -> u32 {
|
||||
b[o] as u32 | (b[o + 1] as u32) << 8 | (b[o + 2] as u32) << 16 | (b[o + 3] as u32) << 24
|
||||
}
|
||||
pub fn p16(b: &mut [u8], o: usize, v: u16) { b[o] = v as u8; b[o + 1] = (v >> 8) as u8; }
|
||||
pub fn p32(b: &mut [u8], o: usize, v: u32) {
|
||||
b[o] = v as u8; b[o + 1] = (v >> 8) as u8; b[o + 2] = (v >> 16) as u8; b[o + 3] = (v >> 24) as u8;
|
||||
}
|
||||
pub fn find(b: &[u8], pat: &[u8]) -> bool {
|
||||
b.windows(pat.len()).any(|w| w == pat)
|
||||
}
|
||||
pub fn hexs(b: &[u8]) -> String { b.iter().map(|x| format!("{:02x}", x)).collect() }
|
||||
|
||||
// ---------- 妯℃澘鍔犺浇 (缂栬瘧鏈熷祵鍏?pcap 瀹炴祴鏍锋湰) ----------
|
||||
/// 真机 RDM 应答帧池: 每行 "<载荷长度> <hex>"
|
||||
pub fn load_rdm_pool() -> Vec<Vec<u8>> {
|
||||
let txt = include_str!("../samples/rdm_pool.txt");
|
||||
let mut out = Vec::new();
|
||||
for line in txt.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() { continue; }
|
||||
if let Some(pos) = line.find(' ') {
|
||||
let hex = line[pos + 1..].trim();
|
||||
let b: Vec<u8> = (0..hex.len()).step_by(2)
|
||||
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap()).collect();
|
||||
out.push(b);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn load_template(name: &str) -> Vec<u8> {
|
||||
let hex = match name {
|
||||
"discovery_req" => include_str!("../samples/discovery_req_222.hex"),
|
||||
"discovery_resp" => include_str!("../samples/discovery_resp_259.hex"),
|
||||
"announce" => include_str!("../samples/announce_686.hex"),
|
||||
"announce_live" => include_str!("../samples/announce_live.hex"),
|
||||
"heartbeat" => include_str!("../samples/heartbeat_78.hex"),
|
||||
"euce" => include_str!("../samples/euce_582.hex"),
|
||||
"dmx_a" => include_str!("../samples/dmx_a_1126.hex"),
|
||||
"event_b" => include_str!("../samples/event_b_1126.hex"),
|
||||
"ack" => include_str!("../samples/ack_84.hex"),
|
||||
"rdm_req" => include_str!("../samples/rdm_req_1126.hex"),
|
||||
"rdm_disc_req" => include_str!("../samples/rdm_disc_req_1126.hex"),
|
||||
"claim_h112" => include_str!("../samples/claim_h112.hex"),
|
||||
"claim_h114" => include_str!("../samples/claim_h114.hex"),
|
||||
"claim_h116" => include_str!("../samples/claim_h116.hex"),
|
||||
"aerep" => include_str!("../samples/aerep_113.hex"),
|
||||
"status" => include_str!("../samples/status_84.hex"),
|
||||
"rdm_pulse_0" => include_str!("../samples/rdm_pulse_101_0.hex"),
|
||||
"rdm_pulse_1" => include_str!("../samples/rdm_pulse_101_1.hex"),
|
||||
"rdm_pulse_2" => include_str!("../samples/rdm_pulse_101_2.hex"),
|
||||
"rdm_pulse_3" => include_str!("../samples/rdm_pulse_101_3.hex"),
|
||||
"rdm_pulse_4" => include_str!("../samples/rdm_pulse_101_4.hex"),
|
||||
"rdm_resp111" => include_str!("../samples/rdm_resp_111.hex"),
|
||||
"rdm_setack68" => include_str!("../samples/rdm_setack_68.hex"),
|
||||
|
||||
"rdm_info119" => include_str!("../samples/rdm_info_119.hex"),
|
||||
"register89" => include_str!("../samples/register_89.hex"),
|
||||
"aere594" => include_str!("../samples/aere_594.hex"),
|
||||
"announce144" => include_str!("../samples/announce_144.hex"),
|
||||
"rdm_disc_req" => include_str!("../samples/rdm_disc_req_1126.hex"),
|
||||
_ => panic!("unknown template {}", name),
|
||||
};
|
||||
let h = hex.trim();
|
||||
(0..h.len()).step_by(2).map(|i| u8::from_str_radix(&h[i..i + 2], 16).unwrap()).collect()
|
||||
}
|
||||
|
||||
// ---------- Socket ----------
|
||||
pub fn open() -> UdpSocket {
|
||||
let s = UdpSocket::bind(BIND).expect("bind 192.168.123.113:31772 澶辫触(缃戝彛/绔彛鍗犵敤?)");
|
||||
s.set_broadcast(true).unwrap();
|
||||
s.set_read_timeout(Some(Duration::from_millis(2))).unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
/// SO_REUSEADDR bind (Windows)
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn open_reuse(bind_addr: &str) -> UdpSocket {
|
||||
use std::os::windows::io::FromRawSocket;
|
||||
#[repr(C)]
|
||||
struct SockAddrIn { sin_family: u16, sin_port: u16, sin_addr: u32, sin_zero: [u8; 8] }
|
||||
#[repr(C)]
|
||||
struct WsaData { wversion: u16, whighversion: u16, szdescription: [u8; 257], szsystemstatus: [u8; 129], imaxsockets: u16, imaxudpdg: u16, lpvendorinfo: usize }
|
||||
#[link(name = "ws2_32")]
|
||||
extern "system" {
|
||||
fn WSAStartup(wver: u16, data: *mut WsaData) -> i32;
|
||||
fn socket(af: i32, ty: i32, proto: i32) -> usize;
|
||||
fn setsockopt(s: usize, level: i32, optname: i32, optval: *const u8, optlen: i32) -> i32;
|
||||
fn bind(s: usize, addr: *const SockAddrIn, len: i32) -> i32;
|
||||
}
|
||||
unsafe {
|
||||
let mut wd: WsaData = std::mem::zeroed();
|
||||
WSAStartup(0x0202, &mut wd);
|
||||
let s = socket(2, 2, 17); // AF_INET, SOCK_DGRAM, IPPROTO_UDP
|
||||
assert!(s != usize::MAX, "ws socket 澶辫触");
|
||||
const SOL_SOCKET: i32 = 0xFFFF;
|
||||
const SO_REUSEADDR: i32 = 4;
|
||||
const SO_BROADCAST: i32 = 6;
|
||||
let one: u8 = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, 1);
|
||||
setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, 1);
|
||||
let (ip, port) = bind_addr.rsplit_once(':').unwrap();
|
||||
let o: Vec<u8> = ip.split('.').map(|x| x.parse().unwrap()).collect();
|
||||
let a = SockAddrIn {
|
||||
sin_family: 2,
|
||||
sin_port: port.parse::<u16>().unwrap().to_be(),
|
||||
sin_addr: u32::from_ne_bytes([o[0], o[1], o[2], o[3]]),
|
||||
sin_zero: [0; 8],
|
||||
};
|
||||
let r = bind(s, &a, std::mem::size_of::<SockAddrIn>() as i32);
|
||||
if r != 0 {
|
||||
let err: i32 = { extern "system" { fn WSAGetLastError() -> i32; } WSAGetLastError() };
|
||||
panic!("reuse bind {} 澶辫触 r={} WSAE={}", bind_addr, r, err);
|
||||
}
|
||||
let u = UdpSocket::from_raw_socket(s as _);
|
||||
u.set_broadcast(true).unwrap();
|
||||
u.set_read_timeout(Some(Duration::from_millis(2))).unwrap();
|
||||
u
|
||||
}
|
||||
}
|
||||
|
||||
/// Linux: 鏍囧噯 bind 鍗冲厑璁稿杩涚▼ SO_REUSEADDR 鍏卞瓨, 鐩存帴杩斿洖 (daemon 鐢?
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn open_reuse(bind_addr: &str) -> UdpSocket {
|
||||
use std::net::UdpSocket as S;
|
||||
use std::os::unix::io::FromRawFd;
|
||||
#[repr(C)]
|
||||
struct SockAddrIn { sin_family: u16, sin_port: u16, sin_addr: u32, sin_zero: [u8; 8] }
|
||||
#[link(name = "c")]
|
||||
extern "C" {
|
||||
fn socket(af: i32, ty: i32, proto: i32) -> i32;
|
||||
fn setsockopt(s: i32, level: i32, optname: i32, optval: *const u8, optlen: u32) -> i32;
|
||||
fn bind(s: i32, addr: *const SockAddrIn, len: u32) -> i32;
|
||||
}
|
||||
const AF_INET: i32 = 2;
|
||||
const SOCK_DGRAM: i32 = 2;
|
||||
const IPPROTO_UDP: i32 = 17;
|
||||
const IPPROTO_IP: i32 = 0;
|
||||
const IP_TTL: i32 = 2;
|
||||
const IP_MTU_DISCOVER: i32 = 10;
|
||||
const SOL_SOCKET: i32 = 1;
|
||||
const SO_REUSEADDR: i32 = 2;
|
||||
const SO_BROADCAST: i32 = 6;
|
||||
unsafe {
|
||||
let s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
assert!(s >= 0, "socket 澶辫触");
|
||||
let one: i32 = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one as *const i32 as *const u8, 4);
|
||||
setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one as *const i32 as *const u8, 4);
|
||||
// 鎸囩汗瀵归綈鐪熸満 Butler: TTL=100, 鍏抽棴 DF (PMTUDISC_DONT)
|
||||
let ttl: i32 = 100;
|
||||
setsockopt(s, IPPROTO_IP, IP_TTL, &ttl as *const i32 as *const u8, 4);
|
||||
let pmtu: i32 = 0; // IP_PMTUDISC_DONT
|
||||
setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu as *const i32 as *const u8, 4);
|
||||
let (ip, port) = bind_addr.rsplit_once(':').unwrap();
|
||||
let o: Vec<u8> = ip.split('.').map(|x| x.parse().unwrap()).collect();
|
||||
let a = SockAddrIn {
|
||||
sin_family: AF_INET as u16,
|
||||
sin_port: port.parse::<u16>().unwrap().to_be(),
|
||||
sin_addr: u32::from_ne_bytes([o[0], o[1], o[2], o[3]]),
|
||||
sin_zero: [0; 8],
|
||||
};
|
||||
let r = bind(s, &a, std::mem::size_of::<SockAddrIn>() as u32);
|
||||
if r != 0 {
|
||||
panic!("reuse bind {} 澶辫触 r={} errno={}", bind_addr, r, std::io::Error::last_os_error());
|
||||
}
|
||||
let u = S::from_raw_fd(s);
|
||||
u.set_broadcast(true).unwrap();
|
||||
u.set_read_timeout(Some(Duration::from_millis(2))).unwrap();
|
||||
u
|
||||
}
|
||||
}
|
||||
|
||||
/// 鍦?dur 鏃堕棿绐楀唴鏀堕泦鎵€鏈夊埌杈剧殑 UDP 包)
|
||||
pub fn recv_window(s: &UdpSocket, dur: Duration) -> Vec<(SocketAddr, Vec<u8>)> {
|
||||
let deadline = Instant::now() + dur;
|
||||
let mut out = Vec::new();
|
||||
loop {
|
||||
let now = Instant::now();
|
||||
if now >= deadline { break; }
|
||||
s.set_read_timeout(Some(deadline.saturating_duration_since(now))).unwrap();
|
||||
let mut buf = [0u8; 2048];
|
||||
match s.recv_from(&mut buf) {
|
||||
Ok((n, a)) => out.push((a, buf[..n].to_vec())),
|
||||
Err(_) => break, // 瓒呮椂
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
// ---------- 鏀跺寘鍒嗙被 ----------
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Rx {
|
||||
/// Butler 鍙戠幇搴旂瓟 (259B 甯?/ 217B 璐熻浇)
|
||||
DiscoveryResp(Vec<u8>),
|
||||
/// Butler ACK (84B 甯?/ 42B 璐熻浇) ack_seq
|
||||
Ack(u32),
|
||||
/// Butler 鐘舵€佸抚 (>90B 璐熻浇, 0200+Butl)
|
||||
Status(Vec<u8>),
|
||||
/// RDM 搴旂瓟 (璐熻浇 60-90B, 0x28 闀垮害 + cc01)
|
||||
RdmResp(Vec<u8>, usize),
|
||||
/// 涓绘満瀹e憡搴旂瓟/鍏朵粬 ENet type=01
|
||||
EnetType1(usize),
|
||||
/// 鍏跺畠
|
||||
Other(usize),
|
||||
}
|
||||
|
||||
pub fn classify(b: &[u8]) -> Rx {
|
||||
if b.len() < 8 || &b[0..4] != b"ENet" {
|
||||
return Rx::Other(b.len());
|
||||
}
|
||||
let t = g32(b, 4);
|
||||
if t == 1 { return Rx::EnetType1(b.len()); }
|
||||
if t == 4 {
|
||||
if b.len() == 42 && g16(b, 0x18) == 2 && &b[0x1A..0x1E] == b"Butl" {
|
||||
return Rx::Ack(g32(b, 0x20));
|
||||
}
|
||||
if b.len() > 0x2C && g16(b, 0x18) == 2 && &b[0x1A..0x1E] == b"Butl" {
|
||||
let rlen = g16(b, 0x28) as usize;
|
||||
if b.len() >= 0x2A + rlen && b[0x2A] == 0xCC && b[0x2B] == 0x01 {
|
||||
return Rx::RdmResp(b.to_vec(), rlen);
|
||||
}
|
||||
return Rx::Status(b.to_vec());
|
||||
}
|
||||
if b.len() == 217 { return Rx::DiscoveryResp(b.to_vec()); }
|
||||
}
|
||||
Rx::Other(b.len())
|
||||
}
|
||||
909
src/hostdemo.rs
Normal file
909
src/hostdemo.rs
Normal file
|
|
@ -0,0 +1,909 @@
|
|||
//! hostdemo.rs — 主机侧等效示例程序 (2026-09-10): 认领真机 + RDM 发现 + 加密 DMX 控制
|
||||
//!
|
||||
//! 全流程不依赖 SymphoLight:
|
||||
//! 1. 发现 (type=1, 180B) → 217B 应答
|
||||
//! 2. sub=01 宣告 → 89B 注册应答
|
||||
//! 3. AECH 认领 ×3 轮 (nonce/B12 自选, AERE[0xC0]==G(nonce) 逐轮验证)
|
||||
//! ★ 真机 .1 只应答源 IP=192.168.123.113 的 AECH (固件过滤, §3.7)
|
||||
//! 4. RDM 发现: E1.20 DISC_UNIQUE_BRANCH 二分遍历 + DISC 脉冲解码 (§5.4 逆运算)
|
||||
//! → 每 UID 发 DISC_MUTE(0002) 收 70B ACK → GET DEVICE_INFO/DMX_START_ADDRESS/DEVICE_LABEL
|
||||
//! 5. DMX 控制: 30fps zPprv0TXRRW 加密帧 (§4.1), 按发现的灯具地址做呼吸图案
|
||||
//!
|
||||
//! 注意: 真机控灯! 且 SymphoLight 若同时运行会插黑场帧 (两主机流交替 → 闪烁)。
|
||||
|
||||
use std::net::UdpSocket;
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::aech::{agios_dmx_encrypt, agios_g};
|
||||
use crate::enet::{load_template, open_reuse};
|
||||
|
||||
const HOST_RDM_UID: [u8; 6] = [0x45, 0x3a, 0x7f, 0xff, 0xff, 0xff]; // 与真主机一致 (§5.3)
|
||||
const BCAST_UID: [u8; 6] = [0xff; 6];
|
||||
|
||||
/// 真机冷启动实测: AECH 认领前主机先发 102B type=2 身份帧 (cold2 @38.087)
|
||||
/// 主机 IP@0x1C (LE), MAC@0x20 (模板值即本机 123 网卡)
|
||||
const IDENT102: [u8; 102] = [
|
||||
0x45, 0x4e, 0x65, 0x74, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x7c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x66, 0x00, 0x00, 0x00, 0x01, 0x00, 0x71, 0x7b, 0xa8, 0xc0, 0x30, 0x0e,
|
||||
0xd5, 0x59, 0xbb, 0x36, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
/// ★type=02 sub=07 绑定帧 (52B, sym_claim2 @351.1/353.4 — 认领前 2s 各一发):
|
||||
/// 主机IP+MAC@0x1A/0x1E, 设备IP+MAC@0x25/0x29, ffff/0000@0x2F (两发唯一差异), 6400 f401@0x31
|
||||
/// (0x64=100, 0x01f4=500 — 疑似刷新率参数, 原样保留)
|
||||
fn build_bind52(flag16: [u8; 2]) -> Vec<u8> {
|
||||
let mut f = Vec::with_capacity(52);
|
||||
f.extend_from_slice(b"ENet");
|
||||
f.extend_from_slice(&2u32.to_le_bytes());
|
||||
f.extend_from_slice(&[0x00, 0x00, 0x1c, 0x7c]);
|
||||
f.extend_from_slice(&[0u8; 4]);
|
||||
f.extend_from_slice(&[0x00, 0x00, 0x01, 0x00]);
|
||||
f.extend_from_slice(&52u32.to_le_bytes());
|
||||
f.extend_from_slice(&[0x07, 0x00]); // sub=07
|
||||
f.extend_from_slice(&[0x71, 0x7b, 0xa8, 0xc0]); // 占位: 主机IP 逆序字节 (patch)
|
||||
f.extend_from_slice(&[0x30, 0x0e, 0xd5, 0x59, 0xbb, 0x36]); // 主机MAC
|
||||
f.push(0x00);
|
||||
f.extend_from_slice(&[0x01, 0x7b, 0xa8, 0xc0]); // 设备IP LE = 192.168.123.1
|
||||
f.extend_from_slice(&[0x00, 0x16, 0x1c, 0xf1, 0x2e, 0x39]); // 设备MAC
|
||||
f.extend_from_slice(&flag16);
|
||||
f.extend_from_slice(&[0x64, 0x00, 0xf4, 0x01]);
|
||||
f
|
||||
}
|
||||
|
||||
fn g16(b: &[u8], o: usize) -> u16 { u16::from_le_bytes([b[o], b[o + 1]]) }
|
||||
fn g32(b: &[u8], o: usize) -> u32 { u32::from_le_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]]) }
|
||||
fn uid_u64(u: &[u8; 6]) -> u64 { u64::from_be_bytes([0, 0, u[0], u[1], u[2], u[3], u[4], u[5]]) }
|
||||
fn u64_uid(v: u64) -> [u8; 6] { let b = v.to_be_bytes(); [b[2], b[3], b[4], b[5], b[6], b[7]] }
|
||||
fn hexs(b: &[u8]) -> String { b.iter().map(|x| format!("{:02x}", x)).collect() }
|
||||
fn uid_s(u: &[u8; 6]) -> String { hexs(u) }
|
||||
|
||||
struct Rng(u32);
|
||||
impl Rng {
|
||||
fn new() -> Self { Rng(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos() | 1) }
|
||||
fn next(&mut self) -> u32 { self.0 ^= self.0 << 13; self.0 ^= self.0 >> 17; self.0 ^= self.0 << 5; self.0 & 0x7FFF_FFFF }
|
||||
fn byte(&mut self) -> u8 { (self.next() >> 8) as u8 }
|
||||
}
|
||||
|
||||
/// E1.20 请求构建: 在 1084B 事件帧模板上 patch RDM 消息 (§4.2/§5.3)
|
||||
/// frame: 0x22 qseq(LE), 0x34=msgLen+1(LE), 0x3C 起 = SubSC/msgLen/destUID/srcUID/TN/...
|
||||
fn build_rdm_frame(dest: &[u8; 6], tn: u8, qseq: u16, cc: u8, pid: u16, pd: &[u8]) -> Vec<u8> {
|
||||
let mut f = load_template("rdm_disc_req"); // 1084B, flag=00 独立帧形态
|
||||
let msg_len = 24 + pd.len(); // CC..PD 末 (不含 checksum)
|
||||
// ★RDM 请求帧头与 DMX 帧不同 (SL 实时帧对照): 计数器/随机槽位必须清零
|
||||
f[0x1A..0x20].copy_from_slice(&[0u8; 6]); // 0x1A 计数器(会话内从0) + 0x1E-0x1F
|
||||
f[0x24..0x2A].copy_from_slice(&[0u8; 6]); // 0x24-0x29 (DMX 帧的 r1/r2 槽位 = 0)
|
||||
f[0x22] = (qseq & 0xFF) as u8;
|
||||
f[0x23] = (qseq >> 8) as u8;
|
||||
f[0x30] = 0; // flag=00 独立查询帧 (真机只应答此类)
|
||||
f[0x34..0x38].copy_from_slice(&((msg_len + 1) as u32).to_le_bytes());
|
||||
f[0x3C] = 0x01; // SubSC
|
||||
f[0x3D] = msg_len as u8;
|
||||
f[0x3E..0x44].copy_from_slice(dest);
|
||||
f[0x44..0x4A].copy_from_slice(&HOST_RDM_UID);
|
||||
f[0x4A] = tn;
|
||||
f[0x4B] = 0x00; // 独立帧 port 字段=00 (模板实证; flag=01 内嵌副本才是 01)
|
||||
f[0x4C] = 0x00; // msgCount
|
||||
f[0x4D] = 0; f[0x4E] = 0; // subDevice
|
||||
f[0x4F] = cc;
|
||||
f[0x50] = (pid >> 8) as u8;
|
||||
f[0x51] = (pid & 0xFF) as u8;
|
||||
f[0x52] = pd.len() as u8;
|
||||
f[0x53..0x53 + pd.len()].copy_from_slice(pd);
|
||||
// checksum: 含隐式 0xCC 起始字节, 从 CC 起 msgLen 字节 (不含自身), BE
|
||||
let mut sum: u16 = 0xCC;
|
||||
for i in 0x3C..0x3C + msg_len { sum = sum.wrapping_add(f[i] as u16); }
|
||||
let cs = 0x53 + pd.len();
|
||||
f[cs] = (sum >> 8) as u8;
|
||||
f[cs + 1] = (sum & 0xFF) as u8;
|
||||
f
|
||||
}
|
||||
|
||||
/// DISC 脉冲解码 (§5.4 逆运算): data = RDM 数据区 (0x2A 起), 返回 Some(UID) 或 None(冲突/非法)
|
||||
fn decode_pulse(d: &[u8]) -> Option<[u8; 6]> {
|
||||
if d.len() < 17 || d[0] != 0xAA { return None; }
|
||||
let mut uid = [0u8; 6];
|
||||
let mut sum: u16 = 0;
|
||||
for i in 0..6 {
|
||||
let b1 = d[1 + 2 * i];
|
||||
let b2 = d[2 + 2 * i];
|
||||
// 编码有效性: b1 高位必含 0xAA 模式, b2 必含 0x55 — 违例即多机冲突
|
||||
if b1 & 0xAA != 0xAA || b2 & 0x55 != 0x55 { return None; }
|
||||
uid[i] = (b1 & 0x55) | (b2 & 0xAA);
|
||||
sum = sum.wrapping_add(b1 as u16).wrapping_add(b2 as u16);
|
||||
}
|
||||
let cs_hi = (d[13] & 0x55) | (d[14] & 0xAA);
|
||||
let cs_lo = (d[15] & 0x55) | (d[16] & 0xAA);
|
||||
let cs = ((cs_hi as u16) << 8) | cs_lo as u16;
|
||||
if cs != sum { return None; } // 冲突/损坏
|
||||
Some(uid)
|
||||
}
|
||||
|
||||
/// 从 Butler→主机 应答帧提取 RDM 数据区 (§5.1): 返回 (is_pulse, data)
|
||||
fn rdm_region(b: &[u8]) -> Option<(bool, &[u8])> {
|
||||
if b.len() < 0x2E || &b[0..4] != b"ENet" || g32(b, 4) != 4 { return None; }
|
||||
if g16(b, 0x18) != 0x0002 { return None; }
|
||||
if &b[0x1A..0x1E] != b"Butl" { return None; }
|
||||
let rlen = g16(b, 0x28) as usize;
|
||||
if b.len() < 0x2A + rlen { return None; }
|
||||
let d = &b[0x2A..0x2A + rlen];
|
||||
Some((d[0] == 0xAA, d))
|
||||
}
|
||||
|
||||
pub fn run(target: &str, bind_ip: &str, secs: u64) {
|
||||
let dst = format!("{}:31772", target);
|
||||
let sock = open_reuse(&format!("{}:31772", bind_ip)); // 源 123.113:31772 (真机过滤+主机惯例)
|
||||
sock.set_read_timeout(Some(Duration::from_millis(5))).unwrap();
|
||||
let mut rng = Rng::new();
|
||||
let mut buf = [0u8; 4096];
|
||||
|
||||
println!("=== 主机侧等效示例: 目标 {} (源 {}) ===", dst, bind_ip);
|
||||
|
||||
// ---- 1) 发现 ----
|
||||
let disc = load_template("discovery_req");
|
||||
sock.send_to(&disc, &dst).unwrap();
|
||||
let mut dev_ok = false;
|
||||
for _ in 0..150 {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
if n >= 200 && &b[0..4] == b"ENet" && g32(b, 4) == 1 {
|
||||
println!("[1] 发现应答 {}B from {} (Butler 在线)", n, src);
|
||||
dev_ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !dev_ok { println!("[1] 发现无应答, 终止"); return; }
|
||||
|
||||
let mut b12 = rng.next();
|
||||
let mut claim_ok = false;
|
||||
|
||||
// claimonly/quick: 最小认领 (announce144 → 47B → AECH) — 实测成功配方, 无绑定/宣告/前奏
|
||||
let hd_mode = std::env::var("HOSTDEMO_MODE").unwrap_or_default();
|
||||
if hd_mode.starts_with("claim") || hd_mode == "quick" {
|
||||
let o2: Vec<u8> = bind_ip.split('.').map(|x| x.parse().unwrap()).collect();
|
||||
let full = hd_mode == "claimfull";
|
||||
if full {
|
||||
// sub=07 绑定帧 ×4 (ffff→0000→ffff→0000, 0.32s 间隔)
|
||||
for flag in [[0xff_u8, 0xff_u8], [0x00, 0x00], [0xff, 0xff], [0x00, 0x00]] {
|
||||
let mut b = build_bind52(flag);
|
||||
b[0x1A..0x1E].copy_from_slice(&[o2[3], o2[2], o2[1], o2[0]]);
|
||||
sock.send_to(&b, &dst).unwrap();
|
||||
std::thread::sleep(Duration::from_millis(320));
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(1500));
|
||||
}
|
||||
let mut ident = IDENT102.to_vec();
|
||||
ident[0x1C..0x20].copy_from_slice(&[o2[3], o2[2], o2[1], o2[0]]);
|
||||
// runs1-3 成功配方: 仅 announce144 (102B 身份帧) 单播一次, 无绑定/EUCE/宣告
|
||||
let ident144 = if full { ident } else { load_template("announce144") };
|
||||
sock.send_to(&ident144, &dst).unwrap();
|
||||
let mut reg = false;
|
||||
let w = Instant::now();
|
||||
while w.elapsed() < Duration::from_millis(400) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() == target && n == 47 { reg = true; break; }
|
||||
}
|
||||
}
|
||||
let nonce = rng.next();
|
||||
let b12c = rng.next();
|
||||
let mut ch = load_template("claim_h112");
|
||||
ch[0x1A..0x1E].copy_from_slice(&[o2[3], o2[2], o2[1], o2[0]]);
|
||||
ch[0x88..0x8C].copy_from_slice(&nonce.to_le_bytes());
|
||||
ch[0x8C..0x90].copy_from_slice(&b12c.to_le_bytes());
|
||||
sock.send_to(&ch, &dst).unwrap();
|
||||
let w2 = Instant::now();
|
||||
while w2.elapsed() < Duration::from_millis(700) {
|
||||
if let Ok((n, _)) = sock.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
if n > 0xC4 && b.windows(4).any(|w| w == b"AERE") {
|
||||
let resp = g32(b, 0xC0);
|
||||
println!("CLAIM_PROBE reg89={} AERE resp=0x{:08x} G=0x{:08x} {}", reg, resp, agios_g(nonce),
|
||||
if resp == agios_g(nonce) { "PASS" } else { "FAIL" });
|
||||
if resp == agios_g(nonce) {
|
||||
// quick 模式: 认领成功后直通 RDM+DMX 阶段 (跳过绑定/宣告/前奏 — 实测有毒)
|
||||
if hd_mode == "quick" {
|
||||
b12 = b12c;
|
||||
claim_ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !claim_ok { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if !claim_ok {
|
||||
println!("CLAIM_PROBE reg89={} AERE=none FAIL", reg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// takeover: 跳过认领, 用 SL 上次会话的 B12 续发 DMX+心跳, 探会话是否可直接延续
|
||||
if std::env::var("HOSTDEMO_MODE").unwrap_or_default() == "takeover" {
|
||||
let b12_old: u32 = 0x4366937a; // sym_startup @101.278 AECH 的 B12@0x8C (SL 22:52 会话)
|
||||
println!("[P] takeover: B12=0x{:08x} 续发 15s DMX(ch101 ramp)+心跳+RDM GET, 侦听回包", b12_old);
|
||||
let tpl = load_template("dmx_a");
|
||||
let hb = load_template("heartbeat");
|
||||
let known: [u8; 6] = [0x01, 0x07, 0x34, 0x67, 0x01, 0x25]; // NFS1K2D
|
||||
let start = Instant::now();
|
||||
let mut seq: u32 = 5000; // 帧序号跳开, 避免与 SL 旧计数冲突
|
||||
let mut last_hb = Instant::now() - Duration::from_secs(10);
|
||||
let mut rdm_done = false;
|
||||
while start.elapsed() < Duration::from_secs(15) {
|
||||
if last_hb.elapsed() >= Duration::from_secs(6) {
|
||||
let _ = sock.send_to(&hb, &dst);
|
||||
last_hb = Instant::now();
|
||||
println!(" [P] 心跳已发");
|
||||
}
|
||||
if !rdm_done && start.elapsed() > Duration::from_secs(5) {
|
||||
rdm_done = true;
|
||||
let f = build_rdm_frame(&known, 1, 1, 0x20, 0x0060, &[]); // GET DEVICE_INFO
|
||||
sock.send_to(&f, &dst).unwrap();
|
||||
println!(" [P] RDM GET DEVICE_INFO 已发");
|
||||
}
|
||||
let t = start.elapsed().as_secs_f32();
|
||||
let mut p1 = [0u8; 512];
|
||||
p1[100] = (((t * 0.8).sin() * 0.5 + 0.5) * 255.0) as u8;
|
||||
let p2 = [0u8; 512];
|
||||
let r1 = rng.next();
|
||||
let r2 = rng.next();
|
||||
let mut f = tpl.clone();
|
||||
f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes());
|
||||
f[0x2E] = 2; f[0x2F] = 0;
|
||||
f[0x22..0x26].copy_from_slice(&r1.to_le_bytes());
|
||||
f[0x26..0x2A].copy_from_slice(&r2.to_le_bytes());
|
||||
f[0x3C..0x23C].copy_from_slice(&agios_dmx_encrypt(&p1, r1, b12_old));
|
||||
f[0x23C..0x43C].copy_from_slice(&agios_dmx_encrypt(&p2, r2, b12_old));
|
||||
let _ = sock.send_to(&f, &dst);
|
||||
seq += 1;
|
||||
while let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
println!(" [P] ← len={} src={} head={}", n, src, hexs(&b[..std::cmp::min(16, n)]));
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(33));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- 2) 宣告: 真主机用 644B type=1 广播宣告 (announce 模板), 主机 IP@0x41 LE;
|
||||
// 再发 102B type=2 身份帧 (cold2 实测: 紧邻 AECH 之前), IP@0x1C LE ----
|
||||
let mode = std::env::var("HOSTDEMO_MODE").unwrap_or_default();
|
||||
let o: Vec<u8> = bind_ip.split('.').map(|x| x.parse().unwrap()).collect();
|
||||
let bcast = format!("{}.255:31772", target.rsplitn(2, '.').nth(1).unwrap_or("192.168.123"));
|
||||
if mode != "skip2" && mode != "noclaim" {
|
||||
// 冷启动广播前奏 (cold2=38s, SymphoLight 实测 2.5min 成功; 10s 短前奏被拒!)
|
||||
// ★门控假说: Butler 须收到 ~13+ 轮 discovery/announce 才武装认领应答
|
||||
let pre_secs: u64 = std::env::var("HOSTDEMO_PRE").ok().and_then(|s| s.parse().ok()).unwrap_or(45);
|
||||
// ★宣告帧用 announce_live 模板 + 新鲜时间戳/随机会话 ID (陈旧宣告 = 僵尸主机, 认领被无视)
|
||||
let euce = load_template("euce"); // 540B, 无主机相关字段
|
||||
let pre = Instant::now();
|
||||
let mut queries_dbg = 0;
|
||||
let mut last = Instant::now() - Duration::from_secs(10);
|
||||
let mut last_euce = Instant::now() - Duration::from_secs(10);
|
||||
while pre.elapsed() < Duration::from_secs(pre_secs) {
|
||||
if last.elapsed() >= Duration::from_secs(2) {
|
||||
let _ = sock.send_to(&disc, "255.255.255.255:31772");
|
||||
let mut ann = build_fresh_announce(&mut rng);
|
||||
ann[0x41..0x45].copy_from_slice(&[o[3], o[2], o[1], o[0]]); // 主机IP逆序字节@0x41
|
||||
if queries_dbg == 0 {
|
||||
queries_dbg = 1;
|
||||
let live = load_template("announce_live");
|
||||
println!(" [dbg] ann timestamp区: ours={}", hexs(&ann[0x230..0x245]));
|
||||
println!(" [dbg] ann timestamp区: live={}", hexs(&live[0x230..0x245]));
|
||||
println!(" [dbg] ann sessid区: ours={}", hexs(&ann[0x219..0x226]));
|
||||
println!(" [dbg] ann sessid区: live={}", hexs(&live[0x219..0x226]));
|
||||
let diffs: Vec<usize> = (0..ann.len()).filter(|&i| ann[i] != live[i]).collect();
|
||||
println!(" [dbg] ann 与 live 模板差异偏移 ({}处): {:?}", diffs.len(), diffs);
|
||||
}
|
||||
let _ = sock.send_to(&ann, &bcast);
|
||||
last = Instant::now();
|
||||
}
|
||||
if last_euce.elapsed() >= Duration::from_secs(6) {
|
||||
// ★EUCE 每 6s (SymphoLight 启动期实测节奏, 30s 节奏认领被拒)
|
||||
let _ = sock.send_to(&euce, "255.255.255.255:31772");
|
||||
last_euce = Instant::now();
|
||||
}
|
||||
// 排空 (217B 发现应答 / 47B 注册等)
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
println!(" [dbg] 前奏期收帧 len={} src={}", n, src);
|
||||
}
|
||||
}
|
||||
let mut ident = IDENT102.to_vec();
|
||||
ident[0x1C..0x20].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
||||
// ★sub=07 绑定帧 ×4: ffff→0000→ffff→0000 (间隔 ~0.32s, sym_startup @98.8-99.5 实测)
|
||||
// 1.8s 后发 ident
|
||||
for flag in [[0xff_u8, 0xff_u8], [0x00, 0x00], [0xff, 0xff], [0x00, 0x00]] {
|
||||
let mut b = build_bind52(flag);
|
||||
b[0x1A..0x1E].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
||||
sock.send_to(&b, &dst).unwrap();
|
||||
std::thread::sleep(Duration::from_millis(320));
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(1800));
|
||||
let mut ident = IDENT102.to_vec();
|
||||
ident[0x1C..0x20].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
||||
sock.send_to(&ident, &dst).unwrap();
|
||||
println!("[2] 冷启动前奏完成 ({}s 广播 + 绑定帧×4 + 身份帧)", pre_secs);
|
||||
}
|
||||
let mut reg_ok = false;
|
||||
for _ in 0..10 {
|
||||
if let Ok((n, _)) = sock.recv_from(&mut buf) {
|
||||
if n == 89 { reg_ok = true; break; }
|
||||
}
|
||||
}
|
||||
println!("[2] 宣告 → 注册应答: {}", if reg_ok { "OK (89B)" } else { "未收到 (继续)" });
|
||||
|
||||
// ---- 3) AECH 认领 ×3 (quick 模式已在上面认领成功; noclaim 跳过) ----
|
||||
if !claim_ok {
|
||||
b12 = rng.next();
|
||||
if mode == "noclaim" {
|
||||
println!("[3] noclaim 模式: 跳过 AECH, 直接以 RDM GET 探测连接态");
|
||||
claim_ok = true;
|
||||
}
|
||||
for round in 0..3 {
|
||||
if mode == "noclaim" { break; }
|
||||
let nonce = rng.next();
|
||||
let mut ch = load_template("claim_h112");
|
||||
ch[0x1A..0x1E].copy_from_slice(&[o[3], o[2], o[1], o[0]]);
|
||||
ch[0x88..0x8C].copy_from_slice(&nonce.to_le_bytes());
|
||||
ch[0x8C..0x90].copy_from_slice(&b12.to_le_bytes());
|
||||
sock.send_to(&ch, &dst).unwrap();
|
||||
for _ in 0..10 {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
println!(" [dbg] AECH等待中收帧 len={} src={} magic={:?}", n, src, &b[0..4.min(n)]);
|
||||
if n > 0xC4 && b.windows(4).any(|w| w == b"AERE") {
|
||||
let resp = g32(b, 0xC0);
|
||||
let want = agios_g(nonce);
|
||||
println!("[3] AECH 第{}轮: resp=0x{:08x} G=0x{:08x} {}", round + 1, resp, want,
|
||||
if resp == want { "★PASS" } else { "FAIL" });
|
||||
if resp == want { claim_ok = true; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !claim_ok { println!("[3] 认领失败 (真机只应答源 123.113 的 AECH), 终止"); return; }
|
||||
println!(" 认领通过, B12=0x{:08x}", b12);
|
||||
|
||||
// quick 模式: 密钥广播 + 统一会话环 — DMX 持续流 (cold2 实证: RDM 代理仅在 DMX 流活跃时工作)
|
||||
if hd_mode == "quick" {
|
||||
println!("[K] 1947 许可密钥广播 4s...");
|
||||
send_key_burst(&sock);
|
||||
session_loop(&sock, &dst, target, &mut rng, b12, secs);
|
||||
return;
|
||||
}
|
||||
|
||||
// noclaim 连接态探测: 直接 GET DEVICE_INFO 已知 3 灯具, 验证 Butler 是否仍视我们为主机
|
||||
if mode == "noclaim" {
|
||||
let known: [[u8; 6]; 3] = [
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x25], // NFS1K2D DMX101
|
||||
[0x01, 0x07, 0x34, 0x88, 0x09, 0x00], // NFM680S DMX112
|
||||
[0x08, 0x99, 0x50, 0x00, 0x26, 0xc9], // HMKJ DMX1-32
|
||||
];
|
||||
let mut tn0: u8 = rng.byte();
|
||||
for (i, uid) in known.iter().enumerate() {
|
||||
tn0 = tn0.wrapping_add(1);
|
||||
let f = build_rdm_frame(uid, tn0, (i + 1) as u16, 0x20, 0x0060, &[]);
|
||||
sock.send_to(&f, &dst).unwrap();
|
||||
let w = Instant::now();
|
||||
let mut got = false;
|
||||
while w.elapsed() < Duration::from_millis(500) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() != target { continue; }
|
||||
let b = &buf[..n];
|
||||
println!(" [dbg] 收帧 len={} 0x18={:02x}{:02x}", n, b.get(0x19).copied().unwrap_or(0), b.get(0x18).copied().unwrap_or(0));
|
||||
if let Some((false, d)) = rdm_region(b) {
|
||||
if d.len() >= 0x18 + 19 && d[0] == 0xCC && d[0x15..0x17] == [0x00, 0x60] {
|
||||
let a = u16::from_be_bytes([d[0x18 + 14], d[0x18 + 15]]);
|
||||
println!(" ★ 连接态确认! UID={} DMX地址={}", uid_s(uid), a);
|
||||
got = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !got { println!(" UID={} 无应答", uid_s(uid)); }
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- 4) RDM 发现 ----
|
||||
let mut tn: u8 = rng.byte();
|
||||
let mut qseq: u16 = 1;
|
||||
let mut fixtures: Vec<[u8; 6]> = Vec::new();
|
||||
{
|
||||
// 4a) 先直查已知 UID 验证 RDM 代理通道 (灯具可能仍处 SL 会话留下的 MUTE 态)
|
||||
let known: [[u8; 6]; 3] = [
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x25], // NFS1K2D DMX101
|
||||
[0x01, 0x07, 0x34, 0x88, 0x09, 0x00], // NFM680S DMX112
|
||||
[0x08, 0x99, 0x50, 0x00, 0x26, 0xc9], // HMKJ DMX1-32
|
||||
];
|
||||
println!("[4a] 直查已知 UID (GET DEVICE_INFO):");
|
||||
for uid in &known {
|
||||
tn = tn.wrapping_add(1);
|
||||
let f = build_rdm_frame(uid, tn, qseq, 0x20, 0x0060, &[]);
|
||||
qseq += 1;
|
||||
sock.send_to(&f, &dst).unwrap();
|
||||
let w = Instant::now();
|
||||
let mut ok = false;
|
||||
while w.elapsed() < Duration::from_millis(400) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() != target { continue; }
|
||||
let b = &buf[..n];
|
||||
if let Some((false, d)) = rdm_region(b) {
|
||||
if d.len() >= 0x18 + 19 && d[0] == 0xCC && d[0x15..0x17] == [0x00, 0x60] {
|
||||
let a = u16::from_be_bytes([d[0x18 + 14], d[0x18 + 15]]);
|
||||
let footprint = u16::from_be_bytes([d[0x18 + 10], d[0x18 + 11]]);
|
||||
println!(" ★ {} → DMX地址 {} / {}通道", uid_s(uid), a, footprint);
|
||||
if !fixtures.contains(uid) { fixtures.push(*uid); }
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok { println!(" {} → 无应答", uid_s(uid)); }
|
||||
}
|
||||
// 先广播 DISC_UN_MUTE (PID=0003): 稳态下主机已把全部灯具 MUTE,
|
||||
// 被 MUTE 的灯具不应答 UNIQUE_BRANCH → 须先解除静音才能重新发现
|
||||
tn = tn.wrapping_add(1);
|
||||
let um = build_rdm_frame(&BCAST_UID, tn, qseq, 0x10, 0x0003, &[]);
|
||||
qseq += 1;
|
||||
sock.send_to(&um, &dst).unwrap();
|
||||
std::thread::sleep(Duration::from_millis(80));
|
||||
println!("[4] 已广播 DISC_UN_MUTE, 开始 UNIQUE_BRANCH 二分遍历");
|
||||
let mut stack: Vec<(u64, u64)> = vec![(0, 0xFFFF_FFFF_FFFF)];
|
||||
let mut queries = 0;
|
||||
let walk_start = Instant::now();
|
||||
while let Some((lo, hi)) = stack.pop() {
|
||||
if queries >= 400 || walk_start.elapsed() > Duration::from_secs(120) { println!(" (发现遍历到达上限)"); break; }
|
||||
let mut pd = Vec::with_capacity(12);
|
||||
pd.extend_from_slice(&u64_uid(lo));
|
||||
pd.extend_from_slice(&u64_uid(hi));
|
||||
// 真机实测: 首个应答延迟可达 ~1.4s, 真主机对同一分支连发多次 → 每分支最多 4 次尝试 × 250ms 窗口
|
||||
let mut found: Vec<[u8; 6]> = Vec::new();
|
||||
let mut collision = false;
|
||||
for attempt in 0..4 {
|
||||
queries += 1;
|
||||
tn = tn.wrapping_add(1);
|
||||
let f = build_rdm_frame(&BCAST_UID, tn, qseq, 0x10, 0x0001, &pd); // DISC_UNIQUE_BRANCH
|
||||
qseq += 1;
|
||||
if queries == 1 {
|
||||
let t = load_template("rdm_disc_req");
|
||||
let diffs: Vec<usize> = (0x2A..0x62).filter(|&i| f[i] != t[i]).collect();
|
||||
println!(" [dbg] 首帧与模板差异偏移: {:?} (应仅 qseq/TN/srcUID末字节/PD/checksum)", diffs);
|
||||
}
|
||||
sock.send_to(&f, &dst).unwrap();
|
||||
let w = Instant::now();
|
||||
while w.elapsed() < Duration::from_millis(250) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
if src.ip().to_string() != target { continue; }
|
||||
if let Some((is_pulse, d)) = rdm_region(b) {
|
||||
if is_pulse {
|
||||
match decode_pulse(d) {
|
||||
Some(uid) => { if !found.contains(&uid) { found.push(uid); } }
|
||||
None => collision = true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found.is_empty() || collision { break; }
|
||||
let _ = attempt;
|
||||
}
|
||||
if collision {
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
stack.push((mid + 1, hi));
|
||||
stack.push((lo, mid));
|
||||
} else if found.len() == 1 {
|
||||
let uid = found[0];
|
||||
if found_valid(&uid) {
|
||||
println!(" [发现] UID={} (深度 {} 次查询后)", uid_s(&uid), queries);
|
||||
// DISC_MUTE (PID=0002) → 70B 参数 ACK
|
||||
tn = tn.wrapping_add(1);
|
||||
let f2 = build_rdm_frame(&uid, tn, qseq, 0x10, 0x0002, &[]);
|
||||
qseq += 1;
|
||||
sock.send_to(&f2, &dst).unwrap();
|
||||
let w2 = Instant::now();
|
||||
let mut acked = false;
|
||||
while w2.elapsed() < Duration::from_millis(400) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() != target { continue; }
|
||||
let b = &buf[..n];
|
||||
if let Some((false, d)) = rdm_region(b) {
|
||||
if d.len() >= 0x18 && d[0] == 0xCC && d[0x14] == 0x11 && d[0x15..0x17] == [0x00, 0x02] {
|
||||
acked = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!(" MUTE(0002) ACK: {}", if acked { "OK" } else { "未收到" });
|
||||
fixtures.push(uid);
|
||||
}
|
||||
} else if found.len() > 1 {
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
stack.push((mid + 1, hi));
|
||||
stack.push((lo, mid));
|
||||
}
|
||||
// found==0: 空分支
|
||||
}
|
||||
println!("[4] RDM 发现完成: {} 台灯具, {} 次查询, {:.1}s", fixtures.len(), queries, walk_start.elapsed().as_secs_f32());
|
||||
}
|
||||
|
||||
// ---- 4b) 读参数: DEVICE_INFO / DMX_START_ADDRESS / DEVICE_LABEL ----
|
||||
let mut addr_list: Vec<u16> = Vec::new();
|
||||
for uid in &fixtures {
|
||||
tn = tn.wrapping_add(1);
|
||||
let f = build_rdm_frame(uid, tn, qseq, 0x20, 0x0060, &[]); // GET DEVICE_INFO
|
||||
qseq += 1;
|
||||
sock.send_to(&f, &dst).unwrap();
|
||||
let mut dmx_addr: Option<u16> = None;
|
||||
let mut footprint = 0u16;
|
||||
let mut sensors = 0u8;
|
||||
let w = Instant::now();
|
||||
while w.elapsed() < Duration::from_millis(400) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() != target { continue; }
|
||||
let b = &buf[..n];
|
||||
if let Some((false, d)) = rdm_region(b) {
|
||||
// d: CC@0 ... CC字段@0x14, PID@0x15-16, PDL@0x17, PD@0x18
|
||||
if d.len() >= 0x18 + 19 && d[0] == 0xCC && d[0x14] == 0x21 && d[0x15..0x17] == [0x00, 0x60] {
|
||||
let pd = &d[0x18..0x18 + 19];
|
||||
footprint = u16::from_be_bytes([pd[10], pd[11]]);
|
||||
dmx_addr = Some(u16::from_be_bytes([pd[14], pd[15]]));
|
||||
sensors = pd[18];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// GET DMX_START_ADDRESS 复核
|
||||
tn = tn.wrapping_add(1);
|
||||
let f2 = build_rdm_frame(uid, tn, qseq, 0x20, 0x00F0, &[]);
|
||||
qseq += 1;
|
||||
sock.send_to(&f2, &dst).unwrap();
|
||||
let w2 = Instant::now();
|
||||
while w2.elapsed() < Duration::from_millis(400) {
|
||||
if let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() != target { continue; }
|
||||
let b = &buf[..n];
|
||||
if let Some((false, d)) = rdm_region(b) {
|
||||
if d.len() >= 0x18 + 2 && d[0] == 0xCC && d[0x14] == 0x21 && d[0x15..0x17] == [0x00, 0xF0] {
|
||||
let a = u16::from_be_bytes([d[0x18], d[0x19]]);
|
||||
if dmx_addr.is_none() { dmx_addr = Some(a); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match dmx_addr {
|
||||
Some(a) => {
|
||||
println!(" 灯具 {} → DMX 地址 {} (占用 {} 通道, {} 传感器)", uid_s(uid), a, footprint, sensors);
|
||||
addr_list.push(a);
|
||||
}
|
||||
None => println!(" 灯具 {} → 参数读取超时", uid_s(uid)),
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 5) DMX 控制: 呼吸图案 (按发现的灯具地址; 无发现则用已知 101/112/1) ----
|
||||
if secs == 0 { println!("[5] secs=0, 跳过 DMX 控制"); return; }
|
||||
if addr_list.is_empty() { addr_list = vec![101, 112, 1]; println!("[5] 无发现结果, 用已知地址 101/112/1"); }
|
||||
println!("[5] DMX 控制 {}s @30fps: 呼吸图案 @地址 {:?} (zPprv0TXRRW 加密, B12=0x{:08x})", secs, addr_list, b12);
|
||||
println!(" ⚠ SymphoLight 若同时运行会插黑场帧造成闪烁; 关其输出可得纯净效果");
|
||||
let tpl = load_template("dmx_a");
|
||||
let hb = load_template("heartbeat"); // 36B type=02 sub=03 心跳 (会话保活, 每 ~6s 单播)
|
||||
let start = Instant::now();
|
||||
let mut seq: u32 = 1;
|
||||
let mut sent = 0u64;
|
||||
let mut last_hb = Instant::now();
|
||||
while start.elapsed() < Duration::from_secs(secs) {
|
||||
if last_hb.elapsed() >= Duration::from_secs(6) {
|
||||
let _ = sock.send_to(&hb, &dst);
|
||||
last_hb = Instant::now();
|
||||
}
|
||||
let t = start.elapsed().as_secs_f32();
|
||||
let mut p1 = [0u8; 512];
|
||||
let mut p2 = [0u8; 512];
|
||||
for (k, &a) in addr_list.iter().enumerate() {
|
||||
let phase = t * 1.2 + k as f32 * 2.1;
|
||||
let v = ((phase.sin() * 0.5 + 0.5) * 255.0) as u8;
|
||||
let ch = a.max(1) - 1; // 0-based
|
||||
if ch < 512 { p1[ch as usize] = v; } else if ch < 1024 { p2[(ch - 512) as usize] = v; }
|
||||
}
|
||||
let r1 = rng.next();
|
||||
let r2 = rng.next();
|
||||
let mut f = tpl.clone();
|
||||
f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes());
|
||||
f[0x2E] = 2; f[0x2F] = 0; // 节点序号 (真机 .1 = 2)
|
||||
f[0x22..0x26].copy_from_slice(&r1.to_le_bytes());
|
||||
f[0x26..0x2A].copy_from_slice(&r2.to_le_bytes());
|
||||
f[0x3C..0x23C].copy_from_slice(&agios_dmx_encrypt(&p1, r1, b12));
|
||||
f[0x23C..0x43C].copy_from_slice(&agios_dmx_encrypt(&p2, r2, b12));
|
||||
if sock.send_to(&f, &dst).is_ok() { sent += 1; }
|
||||
seq += 1;
|
||||
std::thread::sleep(Duration::from_millis(33));
|
||||
}
|
||||
println!("[5] 完成: 发送 {} 帧", sent);
|
||||
}
|
||||
|
||||
fn found_valid(uid: &[u8; 6]) -> bool {
|
||||
uid.iter().any(|&b| b != 0) && uid.iter().any(|&b| b != 0xFF)
|
||||
}
|
||||
|
||||
/// 构造"新鲜"宣告帧: announce_live 模板 (今日 SL 实抓) + 每发随机会话 ID@0x221
|
||||
/// + 当前本地时间戳@0x239 (年u16LE 月 日 时 分 秒 毫秒u16LE 时区) — Butler 按
|
||||
/// 宣告时间戳判主机新鲜度, 陈旧宣告(旧抓包模板)会被当僵尸主机无视 ident/AECH
|
||||
fn build_fresh_announce(rng: &mut Rng) -> Vec<u8> {
|
||||
let mut a = load_template("announce_live"); // 644B
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
// 本地时间分解 (UTC+8, 与实测 tz=02... 用本地时区偏移)
|
||||
let secs = now + tz_offset_secs();
|
||||
let days = secs / 86400;
|
||||
let rem = secs % 86400;
|
||||
let (year, month, day) = civil_from_days(days as i64);
|
||||
let hour = (rem / 3600) as u8;
|
||||
let min = ((rem % 3600) / 60) as u8;
|
||||
let sec = (rem % 60) as u8;
|
||||
let ms = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_millis();
|
||||
// 时间戳字段实际位于 0x235: 年u16LE@0x235 月@0x237 日@0x238 时@0x239 分@0x23A
|
||||
// 秒@0x23B 毫秒u16LE@0x23C 时区@0x23E (实测 2026-09-10 22:52:50.992 tz=02)
|
||||
a[0x235] = (year & 0xFF) as u8;
|
||||
a[0x236] = (year >> 8) as u8;
|
||||
a[0x237] = month;
|
||||
a[0x238] = day;
|
||||
a[0x239] = hour;
|
||||
a[0x23A] = min;
|
||||
a[0x23B] = sec;
|
||||
let msle = (ms as u16).to_le_bytes();
|
||||
a[0x23C] = msle[0];
|
||||
a[0x23D] = msle[1];
|
||||
a[0x23E] = 0x02; // 时区字段 (实测值)
|
||||
// 4B 随机会话 ID @0x21D (每发宣告都变; 0x221 的 0c 恒定)
|
||||
let id = rng.next();
|
||||
a[0x21D..0x221].copy_from_slice(&id.to_le_bytes());
|
||||
a
|
||||
}
|
||||
|
||||
fn tz_offset_secs() -> u64 {
|
||||
// 本地时区偏移 (秒): 用 UTC 与本地时间差
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
|
||||
let utc_days = (now.div_euclid(86400) + 4) % 7; // 0=周四
|
||||
let _ = utc_days;
|
||||
// 简化: 读环境 TZ 不可靠, 直接用 Windows 时区 API? 用 chrono 不行(无依赖).
|
||||
// 实测帧 tz 字节恒 02, 时间字段为本地时间 (UTC+8 中 hour=22 对应当地 22 点)
|
||||
// → 这里返回 +8h; 若部署在其它时区需调整
|
||||
8 * 3600
|
||||
}
|
||||
|
||||
fn civil_from_days(z: i64) -> (u16, u8, u8) {
|
||||
// Howard Hinnant 算法: days since 1970-01-01 → (y, m, d)
|
||||
let z = z + 719468;
|
||||
let era = if z >= 0 { z } else { z - 146096 } / 146097;
|
||||
let doe = (z - era * 146097) as u64;
|
||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
let y = yoe as i64 + era * 400;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = (doy - (153 * mp + 2) / 5 + 1) as u8;
|
||||
let m = if mp < 10 { mp + 3 } else { mp - 9 } as u8;
|
||||
(if m <= 2 { (y + 1) as u16 } else { y as u16 }, m, d)
|
||||
}
|
||||
|
||||
/// ★1947 端口"密钥报文" (allports.pcap 实测): 40B ASCII 许可令牌, SL 每 ~34s 广播一轮
|
||||
/// (双目标 21 帧 @200ms) — Butler 在 1947 监听, 疑为 RDM 代理的许可开关
|
||||
const KEY40: &[u8] = b"BlZRQphAGxUbi7at8KEfmdZ7TdSB03b+qoC1RIA";
|
||||
|
||||
fn send_key_burst(sock: &UdpSocket) {
|
||||
for _ in 0..21 {
|
||||
let _ = sock.send_to(KEY40, "192.168.123.255:1947");
|
||||
let _ = sock.send_to(KEY40, "255.255.255.255:1947");
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
}
|
||||
}
|
||||
|
||||
/// 统一会话环 (quick): DMX 30fps 持续 + RDM 穿插
|
||||
/// 阶段: 0-2s 黑场(设备预热) → UN_MUTE + UNIQUE_BRANCH 二分发现 → MUTE 已发现灯具
|
||||
/// → GET DEVICE_INFO/DMX 地址 → 呼吸图案控制 (未发现则用已知地址)
|
||||
fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u32, secs: u64) {
|
||||
let tpl = load_template("dmx_a");
|
||||
let hb = load_template("heartbeat");
|
||||
let known: [[u8; 6]; 3] = [
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x25], // NFS1K2D DMX101
|
||||
[0x01, 0x07, 0x34, 0x88, 0x09, 0x00], // NFM680S DMX112
|
||||
[0x08, 0x99, 0x50, 0x00, 0x26, 0xc9], // HMKJ DMX1-32
|
||||
];
|
||||
let mut buf = [0u8; 4096];
|
||||
let mut fixtures: Vec<[u8; 6]> = Vec::new();
|
||||
let mut addr_list: Vec<u16> = Vec::new();
|
||||
let mut tn: u8 = rng.byte();
|
||||
let mut qseq: u16 = 1;
|
||||
let mut seq: u32 = 1;
|
||||
// 发现任务队列: (dest, cc, pid, pd) — None = 分支查询由 walk 状态机生成
|
||||
let mut stack: Vec<(u64, u64)> = vec![(0, 0xFFFF_FFFF_FFFF)];
|
||||
let mut get_queue: Vec<[u8; 6]> = Vec::new();
|
||||
let mut muted: Vec<[u8; 6]> = Vec::new();
|
||||
let start = Instant::now();
|
||||
let mut last_hb = Instant::now();
|
||||
let mut last_key_t: f32 = -100.0;
|
||||
let mut last_rdm = Instant::now() - Duration::from_secs(1);
|
||||
let mut unmuting = false;
|
||||
let mut discovering = true;
|
||||
let mut collecting = false;
|
||||
let mut collect_until = Instant::now();
|
||||
let mut found: Vec<[u8; 6]> = Vec::new();
|
||||
let mut collision = false;
|
||||
let mut current_branch: Option<(u64, u64)> = None;
|
||||
let mut sent = 0u64;
|
||||
|
||||
println!("[S] 统一会话环启动: DMX 30fps + RDM 穿插 ({}s)", secs);
|
||||
while start.elapsed() < Duration::from_secs(secs) {
|
||||
let t = start.elapsed().as_secs_f32();
|
||||
|
||||
// --- 心跳每 6s + 密钥每 30s + ★EUCE 每 6s (SL 全程 6s 节奏, 许可信标) ---
|
||||
if last_hb.elapsed() >= Duration::from_secs(6) {
|
||||
let _ = sock.send_to(&hb, dst);
|
||||
let euce = load_template("euce");
|
||||
let _ = sock.send_to(&euce, "255.255.255.255:31772");
|
||||
last_hb = Instant::now();
|
||||
}
|
||||
if t - last_key_t >= 30.0 {
|
||||
let _ = sock.send_to(KEY40, "192.168.123.255:1947");
|
||||
let _ = sock.send_to(KEY40, "255.255.255.255:1947");
|
||||
last_key_t = t;
|
||||
}
|
||||
|
||||
// --- RDM 任务 (每 ~100ms 一发; ★SL 实测: 认领后 27.5s 纯 DMX, RDM 过早发可能重置武装计时) ---
|
||||
if discovering && t > 30.0 && last_rdm.elapsed() >= Duration::from_millis(100) {
|
||||
last_rdm = Instant::now();
|
||||
if collecting && Instant::now() >= collect_until {
|
||||
// 本分支收集结束 → 判定
|
||||
collecting = false;
|
||||
if let Some((lo, hi)) = current_branch.take() {
|
||||
if collision {
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
stack.push((mid + 1, hi));
|
||||
stack.push((lo, mid));
|
||||
println!(" [S] 分支冲突 → 二分");
|
||||
} else if found.len() == 1 && found_valid(&found[0]) {
|
||||
let uid = found[0];
|
||||
println!(" [S] ★发现灯具 {}", uid_s(&uid));
|
||||
if !fixtures.contains(&uid) { fixtures.push(uid); }
|
||||
get_queue.push(uid);
|
||||
// DISC_MUTE 该灯具
|
||||
tn = tn.wrapping_add(1);
|
||||
let f = build_rdm_frame(&uid, tn, qseq, 0x10, 0x0002, &[]);
|
||||
qseq += 1;
|
||||
let _ = sock.send_to(&{ let mut f = f; f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes()); seq += 1; f }, dst);
|
||||
} else if found.len() > 1 {
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
stack.push((mid + 1, hi));
|
||||
stack.push((lo, mid));
|
||||
}
|
||||
found.clear();
|
||||
collision = false;
|
||||
}
|
||||
}
|
||||
if collecting {
|
||||
// 收集窗口内每 150ms 重发同分支查询 (真主机连发节奏)
|
||||
if last_rdm.elapsed() >= Duration::from_millis(150) {
|
||||
last_rdm = Instant::now();
|
||||
if let Some((lo, hi)) = current_branch {
|
||||
let mut pd = Vec::with_capacity(12);
|
||||
pd.extend_from_slice(&u64_uid(lo));
|
||||
pd.extend_from_slice(&u64_uid(hi));
|
||||
tn = tn.wrapping_add(1);
|
||||
let mut f = build_rdm_frame(&BCAST_UID, tn, qseq, 0x10, 0x0001, &pd);
|
||||
qseq += 1;
|
||||
f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes());
|
||||
seq += 1;
|
||||
let _ = sock.send_to(&f, dst);
|
||||
let mut f2 = f.clone();
|
||||
f2[0x30] = 0x01;
|
||||
let _ = sock.send_to(&f2, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !collecting {
|
||||
if !unmuting {
|
||||
// DISC_UN_MUTE 广播
|
||||
tn = tn.wrapping_add(1);
|
||||
let f = build_rdm_frame(&BCAST_UID, tn, qseq, 0x10, 0x0003, &[]);
|
||||
qseq += 1;
|
||||
let _ = sock.send_to(&{ let mut f = f; f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes()); seq += 1; f }, dst);
|
||||
unmuting = true;
|
||||
println!("[S] DISC_UN_MUTE 已发");
|
||||
} else if let Some((lo, hi)) = stack.pop() {
|
||||
let mut pd = Vec::with_capacity(12);
|
||||
pd.extend_from_slice(&u64_uid(lo));
|
||||
pd.extend_from_slice(&u64_uid(hi));
|
||||
tn = tn.wrapping_add(1);
|
||||
let mut f = build_rdm_frame(&BCAST_UID, tn, qseq, 0x10, 0x0001, &pd);
|
||||
qseq += 1;
|
||||
// SL 实测: RDM 请求成对发送 (flag=00 + flag=01 副本, 背靠背 26μs)
|
||||
f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes());
|
||||
seq += 1;
|
||||
let _ = sock.send_to(&f, dst);
|
||||
let mut f2 = f.clone();
|
||||
f2[0x30] = 0x01;
|
||||
let _ = sock.send_to(&f2, dst);
|
||||
current_branch = Some((lo, hi));
|
||||
collecting = true;
|
||||
collect_until = Instant::now() + Duration::from_millis(500);
|
||||
} else {
|
||||
if fixtures.is_empty() && t < 90.0 {
|
||||
// 未发现 → 重新压栈全范围, 循环重试 (SL 实测: 认领后 ~27.5s RDM 才开始被代理!)
|
||||
stack.push((0, 0xFFFF_FFFF_FFFF));
|
||||
} else {
|
||||
discovering = false;
|
||||
println!("[S] 发现完成: {} 台灯具", fixtures.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 收包 (非阻塞): 脉冲/参数应答 ---
|
||||
while let Ok((n, src)) = sock.recv_from(&mut buf) {
|
||||
if src.ip().to_string() != target { continue; }
|
||||
let b = &buf[..n];
|
||||
if let Some((is_pulse, d)) = rdm_region(b) {
|
||||
if is_pulse && collecting {
|
||||
match decode_pulse(d) {
|
||||
Some(uid) => { if !found.contains(&uid) { found.push(uid); } }
|
||||
None => collision = true,
|
||||
}
|
||||
} else if !is_pulse && d.len() >= 0x18 + 19 && d[0] == 0xCC && d[0x15..0x17] == [0x00, 0x60] {
|
||||
let addr = u16::from_be_bytes([d[0x18 + 14], d[0x18 + 15]]);
|
||||
let footprint = u16::from_be_bytes([d[0x18 + 10], d[0x18 + 11]]);
|
||||
println!(" [S] ★{} → DMX地址 {} / {}通道", uid_s(&[d[9], d[10], d[11], d[12], d[13], d[14]]), addr, footprint);
|
||||
addr_list.push(addr);
|
||||
} else if !is_pulse && d.len() >= 0x18 + 2 && d[0] == 0xCC && d[0x15..0x17] == [0x00, 0xF0] {
|
||||
let addr = u16::from_be_bytes([d[0x18], d[0x19]]);
|
||||
println!(" [S] ★DMX_START_ADDRESS 应答: {}", addr);
|
||||
addr_list.push(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- GET 队列 (发现后逐台读参数) ---
|
||||
if !discovering && !get_queue.is_empty() && last_rdm.elapsed() >= Duration::from_millis(120) {
|
||||
last_rdm = Instant::now();
|
||||
let uid = get_queue.remove(0);
|
||||
tn = tn.wrapping_add(1);
|
||||
let f = build_rdm_frame(&uid, tn, qseq, 0x20, 0x0060, &[]);
|
||||
qseq += 1;
|
||||
let _ = sock.send_to(&{ let mut f = f; f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes()); seq += 1; f }, dst);
|
||||
let _ = muted;
|
||||
}
|
||||
|
||||
// --- DMX 帧 @30fps ---
|
||||
let mut p1 = [0u8; 512];
|
||||
let mut p2 = [0u8; 512];
|
||||
if !addr_list.is_empty() {
|
||||
for (k, &a) in addr_list.iter().enumerate() {
|
||||
let phase = t * 1.2 + k as f32 * 2.1;
|
||||
let v = (((phase.sin() * 0.5 + 0.5) * 255.0) * (t * 0.5).min(1.0)) as u8; // 淡入
|
||||
let ch = a.max(1) - 1;
|
||||
if ch < 512 { p1[ch as usize] = v; } else if ch < 1024 { p2[(ch - 512) as usize] = v; }
|
||||
}
|
||||
}
|
||||
let r1 = rng.next();
|
||||
let r2 = rng.next();
|
||||
let mut f = tpl.clone();
|
||||
f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes());
|
||||
f[0x2E] = 2; f[0x2F] = 0;
|
||||
f[0x22..0x26].copy_from_slice(&r1.to_le_bytes());
|
||||
f[0x26..0x2A].copy_from_slice(&r2.to_le_bytes());
|
||||
f[0x3C..0x23C].copy_from_slice(&agios_dmx_encrypt(&p1, r1, b12));
|
||||
f[0x23C..0x43C].copy_from_slice(&agios_dmx_encrypt(&p2, r2, b12));
|
||||
let _ = sock.send_to(&{ let mut f = f; f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes()); seq += 1; f }, dst);
|
||||
seq += 1;
|
||||
sent += 1;
|
||||
std::thread::sleep(Duration::from_millis(33));
|
||||
}
|
||||
println!("[S] 会话环结束: DMX {} 帧, 发现灯具 {} 台, 控制地址 {:?}", sent, fixtures.len(), addr_list);
|
||||
}
|
||||
194
src/main.rs
Normal file
194
src/main.rs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// main.rs — enet-validator: e:net 协议独立验证程序 (模拟 e:cue 主机, 连接真机 Butler S2)
|
||||
mod enet;
|
||||
mod tests;
|
||||
mod dmxhash;
|
||||
mod aech;
|
||||
mod hostdemo;
|
||||
|
||||
use tests::Item;
|
||||
|
||||
fn main() {
|
||||
let arg = std::env::args().nth(1).unwrap_or_else(|| "help".into());
|
||||
let mut results: Vec<Item> = Vec::new();
|
||||
|
||||
println!("enet-validator — e:net 协议实测验证 (Butler S2 @ 192.168.123.1)");
|
||||
println!("================================================================");
|
||||
match arg.as_str() {
|
||||
"discover" => results.push(tests::t1_discover()),
|
||||
"announce" => results.push(tests::tx_observe("T2", "announce", false)),
|
||||
"heartbeat" => results.push(tests::tx_observe("T3", "heartbeat", false)),
|
||||
"euce" => results.push(tests::tx_observe("T4", "euce", false)),
|
||||
"session" => {
|
||||
let secs: u64 = std::env::args().nth(2).and_then(|a| a.parse().ok()).unwrap_or(60);
|
||||
results.extend(tests::t5_t8_session(secs));
|
||||
}
|
||||
"fallback" => results.push(tests::t9_fallback()),
|
||||
"claim" => results.push(tests::t12_claim_replay()),
|
||||
"takeover" => {
|
||||
let secs: u64 = std::env::args().nth(2).and_then(|a| a.parse().ok()).unwrap_or(15);
|
||||
results.extend(tests::t13_takeover(secs));
|
||||
}
|
||||
"holdbind" => {
|
||||
// Rust 先 bind, 等待期间由 SymphoLight 认证; 之后 Rust 发帧测试共存
|
||||
let s = enet::open_reuse("0.0.0.0:31772");
|
||||
println!("Rust 已 bind 31772 (REUSEADDR)。等待 30s (此间启动 SymphoLight 认领)…");
|
||||
std::thread::sleep(std::time::Duration::from_secs(30));
|
||||
println!("开始发送测试帧 15s…");
|
||||
results.extend(tests::t13_takeover_on(s, 15));
|
||||
}
|
||||
"device" => {
|
||||
// 用法: device [设备IP] [MAC_hex12] [秒] (默认 192.168.123.200, 本机网卡MAC)
|
||||
let ip_s = std::env::args().nth(2).unwrap_or_else(|| "192.168.123.200".into());
|
||||
let mac_s = std::env::args().nth(3).unwrap_or_else(|| "300ed559bb36".into());
|
||||
let secs: u64 = std::env::args().nth(4).and_then(|a| a.parse().ok()).unwrap_or(180);
|
||||
let relay_s = std::env::args().nth(5); // 第5参: 中继目标 ip[:port] (默31772)
|
||||
let ip: Vec<u8> = ip_s.split('.').filter_map(|x| x.parse().ok()).collect();
|
||||
let mac: Vec<u8> = (0..mac_s.len()).step_by(2).filter_map(|i| u8::from_str_radix(&mac_s[i..i + 2], 16).ok()).collect();
|
||||
let relay: Option<([u8; 4], u16)> = relay_s.and_then(|s| {
|
||||
let (ips, port) = match s.rsplit_once(':') {
|
||||
Some((a, p)) => (a.to_string(), p.parse::<u16>().unwrap_or(31772)),
|
||||
None => (s, 31772),
|
||||
};
|
||||
let o: Vec<u8> = ips.split('.').filter_map(|x| x.parse().ok()).collect();
|
||||
if o.len() == 4 { Some(([o[0], o[1], o[2], o[3]], port)) } else { None }
|
||||
});
|
||||
if ip.len() == 4 && mac.len() == 6 {
|
||||
let mut ipa = [0u8; 4]; ipa.copy_from_slice(&ip);
|
||||
let mut maca = [0u8; 6]; maca.copy_from_slice(&mac);
|
||||
results.extend(tests::device2(ipa, maca, secs, relay));
|
||||
} else {
|
||||
println!("device 参数错误");
|
||||
}
|
||||
}
|
||||
"rdm" => {
|
||||
let secs: u64 = std::env::args().nth(2).and_then(|a| a.parse().ok()).unwrap_or(30);
|
||||
results.extend(tests::t10_t11_rdm(secs));
|
||||
}
|
||||
"bridge" => {
|
||||
// bridge <listen_ip:port> <real_ip> [secs] — AECH 桥 (非123网段设备认领桥)
|
||||
let lp = std::env::args().nth(2).unwrap_or_else(|| "192.168.58.107:41772".into());
|
||||
let rip_s = std::env::args().nth(3).unwrap_or_else(|| "192.168.123.1".into());
|
||||
let secs: u64 = std::env::args().nth(4).and_then(|a| a.parse().ok()).unwrap_or(7200);
|
||||
let (lip_s, lport) = match lp.rsplit_once(':') {
|
||||
Some((a, p)) => (a.to_string(), p.parse::<u16>().unwrap_or(41772)),
|
||||
None => (lp, 41772),
|
||||
};
|
||||
let lip: Vec<u8> = lip_s.split('.').filter_map(|x| x.parse().ok()).collect();
|
||||
let rip: Vec<u8> = rip_s.split('.').filter_map(|x| x.parse().ok()).collect();
|
||||
if lip.len() == 4 && rip.len() == 4 {
|
||||
tests::bridge([lip[0], lip[1], lip[2], lip[3]], lport, [rip[0], rip[1], rip[2], rip[3]], secs);
|
||||
} else {
|
||||
println!("bridge 参数错误");
|
||||
}
|
||||
return;
|
||||
}
|
||||
"all" => {
|
||||
results.push(tests::t1_discover());
|
||||
results.push(tests::tx_observe("T2", "announce", false));
|
||||
results.push(tests::tx_observe("T3", "heartbeat", false));
|
||||
results.push(tests::tx_observe("T4", "euce", false));
|
||||
results.extend(tests::t5_t8_session(60));
|
||||
results.push(tests::t9_fallback());
|
||||
}
|
||||
"rdmprobe" => {
|
||||
let target = std::env::args().nth(2).unwrap_or_else(|| "192.168.123.1".into());
|
||||
let secs: u64 = std::env::args().nth(3).and_then(|a| a.parse().ok()).unwrap_or(10);
|
||||
results.push(tests::rdm_probe(&target, secs));
|
||||
}
|
||||
"dmxhash" => {
|
||||
// dmxhash <tsv> [主机IP] — DMX-A 帧标签算法爆破 (离线分析, 无网络)
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "dmx.tsv".into());
|
||||
let host = std::env::args().nth(3).unwrap_or_else(|| "192.168.123.113".into());
|
||||
dmxhash::run(&tsv, &host);
|
||||
return;
|
||||
}
|
||||
"evtf" => {
|
||||
// evtf <tsv> [主机IP] [设备IP] [tsv2跨包] — 形态B事件帧结构分析 (离线)
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "dmx.tsv".into());
|
||||
let host = std::env::args().nth(3).unwrap_or_else(|| "192.168.123.113".into());
|
||||
let dev = std::env::args().nth(4).unwrap_or_else(|| "192.168.123.1".into());
|
||||
let tsv2 = std::env::args().nth(5);
|
||||
dmxhash::evt(&tsv, &host, &dev, tsv2.as_deref());
|
||||
return;
|
||||
}
|
||||
"aech" => {
|
||||
// aech <tsv1> [tsv2 ...] — AECH 密钥协商密码学分析 (离线)
|
||||
let tsvs: Vec<String> = std::env::args().skip(2).collect();
|
||||
if tsvs.is_empty() {
|
||||
println!("用法: enet-validator aech <tsv1> [tsv2 ...]");
|
||||
return;
|
||||
}
|
||||
aech::run(&tsvs);
|
||||
return;
|
||||
}
|
||||
"aechprobe" => {
|
||||
// aechprobe <tsv模板> <目标IP> — 选择密文仿射性分析 (在线探测真机)
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "cold2.tsv".into());
|
||||
let target = std::env::args().nth(3).unwrap_or_else(|| "192.168.123.1".into());
|
||||
aech::probe(&tsv, &target);
|
||||
return;
|
||||
}
|
||||
"aechprobe2" => {
|
||||
// aechprobe2 <tsv模板> <目标IP> — part1 块/字节影响图谱 (在线)
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "cold2.tsv".into());
|
||||
let target = std::env::args().nth(3).unwrap_or_else(|| "192.168.123.1".into());
|
||||
aech::probe2(&tsv, &target);
|
||||
return;
|
||||
}
|
||||
"aechid" => {
|
||||
// aechid <tsv> — f(last4) 离线候选函数鉴别
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "cold2.tsv".into());
|
||||
aech::identify(&tsv);
|
||||
return;
|
||||
}
|
||||
"dmxroll" => {
|
||||
// dmxroll <tsv> — DMX 滚动值/保护字节数据驱动分析 (离线)
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "dmx.tsv".into());
|
||||
aech::dmxroll(&tsv);
|
||||
return;
|
||||
}
|
||||
"dmxdec" => {
|
||||
// dmxdec <tsv> — DMX 帧加密假说验证 (kWarv47o79k/zPprv0TXRRW 逆运算, 需 TSV 含 AECH)
|
||||
let tsv = std::env::args().nth(2).unwrap_or_else(|| "dmx.tsv".into());
|
||||
aech::dmxdec(&tsv);
|
||||
return;
|
||||
}
|
||||
"dmxsend" => {
|
||||
// dmxsend <目标IP> [秒] — 主机侧等效播放: 认领 + 加密 DMX 全链路自证
|
||||
let ip = std::env::args().nth(2).unwrap_or_else(|| "192.168.58.7".into());
|
||||
let secs: u64 = std::env::args().nth(3).and_then(|s| s.parse().ok()).unwrap_or(30);
|
||||
aech::dmxsend(&ip, secs);
|
||||
return;
|
||||
}
|
||||
"hostdemo" => {
|
||||
// hostdemo [目标IP] [秒] — 主机侧等效示例: 认领真机 + RDM 发现 + 加密 DMX 控制
|
||||
// 源 IP 固定 192.168.123.113 (真机 .1 的 AECH 固件过滤); 秒=0 只做认领+RDM 不发 DMX
|
||||
let ip = std::env::args().nth(2).unwrap_or_else(|| "192.168.123.1".into());
|
||||
let secs: u64 = std::env::args().nth(3).and_then(|s| s.parse().ok()).unwrap_or(30);
|
||||
hostdemo::run(&ip, "192.168.123.113", secs);
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
println!("用法: enet-validator <discover|announce|heartbeat|euce|session [秒]|fallback|claim|rdm [秒]|all>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
println!("----------------------------------------------------------------");
|
||||
let p = results.iter().filter(|r| r.pass).count();
|
||||
for r in &results { r.show(); }
|
||||
println!("================================================================");
|
||||
println!("汇总: PASS={} FAIL={}", p, results.len() - p);
|
||||
|
||||
// 写 JSON 报告
|
||||
let mut j = String::from("{\n \"target\": \"192.168.123.1\",\n \"results\": [\n");
|
||||
for (i, r) in results.iter().enumerate() {
|
||||
j.push_str(&format!(
|
||||
" {{ \"id\": \"{}\", \"pass\": {}, \"detail\": \"{}\" }}{}\n",
|
||||
r.id, r.pass, r.detail.replace('"', "'"),
|
||||
if i + 1 < results.len() { "," } else { "" }));
|
||||
}
|
||||
j.push_str(" ]\n}\n");
|
||||
let _ = std::fs::write("validation_report.json", j);
|
||||
println!("报告已写: validation_report.json");
|
||||
}
|
||||
905
src/tests.rs
Normal file
905
src/tests.rs
Normal file
|
|
@ -0,0 +1,905 @@
|
|||
// tests.rs — T1..T11 协议验证实现 (判据 = Butler 实际应答)
|
||||
use crate::enet::*;
|
||||
use std::collections::HashSet;
|
||||
use std::net::UdpSocket;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct Item {
|
||||
pub id: &'static str,
|
||||
pub pass: bool,
|
||||
pub detail: String,
|
||||
}
|
||||
impl Item {
|
||||
fn new(id: &'static str, pass: bool, detail: String) -> Item { Item { id, pass, detail } }
|
||||
pub fn show(&self) {
|
||||
println!(" [{}] {} {}", if self.pass { "PASS" } else { "FAIL" }, self.id, self.detail);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- T1: 发现请求 → Butler 应答 (§3.1→§3.2) ----------
|
||||
pub fn t1_discover() -> Item {
|
||||
let s = open();
|
||||
let req = load_template("discovery_req");
|
||||
let _ = s.send_to(&req, BCAST_SUB);
|
||||
let _ = s.send_to(&req, BCAST_ALL);
|
||||
let replies = recv_window(&s, Duration::from_secs(4));
|
||||
let mut detail = format!("收到{}包; ", replies.len());
|
||||
for (_, b) in &replies {
|
||||
if b.len() == 217 && &b[0..4] == b"ENet" {
|
||||
let len_ok = g32(b, 0x14) == 217;
|
||||
let name_ok = find(b, b"Butler S2");
|
||||
let mac_ok = find(b, &[0x00, 0x16, 0x1c, 0xf1, 0x2e, 0x39]);
|
||||
let pass = len_ok && name_ok && mac_ok;
|
||||
detail.push_str(&format!(
|
||||
"应答217B: len字段={} 名字={} MAC={}",
|
||||
len_ok, name_ok, mac_ok
|
||||
));
|
||||
return Item::new("T1", pass, detail);
|
||||
}
|
||||
}
|
||||
detail.push_str("未收到 217B 发现应答");
|
||||
Item::new("T1", false, detail)
|
||||
}
|
||||
|
||||
// ---------- T2/T3/T4: 单向帧发送 → 观察会话反应 ----------
|
||||
pub fn tx_observe(id: &'static str, tpl: &str, expect_reply: bool) -> Item {
|
||||
let s = open();
|
||||
let f = load_template(tpl);
|
||||
let dst = if tpl == "heartbeat" { BCAST_SUB } else { BCAST_ALL };
|
||||
let n = s.send_to(&f, dst).expect("send");
|
||||
let replies = recv_window(&s, Duration::from_secs(3));
|
||||
let mut detail = format!("发送{}B→{}; 收到{}包", n, dst, replies.len());
|
||||
let mut pass = true;
|
||||
if expect_reply {
|
||||
pass = !replies.is_empty();
|
||||
if !pass { detail.push_str("; 无应答"); }
|
||||
}
|
||||
Item::new(id, pass, detail)
|
||||
}
|
||||
|
||||
// ---------- T5-T8: 会话 (DMX + 事件×2 + ACK配对 + 状态帧) ----------
|
||||
pub struct SessionStats {
|
||||
pub dmx_sent: u64,
|
||||
pub evt_sent: u64,
|
||||
pub acks: u64,
|
||||
pub ack_matched: u64,
|
||||
pub status: u64,
|
||||
pub rdm: u64,
|
||||
pub other: u64,
|
||||
}
|
||||
|
||||
pub fn session(secs: u64, mutate_channels: bool) -> (SessionStats, Vec<(u32, u64)>) {
|
||||
let s = open();
|
||||
session_on(s, secs, mutate_channels, true)
|
||||
}
|
||||
|
||||
/// 会话核心 (可注入 socket): takeover 模式跳过 preamble 且使用抢 bind 的 socket
|
||||
pub fn session_on(s: UdpSocket, secs: u64, mutate_channels: bool, preamble: bool) -> (SessionStats, Vec<(u32, u64)>) {
|
||||
if preamble {
|
||||
// --- 前置认领 (§6 时序): 发现请求 → 宣告帧 → 短暂等待 ---
|
||||
let disc = load_template("discovery_req");
|
||||
let ann = load_template("announce");
|
||||
let _ = s.send_to(&disc, BCAST_SUB);
|
||||
let _ = s.send_to(&disc, BCAST_ALL);
|
||||
let _ = s.send_to(&ann, BCAST_ALL);
|
||||
let _ = recv_window(&s, Duration::from_secs(2)); // 吸收发现应答/宣告反应
|
||||
}
|
||||
let dmx_t = load_template("dmx_a");
|
||||
let evt_t = load_template("event_b");
|
||||
let mut dmx_seq = g16(&dmx_t, 0x1A).wrapping_add(1);
|
||||
let mut evt_seq = g32(&evt_t, 0x22).wrapping_add(1);
|
||||
let tick = Duration::from_millis(25);
|
||||
let start = Instant::now();
|
||||
let mut st = SessionStats { dmx_sent: 0, evt_sent: 0, acks: 0, ack_matched: 0, status: 0, rdm: 0, other: 0 };
|
||||
let mut recent: HashSet<u32> = HashSet::new();
|
||||
let mut timeline: Vec<(u32, u64)> = Vec::new();
|
||||
let mut tick_n: u64 = 0;
|
||||
while start.elapsed().as_secs() < secs {
|
||||
// --- DMX 帧 (形态A) ---
|
||||
let mut d = dmx_t.clone();
|
||||
p16(&mut d, 0x1A, dmx_seq);
|
||||
let tms = tick_n * 25;
|
||||
for i in 0..8 { d[0x22 + i] = ((tms >> (i * 8)) & 0xFF) as u8; }
|
||||
if mutate_channels {
|
||||
let ph = start.elapsed().as_secs();
|
||||
for i in 0..1024usize {
|
||||
d[0x3C + i] = match ph {
|
||||
0..=9 => 0,
|
||||
10..=19 => 255,
|
||||
_ => (((1024usize).wrapping_sub(i + (tick_n as usize) * 4)) & 0xFF) as u8,
|
||||
};
|
||||
}
|
||||
}
|
||||
let _ = s.send_to(&d, BUTLER);
|
||||
st.dmx_sent += 1;
|
||||
dmx_seq = dmx_seq.wrapping_add(1);
|
||||
// --- 事件帧 ×2 (形态B) ---
|
||||
for _ in 0..2 {
|
||||
let mut e = evt_t.clone();
|
||||
p32(&mut e, 0x22, evt_seq);
|
||||
recent.insert(evt_seq);
|
||||
if recent.len() > 64 { recent.remove(&(evt_seq.wrapping_sub(64))); }
|
||||
let _ = s.send_to(&e, BUTLER);
|
||||
st.evt_sent += 1;
|
||||
evt_seq = evt_seq.wrapping_add(1);
|
||||
}
|
||||
// --- 收集应答 ---
|
||||
for (_, b) in recv_window(&s, tick) {
|
||||
match classify(&b) {
|
||||
Rx::Ack(sq) => {
|
||||
st.acks += 1;
|
||||
if recent.contains(&sq) { st.ack_matched += 1; }
|
||||
timeline.push((sq, tick_n));
|
||||
}
|
||||
Rx::Status(_) => st.status += 1,
|
||||
Rx::RdmResp(_, _) => st.rdm += 1,
|
||||
Rx::DiscoveryResp(_) | Rx::EnetType1(_) => {}
|
||||
Rx::Other(_) => st.other += 1,
|
||||
}
|
||||
}
|
||||
tick_n += 1;
|
||||
}
|
||||
(st, timeline)
|
||||
}
|
||||
|
||||
pub fn t5_t8_session(secs: u64) -> Vec<Item> {
|
||||
println!(" 会话运行 {}s (DMX→事件×2, 25ms/tick)…", secs);
|
||||
let (st, _) = session(secs, true);
|
||||
let rate_dmx = st.dmx_sent as f64 / secs as f64;
|
||||
let pair = if st.acks > 0 { st.ack_matched as f64 / st.acks as f64 } else { 0.0 };
|
||||
vec![
|
||||
Item::new("T5", st.dmx_sent > secs * 30 && st.status > 0,
|
||||
format!("DMX发送={} (~{:.0}Hz) 状态帧={}", st.dmx_sent, rate_dmx, st.status)),
|
||||
Item::new("T6", pair >= 0.95,
|
||||
format!("ACK={} 配对={} ({:.1}%)", st.acks, st.ack_matched, pair * 100.0)),
|
||||
Item::new("T7", st.status > 0, format!("状态帧={}", st.status)),
|
||||
Item::new("T8", st.dmx_sent > secs * 30 && st.acks > secs,
|
||||
format!("会话{}s无中断", secs)),
|
||||
]
|
||||
}
|
||||
|
||||
// ---------- T9: 回落与恢复 ----------
|
||||
pub fn t9_fallback() -> Item {
|
||||
println!(" 阶段1: 会话 10s…");
|
||||
let (a, _) = session(10, false);
|
||||
println!(" 阶段2: 静默 12s (只收不发)…");
|
||||
let s = open();
|
||||
let quiet = recv_window(&s, Duration::from_secs(12));
|
||||
let mut quiet_status = 0usize;
|
||||
for (_, b) in &quiet {
|
||||
if let Rx::Status(_) = classify(b) { quiet_status += 1; }
|
||||
}
|
||||
println!(" 阶段3: 恢复会话 8s…");
|
||||
let (b, _) = session(8, false);
|
||||
let pass = a.acks > 5 && b.acks > 5; // 恢复后 ACK 恢复 = 重新接管成功
|
||||
Item::new("T9", pass, format!(
|
||||
"会话前段ACK={}; 静默期收{}包(状态{}); 恢复后ACK={}",
|
||||
a.acks, quiet.len(), quiet_status, b.acks))
|
||||
}
|
||||
|
||||
// ---------- T12(实验): AECHA 认领重放 ----------
|
||||
pub fn t12_claim_replay() -> Item {
|
||||
let s = open();
|
||||
let tick = Duration::from_millis(25);
|
||||
let c1 = load_template("claim_h112");
|
||||
let c2 = load_template("claim_h114");
|
||||
let c3 = load_template("claim_h116");
|
||||
let mut aereps = 0u64;
|
||||
let mut last_detail = String::new();
|
||||
for (i, f) in [&c1, &c2, &c3].into_iter().enumerate() {
|
||||
let _ = s.send_to(f, BUTLER);
|
||||
let replies = recv_window(&s, Duration::from_millis(800));
|
||||
let mut got = false;
|
||||
for (_, b) in &replies {
|
||||
if find(b, b"AEREP") { aereps += 1; got = true; }
|
||||
}
|
||||
last_detail.push_str(&format!("[{}] 应答{}包{} ", i + 1, replies.len(), if got { "+AEREP" } else { "" }));
|
||||
}
|
||||
// 若 3 轮完成 → 立即试探 DMX: 发 10 个 tick 看是否出现 ACK
|
||||
let mut acks = 0u64;
|
||||
if aereps >= 1 {
|
||||
let dmx_t = load_template("dmx_a");
|
||||
let evt_t = load_template("event_b");
|
||||
let mut dmx_seq = g16(&dmx_t, 0x1A).wrapping_add(1);
|
||||
let mut evt_seq = g32(&evt_t, 0x22).wrapping_add(1);
|
||||
for _ in 0..10 {
|
||||
let mut d = dmx_t.clone();
|
||||
p16(&mut d, 0x1A, dmx_seq);
|
||||
let _ = s.send_to(&d, BUTLER);
|
||||
dmx_seq = dmx_seq.wrapping_add(1);
|
||||
for _ in 0..2 {
|
||||
let mut e = evt_t.clone();
|
||||
p32(&mut e, 0x22, evt_seq);
|
||||
evt_seq += 1;
|
||||
let _ = s.send_to(&e, BUTLER);
|
||||
}
|
||||
for (_, b) in recv_window(&s, tick) {
|
||||
if let Rx::Ack(_) = classify(&b) { acks += 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
last_detail.push_str(&format!("; AEREP={} 试探ACK={}", aereps, acks));
|
||||
Item::new("T12", acks > 0, last_detail)
|
||||
}
|
||||
|
||||
/// takeover 变体: 使用外部传入的已 bind socket
|
||||
pub fn t13_takeover_on(s: UdpSocket, secs: u64) -> Vec<Item> {
|
||||
println!(" 发帧 {}s (共存模式)…", secs);
|
||||
let (st, _) = session_on(s, secs, true, false);
|
||||
let pair = if st.acks > 0 { st.ack_matched as f64 / st.acks as f64 } else { 0.0 };
|
||||
vec![
|
||||
Item::new("T13a", st.dmx_sent > secs * 20, format!("DMX发送={}", st.dmx_sent)),
|
||||
Item::new("T13b", st.acks > 0, format!("Rust收到ACK={} 配对={:.1}% 状态={}",
|
||||
st.acks, pair * 100.0, st.status)),
|
||||
]
|
||||
}
|
||||
|
||||
// ---------- T13(实验): 挂起 SymphoLight 后 Rust 抢占接管会话 ----------
|
||||
pub fn t13_takeover(secs: u64) -> Vec<Item> {
|
||||
// 用抢 bind 的 socket 跑会话核心 (无 preamble: 认证已由 SymphoLight 完成)
|
||||
let s = open_reuse(BIND);
|
||||
println!(" 接管会话 {}s (SO_REUSEADDR 模式)…", secs);
|
||||
let (st, _) = session_on(s, secs, true, false);
|
||||
let pair = if st.acks > 0 { st.ack_matched as f64 / st.acks as f64 } else { 0.0 };
|
||||
vec![
|
||||
Item::new("T13a", st.dmx_sent > secs * 20, format!("接管后 DMX发送={}", st.dmx_sent)),
|
||||
Item::new("T13b", st.acks > 0, format!("ACK={} 配对={} ({:.1}%) 状态={}",
|
||||
st.acks, st.ack_matched, pair * 100.0, st.status)),
|
||||
]
|
||||
}
|
||||
|
||||
// ---------- 设备端模拟 v2 (Secondary IP, 与真 Butler 并存) ----------
|
||||
// 双 socket: wildcard(REUSEADDR) 收广播发现; dev_ip:31772 收会话/发应答(源IP正确)
|
||||
pub fn device2(dev_ip: [u8; 4], dev_mac: [u8; 6], secs: u64, relay: Option<([u8; 4], u16)>) -> Vec<Item> {
|
||||
let mut items = Vec::new();
|
||||
let bind_addr = format!("{}.{}.{}.{}:31772", dev_ip[0], dev_ip[1], dev_ip[2], dev_ip[3]);
|
||||
let sess = open_reuse(&bind_addr);
|
||||
// wildcard(REUSEADDR) 收广播发现; 注意: 必须先启动本程序、后启动 SymphoLight
|
||||
let disc = std::panic::catch_unwind(|| open_reuse("0.0.0.0:31772")).ok();
|
||||
// AECH 中继 socket: 优先绑 113 (本机 123 网段时直接中继 .1); 非本网段则绑自身 IP (经桥中继)
|
||||
let relay_sock: Option<UdpSocket> = relay.map(|_| {
|
||||
std::panic::catch_unwind(|| open_reuse("192.168.123.113:0"))
|
||||
.unwrap_or_else(|_| open_reuse(&format!("{}.{}.{}.{}:0", dev_ip[0], dev_ip[1], dev_ip[2], dev_ip[3])))
|
||||
});
|
||||
sess.set_read_timeout(Some(Duration::from_millis(2))).unwrap();
|
||||
println!(" 虚拟 Butler @ {} (MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}), 运行 {}s",
|
||||
bind_addr, dev_mac[0], dev_mac[1], dev_mac[2], dev_mac[3], dev_mac[4], dev_mac[5], secs);
|
||||
|
||||
let resp_t = load_template("discovery_resp");
|
||||
let aerep_t = load_template("aerep");
|
||||
let ack_t = load_template("ack");
|
||||
let status_t = load_template("status"); // 84B "Butl" 设备状态遥测帧
|
||||
let reg89_t = load_template("register89"); // 89B 主机注册应答 (sub=02)
|
||||
let aere594_t = load_template("aere594"); // 594B AECH 认领应答 (sub=06, AERE)
|
||||
let ann144_t = load_template("announce144"); // 144B 宣告 (sub=01, 中继唤醒用)
|
||||
// RDM 应答模板: 5种DISC脉冲(101B) + 分支应答(111B) + 参数信息(119B)
|
||||
let pulse_ts = vec![load_template("rdm_pulse_0"), load_template("rdm_pulse_1"),
|
||||
load_template("rdm_pulse_2"), load_template("rdm_pulse_3"), load_template("rdm_pulse_4")];
|
||||
let rdm111_t = load_template("rdm_resp111");
|
||||
let rdm119_t = load_template("rdm_info119");
|
||||
let setack68_t = load_template("rdm_setack68"); // RDM SET 应答模板 (CC=31, PDL=0, 68B, set123b 实证)
|
||||
// 真机 .1 完整 RDM 应答帧池 (178 唯一帧: 56/59/70/77/87B 全部应答词表)
|
||||
let rdm_pool = crate::enet::load_rdm_pool();
|
||||
println!(" RDM 帧池: {} 帧", rdm_pool.len());
|
||||
// PID→应答模板映射 (cmd=18=RDM GET, 查询 PID 在 0x50, 应答含 21<PID>)
|
||||
let mut pid_map: std::collections::HashMap<[u8; 2], Vec<u8>> = std::collections::HashMap::new();
|
||||
// SET 写路径状态: 主机 SET (CC=0x30) 的值按 (UID, PID) 落存, 后续 GET 同 PID 回显新值
|
||||
// (真机行为: SET 立即生效, 主机 +33ms GET 验证 — set123b 实证, §5.6)
|
||||
let mut set_store: std::collections::HashMap<([u8; 6], [u8; 2]), Vec<u8>> = std::collections::HashMap::new();
|
||||
for line in include_str!("../samples/pid_map.txt").lines() {
|
||||
let line = line.trim();
|
||||
if line.len() < 8 { continue; }
|
||||
let pid_hex = &line[..4];
|
||||
let frame_hex = line[5..].trim();
|
||||
let pb: Vec<u8> = (0..pid_hex.len()).step_by(2).map(|i| u8::from_str_radix(&pid_hex[i..i + 2], 16).unwrap()).collect();
|
||||
let fb: Vec<u8> = (0..frame_hex.len()).step_by(2).map(|i| u8::from_str_radix(&frame_hex[i..i + 2], 16).unwrap()).collect();
|
||||
if pb.len() == 2 { pid_map.insert([pb[0], pb[1]], fb); }
|
||||
}
|
||||
println!(" PID 映射: {} 个", pid_map.len());
|
||||
|
||||
let start = Instant::now();
|
||||
let mut n_disc = 0u64; let mut n_aecha = 0u64; let mut n_dmx = 0u64;
|
||||
let mut n_evt = 0u64; let mut n_ack = 0u64; let mut n_other = 0u64;
|
||||
let mut n_status = 0u64; let mut status_seq = 0x62u32;
|
||||
let mut n_rdm_pulse = 0u64; let mut n_rdm_111 = 0u64; let mut n_rdm_119 = 0u64;
|
||||
let mut n_rdm_set = 0u64;
|
||||
let mut n_announce = 0u64;
|
||||
let mut pulse_idx = 0usize; let mut pulse_seq = 0x0f0bu32; let mut info_ctr = 0xc0u8;
|
||||
let mut pulse_step: u32 = 2;
|
||||
let mut pool_idx = 0usize;
|
||||
let mut cur_pulse = 0usize; let mut branch_ctr = 0u64;
|
||||
let mut cur_fixture = 0usize;
|
||||
let mut rdm_req_dumped = 0u32;
|
||||
let mut n_reg = 0u64; let mut n_claim = 0u64; let mut claim_nonce: u32 = 0x51f31ab9;
|
||||
let mut dmx_b12: Option<u32> = None; // AECH 质询 0x8C-0x8F 实例随机 → DMX 解密 rot 基值
|
||||
let mut n_rdm_relay = 0u64; let mut n_rdm_fwd = 0u64;
|
||||
let mut pending_rdm_bridge = [0u8; 6];
|
||||
// AECH 中继: (认领者地址, 主机IP LE, 主机MAC) — 质询转发真机后暂存
|
||||
let mut pending_claim: Option<(std::net::SocketAddr, [u8;4], [u8;6])> = None;
|
||||
let mut last_pulse = Instant::now() - Duration::from_secs(10);
|
||||
let mut first_channels: Option<[u8; 32]> = None;
|
||||
let mut last_channels: [u8; 32] = [0; 32];
|
||||
let mut ch_changes = 0u64;
|
||||
let mut last_ch_show = Instant::now() - Duration::from_secs(10);
|
||||
let mut peer: Option<std::net::SocketAddr> = None;
|
||||
// RDM 应答延迟队列 (真机灯具经 RS485 应答延迟 12-48ms, 中位 29ms — 瞬时应答是假指纹)
|
||||
let mut rdm_queue: Vec<(Instant, Vec<u8>, std::net::SocketAddr)> = Vec::new();
|
||||
let mut delay_lcg: u32 = 0x9e3779b9;
|
||||
let mut next_delay = move |base_ms: u64, span_ms: u64| -> u64 {
|
||||
delay_lcg = delay_lcg.wrapping_mul(2654435761).wrapping_add(0x9e3779b9);
|
||||
base_ms + ((delay_lcg >> 16) as u64 % span_ms)
|
||||
};
|
||||
// 42B 遥测: 真机 ~每 115ms 上报一次"已收事件计数" (0x20-0x23 严格递增,
|
||||
// 增量=事件B帧数, ss123 实证 +2959 计数 ≈ 2963 事件)
|
||||
let mut event_ctr: u32 = 0;
|
||||
let mut ack42_sent_val: u32 = 0;
|
||||
let mut last_ack42 = Instant::now() - Duration::from_secs(1);
|
||||
let mut evt_dst: Option<std::net::SocketAddr> = None;
|
||||
|
||||
while start.elapsed().as_secs() < secs {
|
||||
// --- 真机无自发状态帧 (168s 仅 1 帧 42B, 已全部删除突发遥测) ---
|
||||
// --- 到期的 RDM 延迟应答 ---
|
||||
if !rdm_queue.is_empty() {
|
||||
let now = Instant::now();
|
||||
let mut i = 0;
|
||||
while i < rdm_queue.len() {
|
||||
if rdm_queue[i].0 <= now {
|
||||
let (_, r, dst) = rdm_queue.remove(i);
|
||||
let _ = sess.send_to(&r, dst);
|
||||
} else { i += 1; }
|
||||
}
|
||||
}
|
||||
// --- 42B 遥测: 真机 ~每 115ms 上报一次"已收事件计数" (ss123 实证) ---
|
||||
if last_ack42.elapsed() > Duration::from_millis(115) && event_ctr != ack42_sent_val {
|
||||
if let Some(dst) = evt_dst {
|
||||
let mut a = ack_t.to_vec();
|
||||
p32(&mut a, 0x20, event_ctr);
|
||||
let _ = sess.send_to(&a, dst);
|
||||
ack42_sent_val = event_ctr;
|
||||
last_ack42 = Instant::now();
|
||||
}
|
||||
}
|
||||
let mut buf = [0u8; 2048];
|
||||
// --- wildcard socket: 收广播发现 (仅初始发现阶段用) ---
|
||||
if let Some(ref d) = disc {
|
||||
if let Ok((n, src)) = d.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
if b.len() == 180 && &b[0..4] == b"ENet" && g32(b, 4) == 1 {
|
||||
// 真机处理延迟 ~160ms (指纹对齐)
|
||||
std::thread::sleep(Duration::from_millis(150));
|
||||
let mut r = resp_t.clone();
|
||||
patch_dev(&mut r, dev_ip, dev_mac);
|
||||
patch_259_host(&mut r, src, b);
|
||||
let _ = sess.send_to(&r, src);
|
||||
n_disc += 1;
|
||||
println!(" [{}] 发现请求 type1 from {} len={}", chrono_t(start), src, b.len());
|
||||
} else if b.len() >= 0x1A && &b[0..4] == b"ENet" && g32(b, 4) == 2 && g16(b, 0x18) == 1 {
|
||||
// 广播主机宣告 sub=01 → 也回 89B 注册帧 (从 sess 口发出)
|
||||
let mut r = reg89_t.clone();
|
||||
let ip_le = [dev_ip[3], dev_ip[2], dev_ip[1], dev_ip[0]];
|
||||
r[0x1A..0x1E].copy_from_slice(&ip_le);
|
||||
r[0x1E..0x24].copy_from_slice(&dev_mac);
|
||||
if b.len() > 0x24 {
|
||||
r[0x25..0x29].copy_from_slice(&b[0x1A..0x1E]);
|
||||
r[0x29..0x2F].copy_from_slice(&b[0x1E..0x24]);
|
||||
}
|
||||
let _ = sess.send_to(&r, src);
|
||||
n_reg += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// --- 中继回程轮询: 真机 AERE 回到 relay_sock (113:随机端口) ---
|
||||
if let Some(rs) = relay_sock.as_ref() {
|
||||
if let Ok((n, src)) = rs.recv_from(&mut buf) {
|
||||
let b2 = &buf[..n];
|
||||
if b2.len() >= 8 && &b2[0..4] == b"ENet" {
|
||||
// --- RDM 应答回程: 真机灯具的 type=04 应答 (脉冲59B/分支69B/参数77B) → 发给主机 ---
|
||||
if g32(b2, 4) == 4 && b2.len() >= 40 {
|
||||
if let Some(p) = peer {
|
||||
let mut r = b2.to_vec();
|
||||
// cc01 帧内含 bridge-id (0x2D-0x32), 替换为查询的 bridge-id
|
||||
if r.len() > 0x33 && r[0x2A] == 0xCC {
|
||||
r[0x2D..0x33].copy_from_slice(&pending_rdm_bridge);
|
||||
}
|
||||
let _ = sess.send_to(&r, p);
|
||||
n_rdm_fwd += 1;
|
||||
if n_rdm_fwd <= 4 { println!(" [{}] RDM 应答回程 → 主机 ({}B)", chrono_t(start), r.len()); }
|
||||
}
|
||||
}
|
||||
if let (Some((pc, hip, hmac)), Some(_)) = (pending_claim, relay) {
|
||||
if b2.len() >= 552 && g32(b2, 4) == 2 && g16(b2, 0x18) == 6 { let mut r = b2.to_vec();
|
||||
let ip_le = [dev_ip[3], dev_ip[2], dev_ip[1], dev_ip[0]];
|
||||
r[0x1A..0x1E].copy_from_slice(&ip_le);
|
||||
r[0x41..0x45].copy_from_slice(&ip_le);
|
||||
r[0x1E..0x24].copy_from_slice(&dev_mac);
|
||||
r[0x49..0x4F].copy_from_slice(&dev_mac);
|
||||
r[0x25..0x28].copy_from_slice(&hip[0..3]);
|
||||
r[0x2C..0x2F].copy_from_slice(&hmac[3..6]);
|
||||
let _ = sess.send_to(&r, pc);
|
||||
pending_claim = None;
|
||||
n_claim += 1;
|
||||
println!(" [{}] ★★ 真机 AERE 中继成功 → {} #{}", chrono_t(start), pc, n_claim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// --- 会话 socket: 单播发现 / AECHA / DMX / 事件 ---
|
||||
let (n, src) = match sess.recv_from(&mut buf) {
|
||||
Ok(x) => x,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let b = &buf[..n];
|
||||
if b.len() < 8 || &b[0..4] != b"ENet" { continue; }
|
||||
peer = Some(src); // 记录对端(SymphoLight)用于遥测上行
|
||||
let t = g32(b, 4);
|
||||
if t == 1 && b.len() == 180 {
|
||||
let mut r = resp_t.clone();
|
||||
patch_dev(&mut r, dev_ip, dev_mac);
|
||||
patch_259_host(&mut r, src, b);
|
||||
let _ = sess.send_to(&r, src);
|
||||
n_disc += 1;
|
||||
println!(" [{}] 发现请求 type1 from {} len={}", chrono_t(start), src, b.len());
|
||||
} else if t == 2 {
|
||||
let sub = if b.len() >= 0x1A { g16(b, 0x18) } else { 0 };
|
||||
if sub == 5 && find(b, b"AECH") {
|
||||
if let (Some((rip, rport)), Some(rs)) = (relay, relay_sock.as_ref()) {
|
||||
// --- 中继模式: 先向中继目标发 sub=01 宣告唤醒注册态, 再转发质询 ---
|
||||
let hip = [b[0x1A], b[0x1B], b[0x1C], b[0x1D]];
|
||||
let hmac = [b[0x1E], b[0x1F], b[0x20], b[0x21], b[0x22], b[0x23]];
|
||||
pending_claim = Some((src, hip, hmac));
|
||||
let raddr = format!("{}.{}.{}.{}:{}", rip[0], rip[1], rip[2], rip[3], rport);
|
||||
// 宣告帧: 0x1A-1D 主机IP(LE), 0x1E-23 主机MAC
|
||||
let mut ann = ann144_t.clone();
|
||||
ann[0x1A..0x1E].copy_from_slice(&hip);
|
||||
ann[0x1E..0x24].copy_from_slice(&hmac);
|
||||
let _ = rs.send_to(&ann, &raddr);
|
||||
std::thread::sleep(Duration::from_millis(30));
|
||||
let _ = rs.send_to(b, &raddr);
|
||||
println!(" [{}] AECH 质询中继 → 真机 {} (先宣告唤醒)", chrono_t(start), raddr);
|
||||
} else {
|
||||
// --- AECH 认领质询 (594B sub=05) → AERE 应答 (sub=06) ---
|
||||
// 质询帧内: 0x1A-1D 主机IP(LE), 0x1E-23 主机MAC, 0x28 "AECH"
|
||||
let mut r = aere594_t.clone();
|
||||
// 设备 IP (LE) 0x1A 与 0x41; 设备 MAC 0x1E 与 0x49
|
||||
let ip_le = [dev_ip[3], dev_ip[2], dev_ip[1], dev_ip[0]];
|
||||
r[0x1A..0x1E].copy_from_slice(&ip_le);
|
||||
r[0x41..0x45].copy_from_slice(&ip_le);
|
||||
r[0x1E..0x24].copy_from_slice(&dev_mac);
|
||||
r[0x49..0x4F].copy_from_slice(&dev_mac);
|
||||
// AERE 0x25-27 = 主机IP前3字节(LE), 0x2C-2E = 主机MAC后3字节
|
||||
if b.len() > 0x24 {
|
||||
r[0x25..0x28].copy_from_slice(&b[0x1A..0x1D]);
|
||||
r[0x2C..0x2F].copy_from_slice(&b[0x21..0x24]);
|
||||
}
|
||||
// 0xC0-0xC3 = AgiosElias G(nonce@0x88-0x8B LE) — 算法已从 SymphoLight
|
||||
// Ecue.Devices.dll 反编译还原 (无密钥! G(x)=x^TABLE[(x%32+1+(x*13)%30)%32]),
|
||||
// 主机按同式验证 AERE blob[152]。无需真机中继。
|
||||
let nonce = if b.len() > 0x8C { g32(b, 0x88) } else { 0 };
|
||||
p32(&mut r, 0xC0, crate::aech::agios_g(nonce));
|
||||
// 0x8C-0x8F = AgiosElias 实例随机 B12 — DMX 帧混淆 rot=(B12^r)&511 的会话基值
|
||||
if b.len() > 0x90 { dmx_b12 = Some(g32(b, 0x8C)); }
|
||||
let _ = sess.send_to(&r, src);
|
||||
n_claim += 1;
|
||||
println!(" [{}] ★ AECH 认领质询 → 本地计算 AERE (G({:08x})={:08x}) #{}",
|
||||
chrono_t(start), nonce, crate::aech::agios_g(nonce), n_claim);
|
||||
}
|
||||
} else if sub == 1 {
|
||||
// --- 主机身份宣告 sub=01 (102B) → 89B 注册应答 sub=02 ---
|
||||
// 宣告内: 0x1A-1D 主机IP(LE), 0x1E-23 主机MAC
|
||||
let mut r = reg89_t.clone();
|
||||
let ip_le = [dev_ip[3], dev_ip[2], dev_ip[1], dev_ip[0]];
|
||||
r[0x1A..0x1E].copy_from_slice(&ip_le);
|
||||
r[0x1E..0x24].copy_from_slice(&dev_mac);
|
||||
if b.len() > 0x24 {
|
||||
r[0x25..0x29].copy_from_slice(&b[0x1A..0x1E]); // 主机IP(LE)
|
||||
r[0x29..0x2F].copy_from_slice(&b[0x1E..0x24]); // 主机MAC
|
||||
}
|
||||
let _ = sess.send_to(&r, src);
|
||||
n_reg += 1;
|
||||
if n_reg <= 3 { println!(" [{}] 主机宣告 sub=01 → 回 89B 注册帧", chrono_t(start)); }
|
||||
} else {
|
||||
// --- 已认领心跳 sub=03 (36B, 每~6s): 真机从不应答 (cold2 实证), 静默 ---
|
||||
n_status += 1; // 仅计数
|
||||
}
|
||||
} else if t == 4 && b.len() == 1084 {
|
||||
if g16(b, 0x20) != 0x0101 {
|
||||
event_ctr = event_ctr.wrapping_add(1); // 事件B帧计数 (含 RDM 查询, 42B 遥测用)
|
||||
evt_dst = Some(src);
|
||||
}
|
||||
if g16(b, 0x20) == 0x0101 {
|
||||
n_dmx += 1;
|
||||
// DMX 数据经 zPprv0TXRRW 逐帧混淆 (§4.1): r=0x22-0x25 LE, rot=(B12^r)&511
|
||||
let ch16 = match dmx_b12 {
|
||||
Some(b12) => {
|
||||
let r1 = g32(b, 0x22);
|
||||
let plain = crate::aech::agios_dmx_decrypt(&b[0x3C..0x3C + 512], r1, b12);
|
||||
hexs(&plain[..16])
|
||||
}
|
||||
None => hexs(&b[0x3C..0x3C + 16]), // 未认领前: 原始线值
|
||||
};
|
||||
let mut ch = [0u8; 32];
|
||||
ch.copy_from_slice(&b[0x3C..0x3C + 32]);
|
||||
if first_channels.is_none() { first_channels = Some(ch); }
|
||||
if ch != last_channels { ch_changes += 1; last_channels = ch; }
|
||||
if last_ch_show.elapsed() > Duration::from_secs(5) {
|
||||
last_ch_show = Instant::now();
|
||||
println!(" [{}] DMX #{} CH1-16: {}", chrono_t(start), n_dmx, ch16);
|
||||
}
|
||||
} else if b.len() > 0x44 && b[0x3C] == 0x01 && (b[0x3D] == 0x24 || b[0x3D] == 0x18 || b[0x3D] == 0x19 || b[0x3D] == 0x1a) {
|
||||
// --- RDM 请求 (0x20=0200<seq16>, 0x3C=01, 0x3D=cmd, 0x3E+=目标UID) ---
|
||||
let mut uid = [0u8; 6];
|
||||
uid.copy_from_slice(&b[0x3E..0x44]);
|
||||
// RDM 一律本地生成式应答 (全新UID灯具); relay 仅用于 AECH 认领
|
||||
if rdm_req_dumped < 3 {
|
||||
rdm_req_dumped += 1;
|
||||
let dump_len = std::cmp::min(0x80, b.len());
|
||||
println!(" [RDM-REQ#{}] len={} hex[0..{}]={}", rdm_req_dumped, b.len(), dump_len, hexs(&b[..dump_len]));
|
||||
}
|
||||
if b[0x3D] == 0x19 && rdm_req_dumped < 8 {
|
||||
rdm_req_dumped += 1;
|
||||
let dump_len = std::cmp::min(0x90, b.len());
|
||||
println!(" [RDM-19#{}] len={} hex[0..{}]={}", rdm_req_dumped, b.len(), dump_len, hexs(&b[..dump_len]));
|
||||
}
|
||||
if b[0x30] != 0x00 {
|
||||
// flag=01 = DMX 内嵌副本帧: 真机完全不答, 忽略
|
||||
} else if b[0x3D] == 0x24 {
|
||||
// --- DISC_UNIQUE_BRANCH: 分支范围 lo@0x53-0x58 hi@0x59-0x5E (PID 0001@0x50, PDL=12@0x52) ---
|
||||
// 真机灯具只应答 UID 落在分支范围内的查询 (ss123 实证二分收敛);
|
||||
// 此前无视范围全部应答 → 主机二分水远无法排空分支 → 51/s 发现洪水, 稳态轮询不启动
|
||||
if b.len() > 0x5E {
|
||||
let lo = &b[0x53..0x59]; let hi = &b[0x59..0x5F];
|
||||
for fx in FIXTURES.iter() {
|
||||
if &fx[..] >= lo && &fx[..] <= hi {
|
||||
let pulse = rdm_encode_uid(fx);
|
||||
let mut r: Vec<u8> = Vec::with_capacity(59);
|
||||
r.extend_from_slice(&pulse_ts[0][..0x2A]); // 模板头
|
||||
// 脉冲 seq = 设备本地自增序号 (真机行为, 非回显!)
|
||||
p32(&mut r, 0x20, pulse_seq);
|
||||
pulse_seq = pulse_seq.wrapping_add(pulse_step);
|
||||
pulse_step = 1 + (pulse_step * 7 + 0x1d) % 3; // 每帧 +1..3
|
||||
r.extend_from_slice(&pulse);
|
||||
let due = Instant::now() + Duration::from_millis(next_delay(2, 40));
|
||||
rdm_queue.push((due, r, src));
|
||||
n_rdm_pulse += 1;
|
||||
}
|
||||
}
|
||||
if n_rdm_pulse <= 5 || n_rdm_pulse % 100 == 0 {
|
||||
println!(" [RDM] 分支发现范围内应答 #{}", n_rdm_pulse);
|
||||
}
|
||||
}
|
||||
} else if b[0x3D] == 0x18 && uid == [0xff; 6] {
|
||||
// 广播 UID 的发现命令 (广播 MUTE 0003 等): 真机不应答, 静默
|
||||
} else if b[0x3D] == 0x18 {
|
||||
// cmd=18 = RDM GET. PID 在 0x50-0x51.
|
||||
// PID=0002 = DISC_UN_MUTE → 70B ACK (CC=11, PDL=2, PD=0000) — 冷启动抓包实证
|
||||
let pid = if b.len() > 0x51 { [b[0x50], b[0x51]] } else { [0, 0] };
|
||||
let mirror = mirror_uid(&uid);
|
||||
// 优先帧池中镜像灯具的同 PID 应答 (GET_RESP=0x21, 数据自洽); 其次 pid_map; 兜底 111
|
||||
let pool_hit = rdm_pool.iter().find(|f|
|
||||
f.len() >= 0x42 && f[0x2A] == 0xCC && f[0x33..0x39] == mirror
|
||||
&& f[0x3E] == 0x21 && f[0x3F] == pid[0] && f[0x40] == pid[1]);
|
||||
let mut r = if let Some(t) = pool_hit {
|
||||
t.clone()
|
||||
} else if let Some(t) = pid_map.get(&pid) {
|
||||
t.clone()
|
||||
} else {
|
||||
rdm111_t.clone()
|
||||
};
|
||||
if r.len() > 0x39 { r[0x33..0x39].copy_from_slice(&uid); } // src UID = 虚拟灯具
|
||||
// dest UID (0x2D-0x32) = 查询方 bridge-id; seq 回显; TN 回显查询 TN(0x4A)
|
||||
if b.len() > 0x4A && r.len() > 0x33 { r[0x2D..0x33].copy_from_slice(&b[0x44..0x4A]); }
|
||||
r[0x20] = b[0x22]; r[0x21] = b[0x23];
|
||||
if b.len() > 0x4B && r.len() > 0x3A { r[0x39] = b[0x4A]; }
|
||||
// SET 过的 PID 回显新值 (真机: SET 立即生效, §5.6)
|
||||
if let Some(v) = set_store.get(&(uid, pid)) {
|
||||
let pdl = if r.len() > 0x41 { r[0x41] as usize } else { 0 };
|
||||
if pdl == v.len() && r.len() >= 0x42 + pdl {
|
||||
r[0x42..0x42 + pdl].copy_from_slice(v);
|
||||
}
|
||||
}
|
||||
rdm_checksum(&mut r); // patch 后必须重算 (真机 1396/1396 有效)
|
||||
let due = Instant::now() + Duration::from_millis(next_delay(15, 25));
|
||||
n_rdm_111 += 1;
|
||||
if n_rdm_111 <= 6 || n_rdm_111 % 100 == 0 {
|
||||
println!(" [RDM] GET UID={:02x}{:02x}{:02x}{:02x}{:02x}{:02x} PID={:02x}{:02x} → {}B",
|
||||
uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], pid[0], pid[1], r.len());
|
||||
}
|
||||
rdm_queue.push((due, r, src));
|
||||
} else if b[0x4F] == 0x30 {
|
||||
// --- RDM SET (CC=0x30, msgLen=0x1a+): 真机回 CC=0x31 ACK, RT=00, PDL=0,
|
||||
// 68B, TN/UID 回显, checksum 有效, 延迟 18-25ms (set123b 实证, §5.6) ---
|
||||
let mut r = setack68_t.clone();
|
||||
if r.len() > 0x39 { r[0x33..0x39].copy_from_slice(&uid); } // src UID = 虚拟灯具
|
||||
if b.len() > 0x4A && r.len() > 0x33 { r[0x2D..0x33].copy_from_slice(&b[0x44..0x4A]); } // dest = 查询方
|
||||
r[0x20] = b[0x22]; r[0x21] = b[0x23]; // seq 回显
|
||||
if b.len() > 0x4B && r.len() > 0x3A { r[0x39] = b[0x4A]; } // TN 回显
|
||||
if b.len() > 0x51 && r.len() > 0x41 { r[0x3F] = b[0x50]; r[0x40] = b[0x51]; } // PID 回显
|
||||
rdm_checksum(&mut r); // patch 后必须重算
|
||||
let due = Instant::now() + Duration::from_millis(next_delay(15, 25));
|
||||
let pid = if b.len() > 0x51 { [b[0x50], b[0x51]] } else { [0, 0] };
|
||||
// 应用写入值 (PDL@0x52, PD@0x53): 后续 GET 同 PID 回显新值 (§5.6 主机验证模式)
|
||||
let pdl = if b.len() > 0x52 { b[0x52] as usize } else { 0 };
|
||||
if pdl > 0 && b.len() >= 0x53 + pdl {
|
||||
set_store.insert((uid, pid), b[0x53..0x53 + pdl].to_vec());
|
||||
}
|
||||
rdm_queue.push((due, r, src));
|
||||
n_rdm_set += 1;
|
||||
if n_rdm_set <= 6 || n_rdm_set % 100 == 0 {
|
||||
println!(" [RDM] SET UID={:02x}{:02x}{:02x}{:02x}{:02x}{:02x} PID={:02x}{:02x} PD={} → 68B ACK #{}",
|
||||
uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], pid[0], pid[1],
|
||||
hexs(set_store.get(&(uid, pid)).map(|v| &v[..]).unwrap_or(&[])), n_rdm_set);
|
||||
}
|
||||
} else {
|
||||
// cmd=19/1a (SET类/传感器轮询 SENSOR_VALUE 0201): PID@0x50, PD@0x53
|
||||
// 按镜像灯具 + PID + PD[0](传感器序号) 从帧池取自恰应答
|
||||
let pid = if b.len() > 0x51 { [b[0x50], b[0x51]] } else { [0, 0] };
|
||||
let pd0 = if b.len() > 0x53 { b[0x53] } else { 0 };
|
||||
let mirror = mirror_uid(&uid);
|
||||
let pool_hit = rdm_pool.iter().find(|f|
|
||||
f.len() >= 0x43 && f[0x2A] == 0xCC && f[0x33..0x39] == mirror
|
||||
&& f[0x3F] == pid[0] && f[0x40] == pid[1] && f[0x42] == pd0);
|
||||
let mut r = if let Some(t) = pool_hit {
|
||||
t.clone()
|
||||
} else {
|
||||
rdm119_t.clone()
|
||||
};
|
||||
if r.len() > 0x39 { r[0x33..0x39].copy_from_slice(&uid); }
|
||||
if b.len() > 0x4A && r.len() > 0x33 { r[0x2D..0x33].copy_from_slice(&b[0x44..0x4A]); }
|
||||
r[0x20] = b[0x22]; r[0x21] = b[0x23];
|
||||
if b.len() > 0x4B && r.len() > 0x3A { r[0x39] = b[0x4A]; }
|
||||
rdm_checksum(&mut r);
|
||||
let due = Instant::now() + Duration::from_millis(next_delay(15, 25));
|
||||
let rlen = r.len();
|
||||
rdm_queue.push((due, r, src));
|
||||
n_rdm_119 += 1;
|
||||
if n_rdm_119 <= 6 || n_rdm_119 % 100 == 0 {
|
||||
println!(" [RDM] 参数/传感器 UID={:02x}{:02x}{:02x}{:02x}{:02x}{:02x} PID={:02x}{:02x} PD0={:02x} → {}B #{}",
|
||||
uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], pid[0], pid[1], pd0, rlen, n_rdm_119);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 非 RDM 事件B帧: 真机只计数, 由 42B 周期遥测上报, 无即时 ACK (ss123 实证)
|
||||
n_evt += 1;
|
||||
}
|
||||
} else {
|
||||
n_other += 1;
|
||||
if n_other <= 12 {
|
||||
println!(" [?] 其他帧 src={} len={} type={} hex[0..48]={}", src, b.len(), t, hexs(&b[..std::cmp::min(48, b.len())]));
|
||||
}
|
||||
}
|
||||
}
|
||||
items.push(Item::new("U1", n_disc > 0, format!("发现应答={} 遥测={} RDM脉冲={} RDM111={} RDM119={}", n_disc, n_status, n_rdm_pulse, n_rdm_111, n_rdm_119)));
|
||||
items.push(Item::new("U2", n_aecha > 0, format!("AECHA 认领交换={} 轮 (AEREP 已回)", n_aecha)));
|
||||
items.push(Item::new("U3", n_dmx > 0, format!("明文 DMX 帧接收={}", n_dmx)));
|
||||
items.push(Item::new("U4", n_ack > 0, format!("事件帧={} ACK发送={}", n_evt, n_ack)));
|
||||
items.push(Item::new("U5", n_dmx > secs * 10, format!(
|
||||
"通道值: 首32CH={:?} 变化={} 其他帧={}", first_channels.unwrap_or([0; 32]), ch_changes, n_other)));
|
||||
items
|
||||
}
|
||||
|
||||
// ---------- RDM 广播发现重放探测: 向目标重放真机 RDM 广播请求, 抓取并分类应答 ----------
|
||||
pub fn rdm_probe(target: &str, secs: u64) -> Item {
|
||||
let req_t = load_template("rdm_disc_req");
|
||||
let bind = "0.0.0.0:0";
|
||||
let s = UdpSocket::bind(bind).expect("rdmprobe bind 失败");
|
||||
s.set_broadcast(true).unwrap();
|
||||
s.set_read_timeout(Some(Duration::from_millis(120))).unwrap();
|
||||
let dst = format!("{}:31772", target);
|
||||
println!(" rdmprobe → {} 运行 {}s", dst, secs);
|
||||
let start = Instant::now();
|
||||
let mut seq: u32 = 0x090f;
|
||||
let mut sent = 0u64;
|
||||
let mut resp_lens: std::collections::HashMap<usize, u64> = std::collections::HashMap::new();
|
||||
let mut first_resp: Option<Vec<u8>> = None;
|
||||
let mut delays: Vec<u32> = Vec::new();
|
||||
let mut last_send = Instant::now() - Duration::from_secs(10);
|
||||
let mut pending: Option<(Instant, u32)> = None;
|
||||
while start.elapsed().as_secs() < secs {
|
||||
// 每 500ms 发一次请求
|
||||
if last_send.elapsed() > Duration::from_millis(500) {
|
||||
let mut r = req_t.clone();
|
||||
p32(&mut r, 0x20, seq);
|
||||
let _ = s.send_to(&r, &dst);
|
||||
pending = Some((Instant::now(), seq));
|
||||
seq = seq.wrapping_add(1);
|
||||
sent += 1;
|
||||
last_send = Instant::now();
|
||||
}
|
||||
let mut buf = [0u8; 2048];
|
||||
match s.recv_from(&mut buf) {
|
||||
Ok((n, src)) => {
|
||||
let b = &buf[..n].to_vec();
|
||||
*resp_lens.entry(n).or_insert(0) += 1;
|
||||
if first_resp.is_none() { first_resp = Some(b.clone()); }
|
||||
if let Some((t0, sq)) = pending {
|
||||
if n >= 0x22 && g32(b, 4) == 4 {
|
||||
let echo = g16(b, 0x20);
|
||||
let expect = ((sq >> 16) as u16) ^ 0; // seq低16位在0x22
|
||||
let _ = expect;
|
||||
delays.push((t0.elapsed().as_millis() as u32));
|
||||
println!(" [{}] 应答 src={} len={} seqEcho=0x{:04x} (请求seq低16=0x{:04x}) 延迟={}ms",
|
||||
chrono_t(start), src, n, echo, (sq & 0xffff) as u16, t0.elapsed().as_millis());
|
||||
} else {
|
||||
println!(" [{}] 应答 src={} len={} type={}", chrono_t(start), src, n, if n>=8 { g32(b,4) } else { 0 });
|
||||
}
|
||||
pending = None;
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
let mut lens_desc = String::new();
|
||||
let mut keys: Vec<_> = resp_lens.keys().cloned().collect();
|
||||
keys.sort();
|
||||
for k in keys { lens_desc.push_str(&format!(" {}B×{}", k, resp_lens[&k])); }
|
||||
let avg_delay = if delays.is_empty() { 0 } else { delays.iter().sum::<u32>() / delays.len() as u32 };
|
||||
Item::new("RDMP", sent > 0 && !resp_lens.is_empty(),
|
||||
format!("请求={} 应答[{}] 平均延迟={}ms", sent, lens_desc.trim(), avg_delay))
|
||||
}
|
||||
|
||||
// ---------- RDM DISC 脉冲编码 (E1.20) ----------
|
||||
/// 虚拟灯具: 全新 UID (同厂 0107 系列, 主机未注册过的序列号)
|
||||
const FIXTURES: [[u8; 6]; 3] = [
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x30],
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x31],
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x32],
|
||||
];
|
||||
/// 镜像映射: 虚拟 UID → 真机灯具 UID (参数数据按镜像灯具从 rdm_pool 取,
|
||||
/// 使 3 台虚拟灯具拥有各自自洽的 DMX 地址/标签/传感器数, 与 .1 总线同构)
|
||||
const FIXTURE_MIRROR: [[u8; 6]; 3] = [
|
||||
[0x01, 0x07, 0x34, 0x67, 0x01, 0x25], // NFS1K2DxxxBx DMX101-102 23传感器
|
||||
[0x01, 0x07, 0x34, 0x88, 0x09, 0x00], // NFM680SxxxGx DMX111 17传感器
|
||||
[0x08, 0x99, 0x50, 0x00, 0x26, 0xc9], // HMKJ DMX1-32 4传感器
|
||||
];
|
||||
fn mirror_uid(uid: &[u8; 6]) -> [u8; 6] {
|
||||
for (i, f) in FIXTURES.iter().enumerate() {
|
||||
if f == uid { return FIXTURE_MIRROR[i]; }
|
||||
}
|
||||
*uid
|
||||
}
|
||||
|
||||
// E1.20 checksum: 从 CC(0x2A) 起 msgLen(0x2C) 字节的 16-bit 和, BE 写于 [0x2A+msgLen]
|
||||
// (冷启动抓包验证: 真机 1396/1396 全部满足此规则)
|
||||
fn rdm_checksum(r: &mut [u8]) {
|
||||
if r.len() < 0x2E || r[0x2A] != 0xCC { return; }
|
||||
let msg_len = r[0x2C] as usize;
|
||||
if r.len() < 0x2A + msg_len + 2 { return; }
|
||||
let mut sum: u16 = 0;
|
||||
for i in 0x2A..0x2A + msg_len { sum = sum.wrapping_add(r[i] as u16); }
|
||||
r[0x2A + msg_len] = (sum >> 8) as u8;
|
||||
r[0x2A + msg_len + 1] = (sum & 0xFF) as u8;
|
||||
}
|
||||
|
||||
fn rdm_encode_uid(uid: &[u8; 6]) -> [u8; 17] {
|
||||
let mut out = [0u8; 17];
|
||||
out[0] = 0xAA; // 分隔符
|
||||
let mut sum: u16 = 0;
|
||||
for (i, &b) in uid.iter().enumerate() {
|
||||
let b1 = 0xAA | (b & 0x55);
|
||||
let b2 = 0x55 | (b & 0xAA);
|
||||
out[1 + 2 * i] = b1;
|
||||
out[2 + 2 * i] = b2;
|
||||
sum += b1 as u16 + b2 as u16;
|
||||
}
|
||||
let hi = (sum >> 8) as u8;
|
||||
let lo = (sum & 0xFF) as u8;
|
||||
out[13] = 0xAA | (hi & 0x55);
|
||||
out[14] = 0x55 | (hi & 0xAA);
|
||||
out[15] = 0xAA | (lo & 0x55);
|
||||
out[16] = 0x55 | (lo & 0xAA);
|
||||
out
|
||||
}
|
||||
|
||||
// ---------- 259 应答的 Connected To 动态化: 从发现请求提取请求方主机 IP+MAC ----------
|
||||
fn patch_259_host(r: &mut [u8], src: std::net::SocketAddr, req: &[u8]) {
|
||||
if let std::net::IpAddr::V4(v4) = src.ip() {
|
||||
let o = v4.octets();
|
||||
let ip_le = [o[3], o[2], o[1], o[0]];
|
||||
// host IP (0x4F-0x52) = 请求方 IP (LE)
|
||||
if r.len() > 0x53 { r[0x4F..0x53].copy_from_slice(&ip_le); }
|
||||
// host MAC (0x53-0x58): 请求里 hostIP(LE) 后 +8 处 (IP4 + 00ffffff掩码 + MAC6)
|
||||
for i in 0..req.len().saturating_sub(14) {
|
||||
if req[i..i + 4] == ip_le && req[i + 4..i + 8] == [0x00, 0xff, 0xff, 0xff] {
|
||||
if r.len() > 0x59 { r[0x53..0x59].copy_from_slice(&req[i + 8..i + 14]); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- bridge: AECH 双向桥 (非123网段设备的认领桥) ----------
|
||||
// 监听 listen_ip:listen_port 收远端设备的 AECH, 以源 IP=123.113 转发给真机, 再把真机 AERE 回传给远端设备
|
||||
pub fn bridge(listen_ip: [u8; 4], listen_port: u16, real_ip: [u8; 4], secs: u64) {
|
||||
let listen_addr = format!("{}.{}.{}.{}:{}", listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3], listen_port);
|
||||
let listen = open_reuse(&listen_addr);
|
||||
let to_real = open_reuse("192.168.123.113:0"); // 源=113, 真机才应答
|
||||
let real_addr = format!("{}.{}.{}.{}:31772", real_ip[0], real_ip[1], real_ip[2], real_ip[3]);
|
||||
println!(" AECH 桥 @ {} ↔ {} (真机源 123.113)", listen_addr, real_addr);
|
||||
let mut pending: Option<std::net::SocketAddr> = None;
|
||||
let start = Instant::now();
|
||||
let mut n_fwd = 0u64; let mut n_back = 0u64;
|
||||
while start.elapsed().as_secs() < secs {
|
||||
let mut buf = [0u8; 2048];
|
||||
// 远端设备 -> 真机 (AECH)
|
||||
if let Ok((n, src)) = listen.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
if b.len() >= 8 && &b[0..4] == b"ENet" && g32(b, 4) == 2 && find(b, b"AECH") {
|
||||
pending = Some(src);
|
||||
let _ = to_real.send_to(b, &real_addr);
|
||||
n_fwd += 1;
|
||||
println!(" [{}] 桥: {} → 真机 (AECH #{})", chrono_t(start), src, n_fwd);
|
||||
}
|
||||
}
|
||||
// 真机 -> 远端设备 (AERE)
|
||||
if let Ok((n, src)) = to_real.recv_from(&mut buf) {
|
||||
let b = &buf[..n];
|
||||
if b.len() >= 8 && &b[0..4] == b"ENet" && g32(b, 4) == 2 {
|
||||
if let Some(p) = pending {
|
||||
let _ = listen.send_to(b, p);
|
||||
n_back += 1;
|
||||
println!(" [{}] 桥: 真机 → {} (AERE #{})", chrono_t(start), p, n_back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn chrono_t(start: Instant) -> String { format!("{:.1}s", start.elapsed().as_secs_f64()) }
|
||||
|
||||
fn patch_dev(b: &mut [u8], ip: [u8; 4], mac: [u8; 6]) {
|
||||
// 找 Butler IP LE (01 7b a8 c0) 替换 — 字段为小端存储: 192.168.123.200 → c8 7b a8 c0
|
||||
for i in 0..b.len().saturating_sub(4) {
|
||||
if b[i] == 0x01 && b[i + 1] == 0x7b && b[i + 2] == 0xa8 && b[i + 3] == 0xc0 {
|
||||
b[i] = ip[3]; b[i + 1] = ip[2]; b[i + 2] = ip[1]; b[i + 3] = ip[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 找 Butler MAC 替换
|
||||
for i in 0..b.len().saturating_sub(6) {
|
||||
if b[i] == 0x00 && b[i + 1] == 0x16 && b[i + 2] == 0x1c && b[i + 3] == 0xf1 && b[i + 4] == 0x2e && b[i + 5] == 0x39 {
|
||||
b[i..i + 6].copy_from_slice(&mac);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- T10/T11: RDM 探索 ----------
|
||||
pub fn t10_t11_rdm(secs: u64) -> Vec<Item> {
|
||||
let mut items = Vec::new();
|
||||
let s = open();
|
||||
let req = load_template("rdm_req");
|
||||
// T10: 发送 RDM 请求事件帧模板 (DISC 帧), 解析 Butler RDM 应答
|
||||
println!(" 发送 RDM 请求模板帧并监听 {}s…", secs);
|
||||
let deadline = Instant::now() + Duration::from_secs(secs);
|
||||
let mut uids: HashSet<String> = HashSet::new();
|
||||
let mut rdm_n = 0u64;
|
||||
let mut disc_n = 0u64;
|
||||
let mut req_seq = g32(&req, 0x22);
|
||||
while Instant::now() < deadline {
|
||||
let mut r = req.clone();
|
||||
p32(&mut r, 0x22, req_seq);
|
||||
req_seq = req_seq.wrapping_add(1);
|
||||
let _ = s.send_to(&r, BUTLER);
|
||||
for (_, b) in recv_window(&s, Duration::from_millis(60)) {
|
||||
match classify(&b) {
|
||||
Rx::RdmResp(_, rlen) => {
|
||||
rdm_n += 1;
|
||||
if rlen >= 17 && b[0x2A] == 0xAA { disc_n += 1; } // 发现脉冲
|
||||
if rlen >= 18 && b[0x2A] == 0xCC {
|
||||
let uid = hexs(&b[0x32..0x38]);
|
||||
uids.insert(uid);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
items.push(Item::new("T10", rdm_n > 0,
|
||||
format!("RDM应答={} (发现脉冲={}, 设备UID={})", rdm_n, disc_n,
|
||||
uids.iter().cloned().collect::<Vec<_>>().join(" "))));
|
||||
// T11: 写地址 (PID 0x0330) —— e:cue 私有封装下 PID 未知, 仅记录探索结论
|
||||
items.push(Item::new("T11", false,
|
||||
"写地址 SET DMX_START_ADDRESS: e:net 私有 PID 未确认, 需结合 GUI 写地址抓包比对 (见文档 §5.4)".to_string()));
|
||||
items
|
||||
}
|
||||
10
validation_report.json
Normal file
10
validation_report.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"target": "192.168.123.1",
|
||||
"results": [
|
||||
{ "id": "U1", "pass": true, "detail": "发现应答=2426 遥测=21662 RDM脉冲=208904 RDM111=278460 RDM119=0" },
|
||||
{ "id": "U2", "pass": false, "detail": "AECHA 认领交换=0 轮 (AEREP 已回)" },
|
||||
{ "id": "U3", "pass": true, "detail": "明文 DMX 帧接收=206609" },
|
||||
{ "id": "U4", "pass": false, "detail": "事件帧=0 ACK发送=0" },
|
||||
{ "id": "U5", "pass": true, "detail": "通道值: 首32CH=[175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144] 变化=205895 其他帧=0" }
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user