diff --git a/README.md b/README.md index 1cc8360..4934292 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,9 @@ Legend: | Common (base) | 6 | 4 | 67% | | Common editor/document | 23 | 12 | 52% | | Project manager | 5 | 3 | 60% | -| Board editor (PCB) | 22 | 15 | 68% | +| Board editor (PCB) | 22 | 16 | 73% | | Schematic editor (dedicated proto commands) | 0 | 0 | n/a | -| **Total** | **56** | **34** | **61%** | +| **Total** | **56** | **35** | **63%** | ### Common (base) @@ -104,7 +104,7 @@ Legend: | `SetBoardEnabledLayers` | Not yet | - | | `GetGraphicsDefaults` | Implemented | `KiCadClient::get_graphics_defaults_raw`, `KiCadClient::get_graphics_defaults` | | `GetBoardOrigin` | Implemented | `KiCadClient::get_board_origin` | -| `SetBoardOrigin` | Not yet | - | +| `SetBoardOrigin` | Implemented | `KiCadClient::set_board_origin` | | `GetNets` | Implemented | `KiCadClient::get_nets` | | `GetItemsByNet` | Implemented | `KiCadClient::get_items_by_net_raw`, `KiCadClient::get_items_by_net` | | `GetItemsByNetClass` | Implemented | `KiCadClient::get_items_by_net_class_raw`, `KiCadClient::get_items_by_net_class` | diff --git a/docs/TEST_CLI.md b/docs/TEST_CLI.md index 5d3c052..ecab9e4 100644 --- a/docs/TEST_CLI.md +++ b/docs/TEST_CLI.md @@ -119,6 +119,12 @@ Show drill origin: cargo run --bin kicad-ipc-cli -- board-origin --type drill ``` +Set board origin: + +```bash +cargo run --bin kicad-ipc-cli -- set-board-origin --type grid --x-nm 1000000 --y-nm 2000000 +``` + Refresh PCB editor: ```bash diff --git a/src/client.rs b/src/client.rs index a247035..0011ba8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -51,6 +51,7 @@ const CMD_SET_ACTIVE_LAYER: &str = "kiapi.board.commands.SetActiveLayer"; const CMD_GET_VISIBLE_LAYERS: &str = "kiapi.board.commands.GetVisibleLayers"; const CMD_SET_VISIBLE_LAYERS: &str = "kiapi.board.commands.SetVisibleLayers"; const CMD_GET_BOARD_ORIGIN: &str = "kiapi.board.commands.GetBoardOrigin"; +const CMD_SET_BOARD_ORIGIN: &str = "kiapi.board.commands.SetBoardOrigin"; const CMD_GET_BOARD_STACKUP: &str = "kiapi.board.commands.GetBoardStackup"; const CMD_GET_GRAPHICS_DEFAULTS: &str = "kiapi.board.commands.GetGraphicsDefaults"; const CMD_GET_BOARD_EDITOR_APPEARANCE_SETTINGS: &str = @@ -639,6 +640,23 @@ impl KiCadClient { }) } + pub async fn set_board_origin( + &self, + kind: BoardOriginKind, + origin: Vector2Nm, + ) -> Result<(), KiCadError> { + let board = self.current_board_document_proto().await?; + let command = board_commands::SetBoardOrigin { + board: Some(board), + r#type: board_origin_kind_to_proto(kind), + origin: Some(vector2_nm_to_proto(origin)), + }; + + self.send_command(envelope::pack_any(&command, CMD_SET_BOARD_ORIGIN)) + .await?; + Ok(()) + } + pub async fn get_selection_summary(&self) -> Result { let document = self.current_board_document_proto().await?; let command = common_commands::GetSelection { diff --git a/test-scripts/kicad-ipc-cli.rs b/test-scripts/kicad-ipc-cli.rs index 36db293..418ea1e 100644 --- a/test-scripts/kicad-ipc-cli.rs +++ b/test-scripts/kicad-ipc-cli.rs @@ -58,6 +58,11 @@ enum Command { BoardOrigin { kind: BoardOriginKind, }, + SetBoardOrigin { + kind: BoardOriginKind, + x_nm: i64, + y_nm: i64, + }, RefreshEditor { frame: EditorFrameType, }, @@ -337,6 +342,12 @@ async fn run() -> Result<(), KiCadError> { kind, origin.x_nm, origin.y_nm ); } + Command::SetBoardOrigin { kind, x_nm, y_nm } => { + client + .set_board_origin(kind, Vector2Nm { x_nm, y_nm }) + .await?; + println!("set_origin_kind={} x_nm={} y_nm={}", kind, x_nm, y_nm); + } Command::RefreshEditor { frame } => { client.refresh_editor(frame).await?; println!("refresh_editor=ok frame={}", frame); @@ -864,6 +875,54 @@ fn parse_args_from(mut args: Vec) -> Result<(CliConfig, Command), KiCadE } Command::BoardOrigin { kind } } + "set-board-origin" => { + let mut kind = BoardOriginKind::Grid; + let mut x_nm = None; + let mut y_nm = None; + let mut i = 1; + while i < args.len() { + match args[i].as_str() { + "--type" => { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for set-board-origin --type".to_string(), + })?; + kind = BoardOriginKind::from_str(value) + .map_err(|err| KiCadError::Config { reason: err })?; + i += 2; + } + "--x-nm" => { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for set-board-origin --x-nm".to_string(), + })?; + x_nm = Some(value.parse::().map_err(|err| KiCadError::Config { + reason: format!("invalid set-board-origin --x-nm `{value}`: {err}"), + })?); + i += 2; + } + "--y-nm" => { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for set-board-origin --y-nm".to_string(), + })?; + y_nm = Some(value.parse::().map_err(|err| KiCadError::Config { + reason: format!("invalid set-board-origin --y-nm `{value}`: {err}"), + })?); + i += 2; + } + _ => { + i += 1; + } + } + } + Command::SetBoardOrigin { + kind, + x_nm: x_nm.ok_or_else(|| KiCadError::Config { + reason: "set-board-origin requires `--x-nm `".to_string(), + })?, + y_nm: y_nm.ok_or_else(|| KiCadError::Config { + reason: "set-board-origin requires `--y-nm `".to_string(), + })?, + } + } "refresh-editor" => { let mut frame = EditorFrameType::PcbEditor; let mut i = 1; @@ -1240,7 +1299,7 @@ fn default_config() -> CliConfig { fn print_help() { println!( - "kicad-ipc-cli\n\nUSAGE:\n cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--client-name NAME] [--timeout-ms N] [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n open-docs [--type ] List open docs (default type: pcb)\n project-path Get current project path from open PCB docs\n board-open Exit non-zero if no PCB doc is open\n net-classes List project netclass definitions\n text-variables List text variables for current board document\n expand-text-variables Expand variables in provided text values\n Options: --text (repeatable)\n text-extents Measure text bounding box\n Options: --text \n text-as-shapes Convert text to rendered shapes\n Options: --text (repeatable)\n nets List board nets (requires one open PCB)\n netlist-pads Emit pad-level netlist data (with footprint context)\n items-by-id --id ... Show parsed details for specific item IDs\n item-bbox --id ... Show bounding boxes for item IDs\n hit-test --id --x-nm --y-nm [--tolerance-nm ]\n Hit-test one item at a point\n types-pcb List PCB KiCad object type IDs from proto enum\n items-raw --type-id ... Dump raw Any payloads for requested item type IDs\n items-raw-all-pcb [--debug] Dump all PCB item payloads across all PCB object types\n pad-shape-polygon --pad-id ... --layer-id [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id ... --layer-id ... [--debug]\n Check padstack shape presence matrix across layers\n title-block Show title block fields\n board-as-string Dump board as KiCad s-expression text\n selection-as-string Dump current selection as KiCad s-expression text\n stackup Show typed board stackup\n graphics-defaults Show typed graphics defaults\n appearance Show typed editor appearance settings\n netclass Show typed netclass map for current board nets\n proto-coverage-board-read Print board-read command coverage vs proto\n board-read-report [--out P] Write markdown board reconstruction report\n enabled-layers List enabled board layers\n active-layer Show active board layer\n set-active-layer --layer-id \n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id ...\n Set visible board layers\n board-origin [--type ] Show board origin (`grid` default, or `drill`)\n refresh-editor [--frame ] Refresh a specific editor frame (default: pcb)\n begin-commit Start staged commit and print commit ID\n end-commit --id [--action ] [--message ]\n End staged commit with commit/drop action\n selection-summary Show current selection item type counts\n selection-details Show parsed details for selected items\n selection-raw Show raw Any payload bytes for selected items\n smoke ping + version + board-open summary\n help Show help\n\nTYPES:\n schematic | symbol | pcb | footprint | drawing-sheet | project\n" + "kicad-ipc-cli\n\nUSAGE:\n cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--client-name NAME] [--timeout-ms N] [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n open-docs [--type ] List open docs (default type: pcb)\n project-path Get current project path from open PCB docs\n board-open Exit non-zero if no PCB doc is open\n net-classes List project netclass definitions\n text-variables List text variables for current board document\n expand-text-variables Expand variables in provided text values\n Options: --text (repeatable)\n text-extents Measure text bounding box\n Options: --text \n text-as-shapes Convert text to rendered shapes\n Options: --text (repeatable)\n nets List board nets (requires one open PCB)\n netlist-pads Emit pad-level netlist data (with footprint context)\n items-by-id --id ... Show parsed details for specific item IDs\n item-bbox --id ... Show bounding boxes for item IDs\n hit-test --id --x-nm --y-nm [--tolerance-nm ]\n Hit-test one item at a point\n types-pcb List PCB KiCad object type IDs from proto enum\n items-raw --type-id ... Dump raw Any payloads for requested item type IDs\n items-raw-all-pcb [--debug] Dump all PCB item payloads across all PCB object types\n pad-shape-polygon --pad-id ... --layer-id [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id ... --layer-id ... [--debug]\n Check padstack shape presence matrix across layers\n title-block Show title block fields\n board-as-string Dump board as KiCad s-expression text\n selection-as-string Dump current selection as KiCad s-expression text\n stackup Show typed board stackup\n graphics-defaults Show typed graphics defaults\n appearance Show typed editor appearance settings\n netclass Show typed netclass map for current board nets\n proto-coverage-board-read Print board-read command coverage vs proto\n board-read-report [--out P] Write markdown board reconstruction report\n enabled-layers List enabled board layers\n active-layer Show active board layer\n set-active-layer --layer-id \n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id ...\n Set visible board layers\n board-origin [--type ] Show board origin (`grid` default, or `drill`)\n set-board-origin --type --x-nm --y-nm \n Set board origin (`grid` or `drill`)\n refresh-editor [--frame ] Refresh a specific editor frame (default: pcb)\n begin-commit Start staged commit and print commit ID\n end-commit --id [--action ] [--message ]\n End staged commit with commit/drop action\n selection-summary Show current selection item type counts\n selection-details Show parsed details for selected items\n selection-raw Show raw Any payload bytes for selected items\n smoke ping + version + board-open summary\n help Show help\n\nTYPES:\n schematic | symbol | pcb | footprint | drawing-sheet | project\n" ); } @@ -1793,7 +1852,7 @@ fn hex_char(value: u8) -> char { #[cfg(test)] mod tests { use super::{parse_args_from, Command}; - use kicad_ipc::CommitAction; + use kicad_ipc::{BoardOriginKind, CommitAction}; #[test] fn parse_args_accepts_client_name_for_commit_flow() { @@ -1883,4 +1942,27 @@ mod tests { other => panic!("unexpected command variant: {other:?}"), } } + + #[test] + fn parse_args_parses_set_board_origin() { + let (_, command) = parse_args_from(vec![ + "set-board-origin".to_string(), + "--type".to_string(), + "drill".to_string(), + "--x-nm".to_string(), + "123".to_string(), + "--y-nm".to_string(), + "456".to_string(), + ]) + .expect("set-board-origin args should parse"); + + match command { + Command::SetBoardOrigin { kind, x_nm, y_nm } => { + assert_eq!(kind, BoardOriginKind::Drill); + assert_eq!(x_nm, 123); + assert_eq!(y_nm, 456); + } + other => panic!("unexpected command variant: {other:?}"), + } + } }