TwoFootBot-rust/.idea/copilotDiffState.xml

27 lines
25 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CopilotDiffPersistence">
<option name="pendingDiffs">
<map>
<entry key="/main (snapshot).rs">
<value>
<PendingDiffInfo>
<option name="filePath" value="/main (snapshot).rs" />
<option name="originalContent" value="mod policy;&#10;mod pingEsp32;&#10;mod command;&#10;&#10;use pingEsp32::Link;&#10;use std::time::Duration;&#10;&#10;fn main() -&gt; std::io::Result&lt;()&gt; {&#10; let mut link = Link::open_default()?;&#10; link.send_angles(&amp;[1.57;8])?;&#10;&#10; let mut cmd = command::spawn_listener();&#10;&#10; let mut i = 0;&#10; while i &gt; -1 {&#10; i += 1;&#10;&#10; if cmd.poll_from_terminal() {&#10; let state = cmd.load();&#10; let vec = command::state_to_vector(state);&#10; println!(&quot;[CommandState] {:?} (state=0b{:03b})&quot;, vec, state);&#10; }&#10;&#10; while let Some(msg) = link.recv_timeout(Duration::from_millis(20)) {&#10; if msg.is_data() {&#10; if let Some(mut obs) = msg.telemetry() {&#10; let state = cmd.load();&#10; let cmd_vec = command::state_to_vector(state);&#10; obs[6] = cmd_vec[0];&#10; obs[7] = cmd_vec[1];&#10; obs[8] = cmd_vec[2];&#10;&#10; println!(&quot;收到 36 维观测值(含指令): {:?}&quot;, obs);&#10; let action = policy::policy(obs).expect(&quot;TODO: panic message&quot;);&#10; link.send_angles(&amp;action)?;&#10; println!(&quot;[Action]:{:?}, 【{}】&quot;, action, i);&#10; }&#10; } else if msg.is_reply() {&#10; println!(&quot;[REPLY] {}&quot;, msg.text());&#10; } else {&#10; println!(&quot;[TEXT] {}&quot;, msg.text());&#10; }&#10; }&#10;&#10; std::thread::sleep(Duration::from_millis(5));&#10; }&#10;&#10; Ok(())&#10;}&#10;" />
<option name="updatedContent" value="mod policy;&#10;mod pingEsp32;&#10;mod command;&#10;&#10;use pingEsp32::Link;&#10;use std::time::Duration;&#10;&#10;fn main() -&gt; std::io::Result&lt;()&gt; {&#10; let mut link = Link::open_default()?;&#10; link.send_angles(&amp;[1.57;8])?;&#10;&#10; let mut cmd = command::spawn_listener();&#10;&#10; let mut i = 0;&#10; while i &gt; -1 {&#10; i += 1;&#10;&#10; if let Some(vec) = cmd.poll_from_terminal() {&#10; println!(&quot;[CommandState] {:?}&quot;, vec);&#10; if vec == [0.0, 0.0, 0.0] {&#10; println!(&quot;[Command] released -&gt; zero&quot;);&#10; }&#10; }&#10;&#10; while let Some(msg) = link.recv_timeout(Duration::from_millis(20)) {&#10; if msg.is_data() {&#10; if let Some(mut obs) = msg.telemetry() {&#10; let cmd_vec = cmd.current_vector();&#10; obs[6] = cmd_vec[0];&#10; obs[7] = cmd_vec[1];&#10; obs[8] = cmd_vec[2];&#10;&#10; println!(&quot;收到 36 维观测值(含指令): {:?}&quot;, obs);&#10; let action = policy::policy(obs).expect(&quot;TODO: panic message&quot;);&#10; link.send_angles(&amp;action)?;&#10; println!(&quot;[Action]:{:?}, 【{}】&quot;, action, i);&#10; }&#10; } else if msg.is_reply() {&#10; println!(&quot;[REPLY] {}&quot;, msg.text());&#10; } else {&#10; println!(&quot;[TEXT] {}&quot;, msg.text());&#10; }&#10; }&#10;&#10; std::thread::sleep(Duration::from_millis(5));&#10; }&#10;&#10; Ok(())&#10;}" />
</PendingDiffInfo>
</value>
</entry>
<entry key="/pingEsp32 (snapshot).rs">
<value>
<PendingDiffInfo>
<option name="filePath" value="/pingEsp32 (snapshot).rs" />
<option name="originalContent" value="use std::io::{BufRead, BufReader, Read, Write};&#10;use std::net::TcpStream;&#10;use std::sync::atomic::{AtomicBool, Ordering};&#10;use std::sync::mpsc::{self, Receiver, Sender, RecvTimeoutError};&#10;use std::sync::Arc;&#10;use std::thread::{self, JoinHandle};&#10;use std::time::Duration;&#10;&#10;use std::f32::consts::PI;&#10;&#10;pub const ESP32_IP: &amp;str = &quot;192.168.4.1&quot;;&#10;pub const ESP32_PORT: u16 = 3333;&#10;pub const NUM_SERVOS: usize = 8;&#10;&#10;const TELEMETRY_MAGIC: [u8; 2] = [0xA5, 0x5A];&#10;const TELEMETRY_FRAME_LEN: usize = 22;&#10;&#10;/// 从 ESP32 收到的一条消息(可包含 ASCII 文本,也可包含 36 维 IMU 数据)&#10;#[derive(Debug, Clone)]&#10;pub struct Esp32Message {&#10; text: String,&#10; telemetry: Option&lt;[f32; 36]&gt;,&#10;}&#10;&#10;impl Esp32Message {&#10; pub fn new_text(text: impl Into&lt;String&gt;) -&gt; Self {&#10; Self {&#10; text: text.into(),&#10; telemetry: None,&#10; }&#10; }&#10;&#10; pub fn new_telemetry(data: [f32; 36]) -&gt; Self {&#10; Self {&#10; text: &quot;IMU&quot;.to_string(),&#10; telemetry: Some(data),&#10; }&#10; }&#10;&#10; /// 是否是数据推送(二进制 IMU 帧)&#10; pub fn is_data(&amp;self) -&gt; bool { self.telemetry.is_some() }&#10;&#10; /// 是否是应答,比如 &quot;ESP32: OK ...&quot;&#10; pub fn is_reply(&amp;self) -&gt; bool {&#10; self.telemetry.is_none() &amp;&amp; self.text.starts_with(&quot;ESP32:&quot;)&#10; }&#10;&#10; pub fn text(&amp;self) -&gt; &amp;str { &amp;self.text }&#10;&#10; /// 返回 36 维 f32 数组;其余位置按 0.0 填充。&#10; pub fn telemetry(&amp;self) -&gt; Option&lt;[f32; 36]&gt; { self.telemetry }&#10;}&#10;&#10;// ==================== 双向连接 ====================&#10;&#10;pub struct Link {&#10; writer: TcpStream,&#10; rx: Receiver&lt;Esp32Message&gt;,&#10; stop: Arc&lt;AtomicBool&gt;,&#10; reader_job: Option&lt;JoinHandle&lt;()&gt;&gt;,&#10;}&#10;&#10;impl Link {&#10; /// 建立连接并启动后台接收线程&#10; pub fn open(ip: &amp;str, port: u16) -&gt; std::io::Result&lt;Self&gt; {&#10; let addr = format!(&quot;{}:{}&quot;, ip, port);&#10; println!(&quot;[*] 连接 {} ...&quot;, addr);&#10; println!(&quot;请连接上esp32的wifi热点后再运行本程序【ESP32_AP】&quot;);&#10; let stream = TcpStream::connect(&amp;addr)?;&#10; stream.set_nodelay(true)?;&#10; stream.set_read_timeout(Some(Duration::from_millis(200)))?;&#10; println!(&quot;[+] 已连接&quot;);&#10;&#10; let reader_stream = stream.try_clone()?;&#10; let (tx, rx): (Sender&lt;Esp32Message&gt;, Receiver&lt;Esp32Message&gt;) = mpsc::channel();&#10; let stop = Arc::new(AtomicBool::new(false));&#10;&#10; let reader_job = spawn_reader(reader_stream, tx, Arc::clone(&amp;stop));&#10;&#10; Ok(Self {&#10; writer: stream,&#10; rx,&#10; stop,&#10; reader_job: Some(reader_job),&#10; })&#10; }&#10;&#10; pub fn open_default() -&gt; std::io::Result&lt;Self&gt; {&#10; Self::open(ESP32_IP, ESP32_PORT)&#10; }&#10;&#10; // ---------- 发送 ----------&#10;&#10; /// 发送弧度列表&#10; pub fn send_angles(&amp;mut self, rads: &amp;[f32]) -&gt; std::io::Result&lt;()&gt; {&#10; let n = rads.len().min(NUM_SERVOS);&#10; let payload = rads[..n]&#10; .iter()&#10; .map(|r| format!(&quot;{:.4}&quot;, r.clamp(0.0, PI)))&#10; .collect::&lt;Vec&lt;_&gt;&gt;()&#10; .join(&quot;,&quot;);&#10; self.send_line(&amp;format!(&quot;RAD:{}&quot;, payload))&#10; }&#10;&#10; /// 发送角度列表&#10; pub fn send_degrees(&amp;mut self, degs: &amp;[f32]) -&gt; std::io::Result&lt;()&gt; {&#10; let n = degs.len().min(NUM_SERVOS);&#10; let payload = degs[..n]&#10; .iter()&#10; .map(|d| format!(&quot;{:.2}&quot;, d.clamp(0.0, 180.0)))&#10; .collect::&lt;Vec&lt;_&gt;&gt;()&#10; .join(&quot;,&quot;);&#10; self.send_line(&amp;format!(&quot;DEG:{}&quot;, payload))&#10; }&#10;&#10; /// 发送任意一行(自动补 '\n')&#10; pub fn send_line(&amp;mut self, line: &amp;str) -&gt; std::io::Result&lt;()&gt; {&#10; self.writer.write_all(line.as_bytes())?;&#10; self.writer.write_all(b&quot;\n&quot;)?;&#10; self.writer.flush()&#10; }&#10;&#10; // ---------- 接收 ----------&#10;&#10; /// 阻塞等待一条消息(最多等 timeout)&#10; pub fn recv_timeout(&amp;self, timeout: Duration) -&gt; Option&lt;Esp32Message&gt; {&#10; match self.rx.recv_timeout(timeout) {&#10; Ok(m) =&gt; Some(m),&#10; Err(RecvTimeoutError::Timeout) =&gt; None,&#10; Err(RecvTimeoutError::Disconnected) =&gt; None,&#10; }&#10; }&#10;&#10; /// 非阻塞:立刻返回当前队列里有没有消息&#10; pub fn try_recv(&amp;self) -&gt; Option&lt;Esp32Message&gt; {&#10; self.rx.try_recv().ok()&#10; }&#10;}&#10;&#10;impl Drop for Link {&#10; fn drop(&amp;mut self) {&#10; self.stop.store(true, Ordering::SeqCst);&#10; let _ = self.writer.shutdown(std::net::Shutdown::Both);&#10; if let Some(job) = self.reader_job.take() {&#10; let _ = job.join();&#10; }&#10; }&#10;}&#10;&#10;// ==================== 后台接收线程 ====================&#10;&#10;fn spawn_reader(&#10; stream: TcpStream,&#10; tx: Sender&lt;Esp32Message&gt;,&#10; stop: Arc&lt;AtomicBool&gt;,&#10;) -&gt; JoinHandle&lt;()&gt; {&#10; thread::spawn(move || {&#10; let mut stream = stream;&#10; let mut pending = Vec::new();&#10; let mut buffer = [0u8; 1024];&#10;&#10; loop {&#10; if stop.load(Ordering::SeqCst) {&#10; break;&#10; }&#10;&#10; match stream.read(&amp;mut buffer) {&#10; Ok(0) =&gt; break,&#10; Ok(n) =&gt; {&#10; pending.extend_from_slice(&amp;buffer[..n]);&#10;&#10; loop {&#10; if pending.len() &gt;= TELEMETRY_FRAME_LEN&#10; &amp;&amp; pending[..2] == TELEMETRY_MAGIC&#10; {&#10; let frame = &amp;pending[..TELEMETRY_FRAME_LEN];&#10; if let Some(data) = parse_telemetry_frame(frame) {&#10; if tx.send(Esp32Message::new_telemetry(data)).is_err() {&#10; return;&#10; }&#10; pending.drain(..TELEMETRY_FRAME_LEN);&#10; continue;&#10; }&#10;&#10; // CRC 错误,丢掉首字节继续找下一帧&#10; pending.drain(..1);&#10; continue;&#10; }&#10;&#10; if let Some(newline_pos) = pending.iter().position(|&amp;b| b == b'\n') {&#10; let line_bytes = &amp;pending[..newline_pos];&#10; // 只处理纯 ASCII 文本; 非法 UTF-8 也不阻断连接&#10; let line = String::from_utf8_lossy(line_bytes).trim().to_string();&#10; if !line.is_empty() {&#10; if tx.send(Esp32Message::new_text(line)).is_err() {&#10; return;&#10; }&#10; }&#10; pending.drain(..=newline_pos);&#10; continue;&#10; }&#10;&#10; // 还没凑齐完整帧/整行,等下一批数据&#10; break;&#10; }&#10;&#10; if pending.len() &gt; 65536 {&#10; pending.clear();&#10; }&#10; }&#10; Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock || e.kind() == std::io::ErrorKind::TimedOut =&gt; {&#10; continue;&#10; }&#10; Err(_) =&gt; break,&#10; }&#10; }&#10;&#10; println!(&quot;[*] 接收线程退出&quot;);&#10; })&#10;}&#10;&#10;fn parse_telemetry_frame(frame: &amp;[u8]) -&gt; Option&lt;[f32; 36]&gt; {&#10; if frame.len() != TELEMETRY_FRAME_LEN {&#10; return None;&#10; }&#10; if frame[..2] != TELEMETRY_MAGIC {&#10; return None;&#10; }&#10;&#10; let expected_crc = u16::from_be_bytes([frame[20], frame[21]]);&#10; let actual_crc = calculate_crc16_ccitt(&amp;frame[..20]);&#10; if actual_crc != expected_crc {&#10; return None;&#10; }&#10;&#10; let yaw = i16::from_be_bytes([frame[2], frame[3]]) as f32 / 100.0;&#10; let pitch = i16::from_be_bytes([frame[4], frame[5]]) as f32 / 100.0;&#10; let roll = i16::from_be_bytes([frame[6], frame[7]]) as f32 / 100.0;&#10; let ax = i16::from_be_bytes([frame[8], frame[9]]) as f32;&#10; let ay = i16::from_be_bytes([frame[10], frame[11]]) as f32;&#10; let az = i16::from_be_bytes([frame[12], frame[13]]) as f32;&#10; let gx = i16::from_be_bytes([frame[14], frame[15]]) as f32;&#10; let gy = i16::from_be_bytes([frame[16], frame[17]]) as f32;&#10; let gz = i16::from_be_bytes([frame[18], frame[19]]) as f32;&#10;&#10; let mut values = [0.0f32; 36];&#10; let payload = [yaw, pitch, roll, ax, ay, az, gx, gy, gz];&#10; for (idx, value) in payload.iter().enumerate() {&#10; values[idx] = *value;&#10; }&#10; Some(values)&#10;}&#10;&#10;fn calculate_crc16_ccitt(data: &amp;[u8]) -&gt; u16 {&#10; let mut crc = 0xFFFFu16;&#10; for &amp;byte in data {&#10; crc ^= (byte as u16) &lt;&lt; 8;&#10; for _ in 0..8 {&#10; if crc &amp; 0x8000 != 0 {&#10; crc = ((crc &lt;&lt; 1) ^ 0x1021) &amp; 0xFFFF;&#10; } else {&#10; crc = (crc &lt;&lt; 1) &amp; 0xFFFF;&#10; }&#10; }&#10; }&#10; crc&#10;}&#10;&#10;" />
<option name="updatedContent" value="use std::io::{BufRead, BufReader, Read, Write};&#10;use std::net::TcpStream;&#10;use std::sync::atomic::{AtomicBool, Ordering};&#10;use std::sync::mpsc::{self, Receiver, Sender, RecvTimeoutError};&#10;use std::sync::Arc;&#10;use std::thread::{self, JoinHandle};&#10;use std::time::Duration;&#10;&#10;use std::f32::consts::PI;&#10;&#10;pub const ESP32_IP: &amp;str = &quot;192.168.4.1&quot;;&#10;pub const ESP32_PORT: u16 = 3333;&#10;pub const NUM_SERVOS: usize = 8;&#10;&#10;const TELEMETRY_MAGIC: [u8; 2] = [0xA5, 0x5A];&#10;const TELEMETRY_FRAME_LEN: usize = 22;&#10;&#10;/// 从 ESP32 收到的一条消息(可包含 ASCII 文本,也可包含 36 维 IMU 数据)&#10;#[derive(Debug, Clone)]&#10;pub struct Esp32Message {&#10; text: String,&#10; telemetry: Option&lt;[f32; 36]&gt;,&#10;}&#10;&#10;impl Esp32Message {&#10; pub fn new_text(text: impl Into&lt;String&gt;) -&gt; Self {&#10; Self {&#10; text: text.into(),&#10; telemetry: None,&#10; }&#10; }&#10;&#10; pub fn new_telemetry(data: [f32; 36]) -&gt; Self {&#10; Self {&#10; text: &quot;IMU&quot;.to_string(),&#10; telemetry: Some(data),&#10; }&#10; }&#10;&#10; /// 是否是数据推送(二进制 IMU 帧)&#10; pub fn is_data(&amp;self) -&gt; bool { self.telemetry.is_some() }&#10;&#10; /// 是否是应答,比如 &quot;ESP32: OK ...&quot;&#10; pub fn is_reply(&amp;self) -&gt; bool {&#10; self.telemetry.is_none() &amp;&amp; self.text.starts_with(&quot;ESP32:&quot;)&#10; }&#10;&#10; pub fn text(&amp;self) -&gt; &amp;str { &amp;self.text }&#10;&#10; /// 返回 36 维 f32 数组;其余位置按 0.0 填充。&#10; pub fn telemetry(&amp;self) -&gt; Option&lt;[f32; 36]&gt; { self.telemetry }&#10;}&#10;&#10;// ==================== 双向连接 ====================&#10;&#10;pub struct Link {&#10; writer: TcpStream,&#10; rx: Receiver&lt;Esp32Message&gt;,&#10; stop: Arc&lt;AtomicBool&gt;,&#10; reader_job: Option&lt;JoinHandle&lt;()&gt;&gt;,&#10;}&#10;&#10;impl Link {&#10; /// 建立连接并启动后台接收线程&#10; pub fn open(ip: &amp;str, port: u16) -&gt; std::io::Result&lt;Self&gt; {&#10; let addr = format!(&quot;{}:{}&quot;, ip, port);&#10; println!(&quot;[*] 连接 {} ...&quot;, addr);&#10; println!(&quot;请连接上esp32的wifi热点后再运行本程序【ESP32_AP】&quot;);&#10; let stream = TcpStream::connect(&amp;addr)?;&#10; stream.set_nodelay(true)?;&#10; stream.set_read_timeout(Some(Duration::from_millis(200)))?;&#10; println!(&quot;[+] 已连接&quot;);&#10;&#10; let reader_stream = stream.try_clone()?;&#10; let (tx, rx): (Sender&lt;Esp32Message&gt;, Receiver&lt;Esp32Message&gt;) = mpsc::channel();&#10; let stop = Arc::new(AtomicBool::new(false));&#10;&#10; let reader_job = spawn_reader(reader_stream, tx, Arc::clone(&amp;stop));&#10;&#10; Ok(Self {&#10; writer: stream,&#10; rx,&#10; stop,&#10; reader_job: Some(reader_job),&#10; })&#10; }&#10;&#10; pub fn open_default() -&gt; std::io::Result&lt;Self&gt; {&#10; Self::open(ESP32_IP, ESP32_PORT)&#10; }&#10;&#10; // ---------- 发送 ----------&#10;&#10; /// 发送弧度列表&#10; pub fn send_angles(&amp;mut self, rads: &amp;[f32]) -&gt; std::io::Result&lt;()&gt; {&#10; let n = rads.len().min(NUM_SERVOS);&#10; let payload = rads[..n]&#10; .iter()&#10; .map(|r| format!(&quot;{:.4}&quot;, r.clamp(0.0, PI)))&#10; .collect::&lt;Vec&lt;_&gt;&gt;()&#10; .join(&quot;,&quot;);&#10; self.send_line(&amp;format!(&quot;RAD:{}&quot;, payload))&#10; }&#10;&#10; /// 发送角度列表&#10; pub fn send_degrees(&amp;mut self, degs: &amp;[f32]) -&gt; std::io::Result&lt;()&gt; {&#10; let n = degs.len().min(NUM_SERVOS);&#10; let payload = degs[..n]&#10; .iter()&#10; .map(|d| format!(&quot;{:.2}&quot;, d.clamp(0.0, 180.0)))&#10; .collect::&lt;Vec&lt;_&gt;&gt;()&#10; .join(&quot;,&quot;);&#10; self.send_line(&amp;format!(&quot;DEG:{}&quot;, payload))&#10; }&#10;&#10; /// 发送任意一行(自动补 '\n')&#10; pub fn send_line(&amp;mut self, line: &amp;str) -&gt; std::io::Result&lt;()&gt; {&#10; self.writer.write_all(line.as_bytes())?;&#10; self.writer.write_all(b&quot;\n&quot;)?;&#10; self.writer.flush()&#10; }&#10;&#10; // ---------- 接收 ----------&#10;&#10; /// 阻塞等待一条消息(最多等 timeout)&#10; pub fn recv_timeout(&amp;self, timeout: Duration) -&gt; Option&lt;Esp32Message&gt; {&#10; match self.rx.recv_timeout(timeout) {&#10; Ok(m) =&gt; Some(m),&#10; Err(RecvTimeoutError::Timeout) =&gt; None,&#10; Err(RecvTimeoutError::Disconnected) =&gt; None,&#10; }&#10; }&#10;&#10; /// 非阻塞:立刻返回当前队列里有没有消息&#10; pub fn try_recv(&amp;self) -&gt; Option&lt;Esp32Message&gt; {&#10; self.rx.try_recv().ok()&#10; }&#10;}&#10;&#10;impl Drop for Link {&#10; fn drop(&amp;mut self) {&#10; self.stop.store(true, Ordering::SeqCst);&#10; let _ = self.writer.shutdown(std::net::Shutdown::Both);&#10; if let Some(job) = self.reader_job.take() {&#10; let _ = job.join();&#10; }&#10; }&#10;}&#10;&#10;// ==================== 后台接收线程 ====================&#10;&#10;fn spawn_reader(&#10; stream: TcpStream,&#10; tx: Sender&lt;Esp32Message&gt;,&#10; stop: Arc&lt;AtomicBool&gt;,&#10;) -&gt; JoinHandle&lt;()&gt; {&#10; thread::spawn(move || {&#10; let mut stream = stream;&#10; let mut pending = Vec::new();&#10; let mut buffer = [0u8; 1024];&#10;&#10; loop {&#10; if stop.load(Ordering::SeqCst) {&#10; break;&#10; }&#10;&#10; match stream.read(&amp;mut buffer) {&#10; Ok(0) =&gt; break,&#10; Ok(n) =&gt; {&#10; pending.extend_from_slice(&amp;buffer[..n]);&#10;&#10; loop {&#10; if pending.len() &gt;= TELEMETRY_FRAME_LEN&#10; &amp;&amp; pending[..2] == TELEMETRY_MAGIC&#10; {&#10; let frame = &amp;pending[..TELEMETRY_FRAME_LEN];&#10; if let Some(data) = parse_telemetry_frame(frame) {&#10; if tx.send(Esp32Message::new_telemetry(data)).is_err() {&#10; return;&#10; }&#10; pending.drain(..TELEMETRY_FRAME_LEN);&#10; continue;&#10; }&#10;&#10; // CRC 错误,丢掉首字节继续找下一帧&#10; pending.drain(..1);&#10; continue;&#10; }&#10;&#10; if let Some(newline_pos) = pending.iter().position(|&amp;b| b == b'\n') {&#10; let line_bytes = &amp;pending[..newline_pos];&#10; // 只处理纯 ASCII 文本; 非法 UTF-8 也不阻断连接&#10; let line = String::from_utf8_lossy(line_bytes).trim().to_string();&#10; if !line.is_empty() {&#10; if tx.send(Esp32Message::new_text(line)).is_err() {&#10; return;&#10; }&#10; }&#10; pending.drain(..=newline_pos);&#10; continue;&#10; }&#10;&#10; // 还没凑齐完整帧/整行,等下一批数据&#10; break;&#10; }&#10;&#10; if pending.len() &gt; 65536 {&#10; pending.clear();&#10; }&#10; }&#10; Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock || e.kind() == std::io::ErrorKind::TimedOut =&gt; {&#10; continue;&#10; }&#10; Err(_) =&gt; break,&#10; }&#10; }&#10;&#10; println!(&quot;[*] 接收线程退出&quot;);&#10; })&#10;}&#10;&#10;fn parse_telemetry_frame(frame: &amp;[u8]) -&gt; Option&lt;[f32; 36]&gt; {&#10; if frame.len() != TELEMETRY_FRAME_LEN {&#10; return None;&#10; }&#10; if frame[..2] != TELEMETRY_MAGIC {&#10; return None;&#10; }&#10;&#10; let expected_crc = u16::from_be_bytes([frame[20], frame[21]]);&#10; let actual_crc = calculate_crc16_ccitt(&amp;frame[..20]);&#10; if actual_crc != expected_crc {&#10; return None;&#10; }&#10;&#10; let yaw = i16::from_be_bytes([frame[2], frame[3]]) as f32 / 100.0;&#10; let pitch = i16::from_be_bytes([frame[4], frame[5]]) as f32 / 100.0;&#10; let roll = i16::from_be_bytes([frame[6], frame[7]]) as f32 / 100.0;&#10; let ax = i16::from_be_bytes([frame[8], frame[9]]) as f32;&#10; let ay = i16::from_be_bytes([frame[10], frame[11]]) as f32;&#10; let az = i16::from_be_bytes([frame[12], frame[13]]) as f32;&#10; let gx = i16::from_be_bytes([frame[14], frame[15]]) as f32;&#10; let gy = i16::from_be_bytes([frame[16], frame[17]]) as f32;&#10; let gz = i16::from_be_bytes([frame[18], frame[19]]) as f32;&#10;&#10; let mut values = [0.0f32; 36];&#10; let payload = [yaw, pitch, roll, ax, ay, az, gx, gy, gz];&#10; for (idx, value) in payload.iter().enumerate() {&#10; values[idx] = *value;&#10; }&#10;&#10; // 对于传回来的向量,按照要求将前三位除以 10000&#10; for i in 0..3 {&#10; values[i] /= 10000.0;&#10; }&#10;&#10; Some(values)&#10;}&#10;&#10;fn calculate_crc16_ccitt(data: &amp;[u8]) -&gt; u16 {&#10; let mut crc = 0xFFFFu16;&#10; for &amp;byte in data {&#10; crc ^= (byte as u16) &lt;&lt; 8;&#10; for _ in 0..8 {&#10; if crc &amp; 0x8000 != 0 {&#10; crc = ((crc &lt;&lt; 1) ^ 0x1021) &amp; 0xFFFF;&#10; } else {&#10; crc = (crc &lt;&lt; 1) &amp; 0xFFFF;&#10; }&#10; }&#10; }&#10; crc&#10;}&#10;&#10;" />
</PendingDiffInfo>
</value>
</entry>
</map>
</option>
</component>
</project>