Skip to main content

altium_format/records/pcb/
connection.rs

1//! PCB connection (ratsnest) record type.
2//!
3//! Connections represent the unrouted "ratsnest" lines between
4//! pads that need to be connected but aren't yet routed.
5
6use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
7use std::io::{Read, Write};
8
9use crate::error::Result;
10use crate::traits::{FromBinary, ToBinary};
11use crate::types::Coord;
12
13/// PCB connection (ratsnest) record.
14///
15/// A connection represents an unrouted connection between two points
16/// on the PCB. These are shown as "ratsnest" lines in the editor.
17///
18/// Binary format (43 bytes):
19/// - Bytes 0-3: Net index (u16) + flags
20/// - Bytes 4-7: Unknown (0xFF padding)
21/// - Bytes 8-15: From point (X, Y as i32)
22/// - Bytes 16-23: To point (X, Y as i32)
23/// - Bytes 24-42: Component/pad indices and flags
24#[derive(Debug, Clone, Default)]
25pub struct PcbConnection {
26    /// Net index this connection belongs to.
27    pub net_index: u16,
28    /// From X coordinate.
29    pub from_x: Coord,
30    /// From Y coordinate.
31    pub from_y: Coord,
32    /// To X coordinate.
33    pub to_x: Coord,
34    /// To Y coordinate.
35    pub to_y: Coord,
36    /// From component index (-1 if none).
37    pub from_component_index: i16,
38    /// From pad index (-1 if none).
39    pub from_pad_index: i16,
40    /// To component index (-1 if none).
41    pub to_component_index: i16,
42    /// To pad index (-1 if none).
43    pub to_pad_index: i16,
44    /// Raw data for round-tripping unknown fields.
45    pub raw_data: Vec<u8>,
46}
47
48impl PcbConnection {
49    /// Binary record size.
50    pub const SIZE: usize = 43;
51}
52
53impl FromBinary for PcbConnection {
54    fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
55        // Read the entire record
56        let mut data = vec![0u8; Self::SIZE];
57        reader.read_exact(&mut data)?;
58
59        let mut cursor = std::io::Cursor::new(&data);
60
61        // Parse net index (bytes 0-1)
62        let net_index = cursor.read_u16::<LittleEndian>()?;
63
64        // Skip bytes 2-7 (flags and padding)
65        cursor.set_position(8);
66
67        // Parse from point (bytes 8-15)
68        let from_x = Coord::from_raw(cursor.read_i32::<LittleEndian>()?);
69        let from_y = Coord::from_raw(cursor.read_i32::<LittleEndian>()?);
70
71        // Parse to point (bytes 16-23)
72        let to_x = Coord::from_raw(cursor.read_i32::<LittleEndian>()?);
73        let to_y = Coord::from_raw(cursor.read_i32::<LittleEndian>()?);
74
75        // Skip to component/pad indices (bytes 28+)
76        cursor.set_position(28);
77
78        // Component/pad indices appear to be near the end
79        // Format varies, store raw data for now
80        let from_component_index = cursor.read_i16::<LittleEndian>().unwrap_or(-1);
81        cursor.set_position(32);
82        let from_pad_index = cursor.read_i16::<LittleEndian>().unwrap_or(-1);
83        cursor.set_position(35);
84        let to_component_index = cursor.read_i16::<LittleEndian>().unwrap_or(-1);
85        cursor.set_position(39);
86        let to_pad_index = cursor.read_i16::<LittleEndian>().unwrap_or(-1);
87
88        Ok(PcbConnection {
89            net_index,
90            from_x,
91            from_y,
92            to_x,
93            to_y,
94            from_component_index,
95            from_pad_index,
96            to_component_index,
97            to_pad_index,
98            raw_data: data,
99        })
100    }
101}
102
103impl ToBinary for PcbConnection {
104    fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
105        // If we have raw data, write it as-is for round-tripping
106        if self.raw_data.len() == Self::SIZE {
107            writer.write_all(&self.raw_data)?;
108        } else {
109            // Create new record
110            let mut data = vec![0u8; Self::SIZE];
111            let mut cursor = std::io::Cursor::new(&mut data[..]);
112
113            cursor.write_u16::<LittleEndian>(self.net_index)?;
114
115            // Fill padding with 0xFF
116            cursor.set_position(4);
117            cursor.write_all(&[0xFF; 4])?;
118
119            // Write coordinates
120            cursor.write_i32::<LittleEndian>(self.from_x.to_raw())?;
121            cursor.write_i32::<LittleEndian>(self.from_y.to_raw())?;
122            cursor.write_i32::<LittleEndian>(self.to_x.to_raw())?;
123            cursor.write_i32::<LittleEndian>(self.to_y.to_raw())?;
124
125            writer.write_all(&data)?;
126        }
127        Ok(())
128    }
129
130    fn binary_size(&self) -> usize {
131        Self::SIZE
132    }
133}