feat(client): add RefreshEditor API and CLI command
This commit is contained in:
parent
aa406927a5
commit
331910444d
|
|
@ -39,11 +39,11 @@ Legend:
|
||||||
| Section | Proto Commands | Implemented | Coverage |
|
| Section | Proto Commands | Implemented | Coverage |
|
||||||
| --- | ---: | ---: | ---: |
|
| --- | ---: | ---: | ---: |
|
||||||
| Common (base) | 6 | 4 | 67% |
|
| Common (base) | 6 | 4 | 67% |
|
||||||
| Common editor/document | 23 | 11 | 48% |
|
| Common editor/document | 23 | 12 | 52% |
|
||||||
| Project manager | 5 | 3 | 60% |
|
| Project manager | 5 | 3 | 60% |
|
||||||
| Board editor (PCB) | 22 | 13 | 59% |
|
| Board editor (PCB) | 22 | 13 | 59% |
|
||||||
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
||||||
| **Total** | **56** | **31** | **55%** |
|
| **Total** | **56** | **32** | **57%** |
|
||||||
|
|
||||||
### Common (base)
|
### Common (base)
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ Legend:
|
||||||
|
|
||||||
| KiCad Command | Status | Rust API |
|
| KiCad Command | Status | Rust API |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `RefreshEditor` | Not yet | - |
|
| `RefreshEditor` | Implemented | `KiCadClient::refresh_editor` |
|
||||||
| `GetOpenDocuments` | Implemented | `KiCadClient::get_open_documents`, `KiCadClient::get_current_project_path`, `KiCadClient::has_open_board` |
|
| `GetOpenDocuments` | Implemented | `KiCadClient::get_open_documents`, `KiCadClient::get_current_project_path`, `KiCadClient::has_open_board` |
|
||||||
| `SaveDocument` | Not yet | - |
|
| `SaveDocument` | Not yet | - |
|
||||||
| `SaveCopyOfDocument` | Not yet | - |
|
| `SaveCopyOfDocument` | Not yet | - |
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,14 @@ Show drill origin:
|
||||||
cargo run --bin kicad-ipc-cli -- board-origin --type drill
|
cargo run --bin kicad-ipc-cli -- board-origin --type drill
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Refresh PCB editor:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin kicad-ipc-cli -- refresh-editor --frame pcb
|
||||||
|
```
|
||||||
|
|
||||||
|
If your KiCad build does not expose this handler yet, this call may return `AS_UNHANDLED`.
|
||||||
|
|
||||||
Start a staged commit and print commit ID:
|
Start a staged commit and print commit ID:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use crate::model::board::{
|
||||||
Vector2Nm,
|
Vector2Nm,
|
||||||
};
|
};
|
||||||
use crate::model::common::{
|
use crate::model::common::{
|
||||||
CommitAction, CommitSession, DocumentSpecifier, DocumentType, ItemBoundingBox,
|
CommitAction, CommitSession, DocumentSpecifier, DocumentType, EditorFrameType, ItemBoundingBox,
|
||||||
ItemHitTestResult, PcbObjectTypeCode, ProjectInfo, SelectionItemDetail, SelectionSummary,
|
ItemHitTestResult, PcbObjectTypeCode, ProjectInfo, SelectionItemDetail, SelectionSummary,
|
||||||
SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents,
|
SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents,
|
||||||
TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
||||||
|
|
@ -42,6 +42,7 @@ const CMD_GET_TEXT_VARIABLES: &str = "kiapi.common.commands.GetTextVariables";
|
||||||
const CMD_EXPAND_TEXT_VARIABLES: &str = "kiapi.common.commands.ExpandTextVariables";
|
const CMD_EXPAND_TEXT_VARIABLES: &str = "kiapi.common.commands.ExpandTextVariables";
|
||||||
const CMD_GET_TEXT_EXTENTS: &str = "kiapi.common.commands.GetTextExtents";
|
const CMD_GET_TEXT_EXTENTS: &str = "kiapi.common.commands.GetTextExtents";
|
||||||
const CMD_GET_TEXT_AS_SHAPES: &str = "kiapi.common.commands.GetTextAsShapes";
|
const CMD_GET_TEXT_AS_SHAPES: &str = "kiapi.common.commands.GetTextAsShapes";
|
||||||
|
const CMD_REFRESH_EDITOR: &str = "kiapi.common.commands.RefreshEditor";
|
||||||
const CMD_GET_OPEN_DOCUMENTS: &str = "kiapi.common.commands.GetOpenDocuments";
|
const CMD_GET_OPEN_DOCUMENTS: &str = "kiapi.common.commands.GetOpenDocuments";
|
||||||
const CMD_GET_NETS: &str = "kiapi.board.commands.GetNets";
|
const CMD_GET_NETS: &str = "kiapi.board.commands.GetNets";
|
||||||
const CMD_GET_BOARD_ENABLED_LAYERS: &str = "kiapi.board.commands.GetBoardEnabledLayers";
|
const CMD_GET_BOARD_ENABLED_LAYERS: &str = "kiapi.board.commands.GetBoardEnabledLayers";
|
||||||
|
|
@ -293,6 +294,17 @@ impl KiCadClient {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn refresh_editor(&self, frame: EditorFrameType) -> Result<(), KiCadError> {
|
||||||
|
let command = envelope::pack_any(
|
||||||
|
&common_commands::RefreshEditor {
|
||||||
|
frame: frame.to_proto(),
|
||||||
|
},
|
||||||
|
CMD_REFRESH_EDITOR,
|
||||||
|
);
|
||||||
|
self.send_command(command).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_version(&self) -> Result<VersionInfo, KiCadError> {
|
pub async fn get_version(&self) -> Result<VersionInfo, KiCadError> {
|
||||||
let command = envelope::pack_any(&common_commands::GetVersion {}, CMD_GET_VERSION);
|
let command = envelope::pack_any(&common_commands::GetVersion {}, CMD_GET_VERSION);
|
||||||
let response = self.send_command(command).await?;
|
let response = self.send_command(command).await?;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ pub use crate::model::board::{
|
||||||
Vector2Nm,
|
Vector2Nm,
|
||||||
};
|
};
|
||||||
pub use crate::model::common::{
|
pub use crate::model::common::{
|
||||||
CommitAction, CommitSession, DocumentSpecifier, DocumentType, ItemBoundingBox,
|
CommitAction, CommitSession, DocumentSpecifier, DocumentType, EditorFrameType, ItemBoundingBox,
|
||||||
ItemHitTestResult, PcbObjectTypeCode, SelectionItemDetail, SelectionSummary,
|
ItemHitTestResult, PcbObjectTypeCode, SelectionItemDetail, SelectionSummary,
|
||||||
SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents,
|
SelectionTypeCount, TextAsShapesEntry, TextAttributesSpec, TextBoxSpec, TextExtents,
|
||||||
TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
TextHorizontalAlignment, TextObjectSpec, TextShape, TextShapeGeometry, TextSpec,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,65 @@ pub struct VersionInfo {
|
||||||
pub full_version: String,
|
pub full_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum EditorFrameType {
|
||||||
|
ProjectManager,
|
||||||
|
SchematicEditor,
|
||||||
|
PcbEditor,
|
||||||
|
SpiceSimulator,
|
||||||
|
SymbolEditor,
|
||||||
|
FootprintEditor,
|
||||||
|
DrawingSheetEditor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditorFrameType {
|
||||||
|
pub(crate) fn to_proto(self) -> i32 {
|
||||||
|
match self {
|
||||||
|
Self::ProjectManager => common_types::FrameType::FtProjectManager as i32,
|
||||||
|
Self::SchematicEditor => common_types::FrameType::FtSchematicEditor as i32,
|
||||||
|
Self::PcbEditor => common_types::FrameType::FtPcbEditor as i32,
|
||||||
|
Self::SpiceSimulator => common_types::FrameType::FtSpiceSimulator as i32,
|
||||||
|
Self::SymbolEditor => common_types::FrameType::FtSymbolEditor as i32,
|
||||||
|
Self::FootprintEditor => common_types::FrameType::FtFootprintEditor as i32,
|
||||||
|
Self::DrawingSheetEditor => common_types::FrameType::FtDrawingSheetEditor as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for EditorFrameType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let value = match self {
|
||||||
|
Self::ProjectManager => "project-manager",
|
||||||
|
Self::SchematicEditor => "schematic",
|
||||||
|
Self::PcbEditor => "pcb",
|
||||||
|
Self::SpiceSimulator => "spice",
|
||||||
|
Self::SymbolEditor => "symbol",
|
||||||
|
Self::FootprintEditor => "footprint",
|
||||||
|
Self::DrawingSheetEditor => "drawing-sheet",
|
||||||
|
};
|
||||||
|
write!(f, "{value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for EditorFrameType {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||||
|
match value {
|
||||||
|
"project-manager" => Ok(Self::ProjectManager),
|
||||||
|
"schematic" => Ok(Self::SchematicEditor),
|
||||||
|
"pcb" => Ok(Self::PcbEditor),
|
||||||
|
"spice" => Ok(Self::SpiceSimulator),
|
||||||
|
"symbol" => Ok(Self::SymbolEditor),
|
||||||
|
"footprint" => Ok(Self::FootprintEditor),
|
||||||
|
"drawing-sheet" => Ok(Self::DrawingSheetEditor),
|
||||||
|
_ => Err(format!(
|
||||||
|
"unknown frame `{value}`; expected one of: project-manager, schematic, pcb, spice, symbol, footprint, drawing-sheet"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum DocumentType {
|
pub enum DocumentType {
|
||||||
Schematic,
|
Schematic,
|
||||||
|
|
@ -336,7 +395,7 @@ impl std::fmt::Display for ItemHitTestResult {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::CommitAction;
|
use super::{CommitAction, EditorFrameType};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -349,4 +408,21 @@ mod tests {
|
||||||
fn commit_action_rejects_unknown_values() {
|
fn commit_action_rejects_unknown_values() {
|
||||||
assert!(CommitAction::from_str("rollback").is_err());
|
assert!(CommitAction::from_str("rollback").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn editor_frame_type_parses_known_values() {
|
||||||
|
assert_eq!(
|
||||||
|
EditorFrameType::from_str("pcb"),
|
||||||
|
Ok(EditorFrameType::PcbEditor)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
EditorFrameType::from_str("project-manager"),
|
||||||
|
Ok(EditorFrameType::ProjectManager)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn editor_frame_type_rejects_unknown_values() {
|
||||||
|
assert!(EditorFrameType::from_str("layout").is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use kicad_ipc::{
|
use kicad_ipc::{
|
||||||
BoardOriginKind, ClientBuilder, CommitAction, CommitSession, DocumentType, KiCadClient,
|
BoardOriginKind, ClientBuilder, CommitAction, CommitSession, DocumentType, EditorFrameType,
|
||||||
KiCadError, PadstackPresenceState, PcbObjectTypeCode, TextObjectSpec, TextShapeGeometry,
|
KiCadClient, KiCadError, PadstackPresenceState, PcbObjectTypeCode, TextObjectSpec,
|
||||||
TextSpec, Vector2Nm,
|
TextShapeGeometry, TextSpec, Vector2Nm,
|
||||||
};
|
};
|
||||||
|
|
||||||
const REPORT_MAX_PAD_NET_ROWS: usize = 2_000;
|
const REPORT_MAX_PAD_NET_ROWS: usize = 2_000;
|
||||||
|
|
@ -52,6 +52,9 @@ enum Command {
|
||||||
BoardOrigin {
|
BoardOrigin {
|
||||||
kind: BoardOriginKind,
|
kind: BoardOriginKind,
|
||||||
},
|
},
|
||||||
|
RefreshEditor {
|
||||||
|
frame: EditorFrameType,
|
||||||
|
},
|
||||||
BeginCommit,
|
BeginCommit,
|
||||||
EndCommit {
|
EndCommit {
|
||||||
id: String,
|
id: String,
|
||||||
|
|
@ -320,6 +323,10 @@ async fn run() -> Result<(), KiCadError> {
|
||||||
kind, origin.x_nm, origin.y_nm
|
kind, origin.x_nm, origin.y_nm
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Command::RefreshEditor { frame } => {
|
||||||
|
client.refresh_editor(frame).await?;
|
||||||
|
println!("refresh_editor=ok frame={}", frame);
|
||||||
|
}
|
||||||
Command::BeginCommit => {
|
Command::BeginCommit => {
|
||||||
let session = client.begin_commit().await?;
|
let session = client.begin_commit().await?;
|
||||||
println!("commit_id={}", session.id);
|
println!("commit_id={}", session.id);
|
||||||
|
|
@ -795,6 +802,23 @@ fn parse_args_from(mut args: Vec<String>) -> Result<(CliConfig, Command), KiCadE
|
||||||
}
|
}
|
||||||
Command::BoardOrigin { kind }
|
Command::BoardOrigin { kind }
|
||||||
}
|
}
|
||||||
|
"refresh-editor" => {
|
||||||
|
let mut frame = EditorFrameType::PcbEditor;
|
||||||
|
let mut i = 1;
|
||||||
|
while i < args.len() {
|
||||||
|
if args[i] == "--frame" {
|
||||||
|
let value = args.get(i + 1).ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "missing value for refresh-editor --frame".to_string(),
|
||||||
|
})?;
|
||||||
|
frame = EditorFrameType::from_str(value)
|
||||||
|
.map_err(|err| KiCadError::Config { reason: err })?;
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Command::RefreshEditor { frame }
|
||||||
|
}
|
||||||
"begin-commit" => Command::BeginCommit,
|
"begin-commit" => Command::BeginCommit,
|
||||||
"end-commit" => {
|
"end-commit" => {
|
||||||
let mut id = None;
|
let mut id = None;
|
||||||
|
|
@ -1154,7 +1178,7 @@ fn default_config() -> CliConfig {
|
||||||
|
|
||||||
fn print_help() {
|
fn print_help() {
|
||||||
println!(
|
println!(
|
||||||
"kicad-ipc-cli\n\nUSAGE:\n cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--client-name NAME] [--timeout-ms N] <command> [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n open-docs [--type <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 <value> (repeatable)\n text-extents Measure text bounding box\n Options: --text <value>\n text-as-shapes Convert text to rendered shapes\n Options: --text <value> (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 <uuid> ... Show parsed details for specific item IDs\n item-bbox --id <uuid> ... Show bounding boxes for item IDs\n hit-test --id <uuid> --x-nm <x> --y-nm <y> [--tolerance-nm <n>]\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 <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 <uuid> ... --layer-id <i32> [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id <uuid> ... --layer-id <i32> ... [--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 visible-layers Show currently visible board layers\n board-origin [--type <t>] Show board origin (`grid` default, or `drill`)\n begin-commit Start staged commit and print commit ID\n end-commit --id <uuid> [--action <commit|drop>] [--message <text>]\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> [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n open-docs [--type <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 <value> (repeatable)\n text-extents Measure text bounding box\n Options: --text <value>\n text-as-shapes Convert text to rendered shapes\n Options: --text <value> (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 <uuid> ... Show parsed details for specific item IDs\n item-bbox --id <uuid> ... Show bounding boxes for item IDs\n hit-test --id <uuid> --x-nm <x> --y-nm <y> [--tolerance-nm <n>]\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 <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 <uuid> ... --layer-id <i32> [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id <uuid> ... --layer-id <i32> ... [--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 visible-layers Show currently visible board layers\n board-origin [--type <t>] Show board origin (`grid` default, or `drill`)\n refresh-editor [--frame <f>] Refresh a specific editor frame (default: pcb)\n begin-commit Start staged commit and print commit ID\n end-commit --id <uuid> [--action <commit|drop>] [--message <text>]\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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1748,4 +1772,21 @@ mod tests {
|
||||||
other => panic!("unexpected command variant: {other:?}"),
|
other => panic!("unexpected command variant: {other:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_args_parses_refresh_editor_frame() {
|
||||||
|
let (_, command) = parse_args_from(vec![
|
||||||
|
"refresh-editor".to_string(),
|
||||||
|
"--frame".to_string(),
|
||||||
|
"schematic".to_string(),
|
||||||
|
])
|
||||||
|
.expect("refresh-editor args should parse");
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Command::RefreshEditor { frame } => {
|
||||||
|
assert_eq!(frame.to_string(), "schematic");
|
||||||
|
}
|
||||||
|
other => panic!("unexpected command variant: {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue