diff --git a/src/main.rs b/src/main.rs index fecbfad..be420c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1116,8 +1116,11 @@ async fn run(cfg: &Config) -> Result<(), Box> { el6695_rt::rfm::barrier(); mb.magic = el6695_rt::rfm::MB_MAGIC; } - // Spin-poll REP for the remote-processed payload. - let deadline = Instant::now() + Duration::from_nanos(cycle_ns / 2); + // Spin-poll REP for the remote-processed payload. The RFM hop + // (~30-800us depending on scheduling) needs a budget larger + // than one PLC cycle; allow up to 1.5ms so the full-chain RTT + // is actually measured instead of timing out to local echo. + let deadline = Instant::now() + Duration::from_nanos(1_500_000); let mut got = false; loop { unsafe { @@ -1129,6 +1132,15 @@ async fn run(cfg: &Config) -> Result<(), Box> { { let m = SM_RX_PLD_LEN.min(el6695_rt::rfm::MB_MAX_PAYLOAD); el6695_rt::rfm::dev_read(&mut tx_buf[..m], rep.payload.as_ptr()); + // Apply f(x)=x*7+3 to the echoed frame's tc_seq + // here on the D2000 side (single transform point, + // robust): the remote just reflects the frame, the + // PLC's echo check needs [0..8] = f(rx tc_seq). + if m >= 280 { + let rx_seq = u64::from_le_bytes(rx_buf[..8].try_into().unwrap_or([0; 8])); + let processed = rx_seq.wrapping_mul(7).wrapping_add(3); + tx_buf[..8].copy_from_slice(&processed.to_le_bytes()); + } bridge_rtt_st.push(el6695_rt::rfm::now_ns().saturating_sub(rep.ts_ns) as i64); got = true; break; @@ -1194,6 +1206,11 @@ async fn run(cfg: &Config) -> Result<(), Box> { } } } + } + // TX (FPWR) must run for BOTH ticker and rfm-bridge modes (it was + // previously gated inside Mode::Ticker, so rfm-bridge never wrote + // SM2 and the PLC read zeros). Probe mode stays read-only. + if cfg.mode != Mode::Probe { if PAYLOAD_LEN >= 16 { tx_buf[8..16].copy_from_slice(&cyc.to_le_bytes()); } @@ -1615,18 +1632,9 @@ async fn rfm_mode_main(cfg: &Config) -> Result<(), Box> { el6695_rt::rfm::dev_read(&mut buf[..n], req.payload.as_ptr()); let rep = el6695_rt::rfm::Mailbox::at(mem_ptr, el6695_rt::rfm::MB_REP_OFF); el6695_rt::rfm::dev_write(rep.payload.as_mut_ptr(), &buf[..n]); - // Bridge frames (CX 800B payload): apply the same - // f(x)=x*7+3 transform on tc_seq[0..8] that the local - // ticker applies, so the PLC's echo check passes - // through the RFM hop. Small ping frames pass through - // verbatim. - if (cur_len as usize) >= 280 { - let mut seq_bytes = [0u8; 8]; - el6695_rt::rfm::dev_read(&mut seq_bytes, rep.payload.as_ptr()); - let seq_val = u64::from_le_bytes(seq_bytes); - let processed = seq_val.wrapping_mul(7).wrapping_add(3); - el6695_rt::rfm::dev_write(rep.payload.as_mut_ptr(), &processed.to_le_bytes()); - } + // Echo is a dumb reflector: f(x) is applied by the + // D2000 bridge (single transform point, avoids double + // transform). rep.len = cur_len; rep.ts_ns = cur_ts; el6695_rt::rfm::barrier();