feat(client): add InjectDrcError API and CLI command
This commit is contained in:
parent
909007f74b
commit
a896a1c38e
|
|
@ -41,9 +41,9 @@ Legend:
|
||||||
| Common (base) | 6 | 4 | 67% |
|
| Common (base) | 6 | 4 | 67% |
|
||||||
| Common editor/document | 23 | 12 | 52% |
|
| Common editor/document | 23 | 12 | 52% |
|
||||||
| Project manager | 5 | 3 | 60% |
|
| Project manager | 5 | 3 | 60% |
|
||||||
| Board editor (PCB) | 22 | 18 | 82% |
|
| Board editor (PCB) | 22 | 19 | 86% |
|
||||||
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
| Schematic editor (dedicated proto commands) | 0 | 0 | n/a |
|
||||||
| **Total** | **56** | **37** | **66%** |
|
| **Total** | **56** | **38** | **68%** |
|
||||||
|
|
||||||
### Common (base)
|
### Common (base)
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ Legend:
|
||||||
| `RefillZones` | Not yet | - |
|
| `RefillZones` | Not yet | - |
|
||||||
| `GetPadShapeAsPolygon` | Implemented | `KiCadClient::get_pad_shape_as_polygon_raw`, `KiCadClient::get_pad_shape_as_polygon` |
|
| `GetPadShapeAsPolygon` | Implemented | `KiCadClient::get_pad_shape_as_polygon_raw`, `KiCadClient::get_pad_shape_as_polygon` |
|
||||||
| `CheckPadstackPresenceOnLayers` | Implemented | `KiCadClient::check_padstack_presence_on_layers_raw`, `KiCadClient::check_padstack_presence_on_layers` |
|
| `CheckPadstackPresenceOnLayers` | Implemented | `KiCadClient::check_padstack_presence_on_layers_raw`, `KiCadClient::check_padstack_presence_on_layers` |
|
||||||
| `InjectDrcError` | Not yet | - |
|
| `InjectDrcError` | Implemented | `KiCadClient::inject_drc_error_raw`, `KiCadClient::inject_drc_error` |
|
||||||
| `GetVisibleLayers` | Implemented | `KiCadClient::get_visible_layers` |
|
| `GetVisibleLayers` | Implemented | `KiCadClient::get_visible_layers` |
|
||||||
| `SetVisibleLayers` | Implemented | `KiCadClient::set_visible_layers` |
|
| `SetVisibleLayers` | Implemented | `KiCadClient::set_visible_layers` |
|
||||||
| `GetActiveLayer` | Implemented | `KiCadClient::get_active_layer` |
|
| `GetActiveLayer` | Implemented | `KiCadClient::get_active_layer` |
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,12 @@ Set editor appearance:
|
||||||
cargo run --bin kicad-ipc-cli -- set-appearance --inactive-layer-display hidden --net-color-display all --board-flip normal --ratsnest-display all-layers
|
cargo run --bin kicad-ipc-cli -- set-appearance --inactive-layer-display hidden --net-color-display all --board-flip normal --ratsnest-display all-layers
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Inject DRC marker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin kicad-ipc-cli -- inject-drc-error --severity error --message "API marker test" --x-nm 1000000 --y-nm 1000000
|
||||||
|
```
|
||||||
|
|
||||||
Show typed netclass map:
|
Show typed netclass map:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::model::board::{
|
||||||
ArcStartMidEndNm, BoardEditorAppearanceSettings, BoardEnabledLayers, BoardFlipMode,
|
ArcStartMidEndNm, BoardEditorAppearanceSettings, BoardEnabledLayers, BoardFlipMode,
|
||||||
BoardLayerClass, BoardLayerGraphicsDefault, BoardLayerInfo, BoardNet, BoardOriginKind,
|
BoardLayerClass, BoardLayerGraphicsDefault, BoardLayerInfo, BoardNet, BoardOriginKind,
|
||||||
BoardStackup, BoardStackupDielectricProperties, BoardStackupLayer, BoardStackupLayerType,
|
BoardStackup, BoardStackupDielectricProperties, BoardStackupLayer, BoardStackupLayerType,
|
||||||
ColorRgba, GraphicsDefaults, InactiveLayerDisplayMode, NetClassBoardSettings,
|
ColorRgba, DrcSeverity, GraphicsDefaults, InactiveLayerDisplayMode, NetClassBoardSettings,
|
||||||
NetClassForNetEntry, NetClassInfo, NetClassType, NetColorDisplayMode, PadNetEntry,
|
NetClassForNetEntry, NetClassInfo, NetClassType, NetColorDisplayMode, PadNetEntry,
|
||||||
PadShapeAsPolygonEntry, PadstackPresenceEntry, PadstackPresenceState, PcbArc,
|
PadShapeAsPolygonEntry, PadstackPresenceEntry, PadstackPresenceState, PcbArc,
|
||||||
PcbBoardGraphicShape, PcbBoardText, PcbBoardTextBox, PcbDimension, PcbField, PcbFootprint,
|
PcbBoardGraphicShape, PcbBoardText, PcbBoardTextBox, PcbDimension, PcbField, PcbFootprint,
|
||||||
|
|
@ -65,6 +65,7 @@ const CMD_GET_NETCLASS_FOR_NETS: &str = "kiapi.board.commands.GetNetClassForNets
|
||||||
const CMD_GET_PAD_SHAPE_AS_POLYGON: &str = "kiapi.board.commands.GetPadShapeAsPolygon";
|
const CMD_GET_PAD_SHAPE_AS_POLYGON: &str = "kiapi.board.commands.GetPadShapeAsPolygon";
|
||||||
const CMD_CHECK_PADSTACK_PRESENCE_ON_LAYERS: &str =
|
const CMD_CHECK_PADSTACK_PRESENCE_ON_LAYERS: &str =
|
||||||
"kiapi.board.commands.CheckPadstackPresenceOnLayers";
|
"kiapi.board.commands.CheckPadstackPresenceOnLayers";
|
||||||
|
const CMD_INJECT_DRC_ERROR: &str = "kiapi.board.commands.InjectDrcError";
|
||||||
const CMD_GET_SELECTION: &str = "kiapi.common.commands.GetSelection";
|
const CMD_GET_SELECTION: &str = "kiapi.common.commands.GetSelection";
|
||||||
const CMD_BEGIN_COMMIT: &str = "kiapi.common.commands.BeginCommit";
|
const CMD_BEGIN_COMMIT: &str = "kiapi.common.commands.BeginCommit";
|
||||||
const CMD_END_COMMIT: &str = "kiapi.common.commands.EndCommit";
|
const CMD_END_COMMIT: &str = "kiapi.common.commands.EndCommit";
|
||||||
|
|
@ -95,6 +96,7 @@ const RES_BOARD_EDITOR_APPEARANCE_SETTINGS: &str =
|
||||||
const RES_NETCLASS_FOR_NETS_RESPONSE: &str = "kiapi.board.commands.NetClassForNetsResponse";
|
const RES_NETCLASS_FOR_NETS_RESPONSE: &str = "kiapi.board.commands.NetClassForNetsResponse";
|
||||||
const RES_PAD_SHAPE_AS_POLYGON_RESPONSE: &str = "kiapi.board.commands.PadShapeAsPolygonResponse";
|
const RES_PAD_SHAPE_AS_POLYGON_RESPONSE: &str = "kiapi.board.commands.PadShapeAsPolygonResponse";
|
||||||
const RES_PADSTACK_PRESENCE_RESPONSE: &str = "kiapi.board.commands.PadstackPresenceResponse";
|
const RES_PADSTACK_PRESENCE_RESPONSE: &str = "kiapi.board.commands.PadstackPresenceResponse";
|
||||||
|
const RES_INJECT_DRC_ERROR_RESPONSE: &str = "kiapi.board.commands.InjectDrcErrorResponse";
|
||||||
const RES_VECTOR2: &str = "kiapi.common.types.Vector2";
|
const RES_VECTOR2: &str = "kiapi.common.types.Vector2";
|
||||||
const RES_SELECTION_RESPONSE: &str = "kiapi.common.commands.SelectionResponse";
|
const RES_SELECTION_RESPONSE: &str = "kiapi.common.commands.SelectionResponse";
|
||||||
const RES_BEGIN_COMMIT_RESPONSE: &str = "kiapi.common.commands.BeginCommitResponse";
|
const RES_BEGIN_COMMIT_RESPONSE: &str = "kiapi.common.commands.BeginCommitResponse";
|
||||||
|
|
@ -1047,6 +1049,46 @@ impl KiCadClient {
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn inject_drc_error_raw(
|
||||||
|
&self,
|
||||||
|
severity: DrcSeverity,
|
||||||
|
message: impl Into<String>,
|
||||||
|
position: Option<Vector2Nm>,
|
||||||
|
item_ids: Vec<String>,
|
||||||
|
) -> Result<prost_types::Any, KiCadError> {
|
||||||
|
let board = self.current_board_document_proto().await?;
|
||||||
|
let command = board_commands::InjectDrcError {
|
||||||
|
board: Some(board),
|
||||||
|
severity: drc_severity_to_proto(severity),
|
||||||
|
message: message.into(),
|
||||||
|
position: position.map(vector2_nm_to_proto),
|
||||||
|
items: item_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|value| common_types::Kiid { value })
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.send_command(envelope::pack_any(&command, CMD_INJECT_DRC_ERROR))
|
||||||
|
.await?;
|
||||||
|
response_payload_as_any(response, RES_INJECT_DRC_ERROR_RESPONSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn inject_drc_error(
|
||||||
|
&self,
|
||||||
|
severity: DrcSeverity,
|
||||||
|
message: impl Into<String>,
|
||||||
|
position: Option<Vector2Nm>,
|
||||||
|
item_ids: Vec<String>,
|
||||||
|
) -> Result<Option<String>, KiCadError> {
|
||||||
|
let payload = self
|
||||||
|
.inject_drc_error_raw(severity, message, position, item_ids)
|
||||||
|
.await?;
|
||||||
|
let response: board_commands::InjectDrcErrorResponse =
|
||||||
|
decode_any(&payload, RES_INJECT_DRC_ERROR_RESPONSE)?;
|
||||||
|
Ok(response.marker.map(|marker| marker.value))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_board_stackup_raw(&self) -> Result<prost_types::Any, KiCadError> {
|
pub async fn get_board_stackup_raw(&self) -> Result<prost_types::Any, KiCadError> {
|
||||||
let command = board_commands::GetBoardStackup {
|
let command = board_commands::GetBoardStackup {
|
||||||
board: Some(self.current_board_document_proto().await?),
|
board: Some(self.current_board_document_proto().await?),
|
||||||
|
|
@ -1674,6 +1716,19 @@ fn board_origin_kind_to_proto(kind: BoardOriginKind) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drc_severity_to_proto(value: DrcSeverity) -> i32 {
|
||||||
|
match value {
|
||||||
|
DrcSeverity::Warning => board_commands::DrcSeverity::DrsWarning as i32,
|
||||||
|
DrcSeverity::Error => board_commands::DrcSeverity::DrsError as i32,
|
||||||
|
DrcSeverity::Exclusion => board_commands::DrcSeverity::DrsExclusion as i32,
|
||||||
|
DrcSeverity::Ignore => board_commands::DrcSeverity::DrsIgnore as i32,
|
||||||
|
DrcSeverity::Info => board_commands::DrcSeverity::DrsInfo as i32,
|
||||||
|
DrcSeverity::Action => board_commands::DrcSeverity::DrsAction as i32,
|
||||||
|
DrcSeverity::Debug => board_commands::DrcSeverity::DrsDebug as i32,
|
||||||
|
DrcSeverity::Undefined => board_commands::DrcSeverity::DrsUndefined as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn commit_action_to_proto(action: CommitAction) -> i32 {
|
fn commit_action_to_proto(action: CommitAction) -> i32 {
|
||||||
match action {
|
match action {
|
||||||
CommitAction::Commit => common_commands::CommitAction::CmaCommit as i32,
|
CommitAction::Commit => common_commands::CommitAction::CmaCommit as i32,
|
||||||
|
|
@ -2854,12 +2909,12 @@ fn default_client_name() -> String {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
any_to_pretty_debug, board_editor_appearance_settings_to_proto, commit_action_to_proto,
|
any_to_pretty_debug, board_editor_appearance_settings_to_proto, commit_action_to_proto,
|
||||||
ensure_item_request_ok, layer_to_model, map_commit_session, map_hit_test_result,
|
drc_severity_to_proto, ensure_item_request_ok, layer_to_model, map_commit_session,
|
||||||
map_item_bounding_boxes, map_polygon_with_holes, model_document_to_proto,
|
map_hit_test_result, map_item_bounding_boxes, map_polygon_with_holes,
|
||||||
normalize_socket_uri, pad_netlist_from_footprint_items, response_payload_as_any,
|
model_document_to_proto, normalize_socket_uri, pad_netlist_from_footprint_items,
|
||||||
select_single_board_document, select_single_project_path, selection_item_detail,
|
response_payload_as_any, select_single_board_document, select_single_project_path,
|
||||||
summarize_item_details, summarize_selection, text_horizontal_alignment_to_proto,
|
selection_item_detail, summarize_item_details, summarize_selection,
|
||||||
text_spec_to_proto, PCB_OBJECT_TYPES,
|
text_horizontal_alignment_to_proto, text_spec_to_proto, PCB_OBJECT_TYPES,
|
||||||
};
|
};
|
||||||
use crate::error::KiCadError;
|
use crate::error::KiCadError;
|
||||||
use crate::model::common::{
|
use crate::model::common::{
|
||||||
|
|
@ -3027,6 +3082,18 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drc_severity_to_proto_maps_known_variants() {
|
||||||
|
assert_eq!(
|
||||||
|
drc_severity_to_proto(crate::model::board::DrcSeverity::Warning),
|
||||||
|
crate::proto::kiapi::board::commands::DrcSeverity::DrsWarning as i32
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
drc_severity_to_proto(crate::model::board::DrcSeverity::Error),
|
||||||
|
crate::proto::kiapi::board::commands::DrcSeverity::DrsError as i32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn board_editor_appearance_settings_to_proto_maps_known_variants() {
|
fn board_editor_appearance_settings_to_proto_maps_known_variants() {
|
||||||
let proto = board_editor_appearance_settings_to_proto(
|
let proto = board_editor_appearance_settings_to_proto(
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub use crate::model::board::{
|
||||||
ArcStartMidEndNm, BoardEditorAppearanceSettings, BoardEnabledLayers, BoardFlipMode,
|
ArcStartMidEndNm, BoardEditorAppearanceSettings, BoardEnabledLayers, BoardFlipMode,
|
||||||
BoardLayerClass, BoardLayerGraphicsDefault, BoardLayerInfo, BoardNet, BoardOriginKind,
|
BoardLayerClass, BoardLayerGraphicsDefault, BoardLayerInfo, BoardNet, BoardOriginKind,
|
||||||
BoardStackup, BoardStackupDielectricProperties, BoardStackupLayer, BoardStackupLayerType,
|
BoardStackup, BoardStackupDielectricProperties, BoardStackupLayer, BoardStackupLayerType,
|
||||||
ColorRgba, GraphicsDefaults, InactiveLayerDisplayMode, NetClassBoardSettings,
|
ColorRgba, DrcSeverity, GraphicsDefaults, InactiveLayerDisplayMode, NetClassBoardSettings,
|
||||||
NetClassForNetEntry, NetClassInfo, NetClassType, NetColorDisplayMode, PadNetEntry,
|
NetClassForNetEntry, NetClassInfo, NetClassType, NetColorDisplayMode, PadNetEntry,
|
||||||
PadShapeAsPolygonEntry, PadstackPresenceEntry, PadstackPresenceState, PcbArc,
|
PadShapeAsPolygonEntry, PadstackPresenceEntry, PadstackPresenceState, PcbArc,
|
||||||
PcbBoardGraphicShape, PcbBoardText, PcbBoardTextBox, PcbDimension, PcbField, PcbFootprint,
|
PcbBoardGraphicShape, PcbBoardText, PcbBoardTextBox, PcbDimension, PcbField, PcbFootprint,
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,54 @@ pub enum RatsnestDisplayMode {
|
||||||
Unknown(i32),
|
Unknown(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum DrcSeverity {
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Exclusion,
|
||||||
|
Ignore,
|
||||||
|
Info,
|
||||||
|
Action,
|
||||||
|
Debug,
|
||||||
|
Undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for DrcSeverity {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let value = match self {
|
||||||
|
Self::Warning => "warning",
|
||||||
|
Self::Error => "error",
|
||||||
|
Self::Exclusion => "exclusion",
|
||||||
|
Self::Ignore => "ignore",
|
||||||
|
Self::Info => "info",
|
||||||
|
Self::Action => "action",
|
||||||
|
Self::Debug => "debug",
|
||||||
|
Self::Undefined => "undefined",
|
||||||
|
};
|
||||||
|
write!(f, "{value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DrcSeverity {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||||
|
match value {
|
||||||
|
"warning" => Ok(Self::Warning),
|
||||||
|
"error" => Ok(Self::Error),
|
||||||
|
"exclusion" => Ok(Self::Exclusion),
|
||||||
|
"ignore" => Ok(Self::Ignore),
|
||||||
|
"info" => Ok(Self::Info),
|
||||||
|
"action" => Ok(Self::Action),
|
||||||
|
"debug" => Ok(Self::Debug),
|
||||||
|
"undefined" => Ok(Self::Undefined),
|
||||||
|
_ => Err(format!(
|
||||||
|
"unknown drc severity `{value}`; expected warning, error, exclusion, ignore, info, action, debug, or undefined"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct BoardEditorAppearanceSettings {
|
pub struct BoardEditorAppearanceSettings {
|
||||||
pub inactive_layer_display: InactiveLayerDisplayMode,
|
pub inactive_layer_display: InactiveLayerDisplayMode,
|
||||||
|
|
@ -424,7 +472,7 @@ pub enum PcbItem {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::BoardOriginKind;
|
use super::{BoardOriginKind, DrcSeverity};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn board_origin_kind_parses_known_values() {
|
fn board_origin_kind_parses_known_values() {
|
||||||
|
|
@ -443,4 +491,22 @@ mod tests {
|
||||||
let result = BoardOriginKind::from_str("other");
|
let result = BoardOriginKind::from_str("other");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drc_severity_parses_known_values() {
|
||||||
|
assert_eq!(
|
||||||
|
DrcSeverity::from_str("warning").expect("warning should parse"),
|
||||||
|
DrcSeverity::Warning
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
DrcSeverity::from_str("error").expect("error should parse"),
|
||||||
|
DrcSeverity::Error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drc_severity_rejects_unknown_values() {
|
||||||
|
let result = DrcSeverity::from_str("fatal");
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ use std::time::Duration;
|
||||||
|
|
||||||
use kicad_ipc::{
|
use kicad_ipc::{
|
||||||
BoardFlipMode, BoardOriginKind, ClientBuilder, CommitAction, CommitSession, DocumentType,
|
BoardFlipMode, BoardOriginKind, ClientBuilder, CommitAction, CommitSession, DocumentType,
|
||||||
EditorFrameType, InactiveLayerDisplayMode, KiCadClient, KiCadError, NetColorDisplayMode,
|
DrcSeverity, EditorFrameType, InactiveLayerDisplayMode, KiCadClient, KiCadError,
|
||||||
PadstackPresenceState, PcbObjectTypeCode, RatsnestDisplayMode, TextObjectSpec,
|
NetColorDisplayMode, PadstackPresenceState, PcbObjectTypeCode, RatsnestDisplayMode,
|
||||||
TextShapeGeometry, TextSpec, Vector2Nm,
|
TextObjectSpec, TextShapeGeometry, TextSpec, Vector2Nm,
|
||||||
};
|
};
|
||||||
|
|
||||||
const REPORT_MAX_PAD_NET_ROWS: usize = 2_000;
|
const REPORT_MAX_PAD_NET_ROWS: usize = 2_000;
|
||||||
|
|
@ -68,6 +68,13 @@ enum Command {
|
||||||
x_nm: i64,
|
x_nm: i64,
|
||||||
y_nm: i64,
|
y_nm: i64,
|
||||||
},
|
},
|
||||||
|
InjectDrcError {
|
||||||
|
severity: DrcSeverity,
|
||||||
|
message: String,
|
||||||
|
x_nm: Option<i64>,
|
||||||
|
y_nm: Option<i64>,
|
||||||
|
item_ids: Vec<String>,
|
||||||
|
},
|
||||||
RefreshEditor {
|
RefreshEditor {
|
||||||
frame: EditorFrameType,
|
frame: EditorFrameType,
|
||||||
},
|
},
|
||||||
|
|
@ -371,6 +378,25 @@ async fn run() -> Result<(), KiCadError> {
|
||||||
.await?;
|
.await?;
|
||||||
println!("set_origin_kind={} x_nm={} y_nm={}", kind, x_nm, y_nm);
|
println!("set_origin_kind={} x_nm={} y_nm={}", kind, x_nm, y_nm);
|
||||||
}
|
}
|
||||||
|
Command::InjectDrcError {
|
||||||
|
severity,
|
||||||
|
message,
|
||||||
|
x_nm,
|
||||||
|
y_nm,
|
||||||
|
item_ids,
|
||||||
|
} => {
|
||||||
|
let position = match (x_nm, y_nm) {
|
||||||
|
(Some(x_nm), Some(y_nm)) => Some(Vector2Nm { x_nm, y_nm }),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let marker = client
|
||||||
|
.inject_drc_error(severity, message, position, item_ids)
|
||||||
|
.await?;
|
||||||
|
println!(
|
||||||
|
"drc_marker_id={}",
|
||||||
|
marker.unwrap_or_else(|| "-".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
Command::RefreshEditor { frame } => {
|
Command::RefreshEditor { frame } => {
|
||||||
client.refresh_editor(frame).await?;
|
client.refresh_editor(frame).await?;
|
||||||
println!("refresh_editor=ok frame={}", frame);
|
println!("refresh_editor=ok frame={}", frame);
|
||||||
|
|
@ -1005,6 +1031,79 @@ fn parse_args_from(mut args: Vec<String>) -> Result<(CliConfig, Command), KiCadE
|
||||||
})?,
|
})?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"inject-drc-error" => {
|
||||||
|
let mut severity = DrcSeverity::Error;
|
||||||
|
let mut message = None;
|
||||||
|
let mut x_nm = None;
|
||||||
|
let mut y_nm = None;
|
||||||
|
let mut item_ids = Vec::new();
|
||||||
|
let mut i = 1;
|
||||||
|
while i < args.len() {
|
||||||
|
match args[i].as_str() {
|
||||||
|
"--severity" => {
|
||||||
|
let value = args.get(i + 1).ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "missing value for inject-drc-error --severity".to_string(),
|
||||||
|
})?;
|
||||||
|
severity = parse_drc_severity(value)
|
||||||
|
.map_err(|err| KiCadError::Config { reason: err })?;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
"--message" => {
|
||||||
|
let value = args.get(i + 1).ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "missing value for inject-drc-error --message".to_string(),
|
||||||
|
})?;
|
||||||
|
message = Some(value.clone());
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
"--x-nm" => {
|
||||||
|
let value = args.get(i + 1).ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "missing value for inject-drc-error --x-nm".to_string(),
|
||||||
|
})?;
|
||||||
|
x_nm = Some(value.parse::<i64>().map_err(|err| KiCadError::Config {
|
||||||
|
reason: format!("invalid inject-drc-error --x-nm `{value}`: {err}"),
|
||||||
|
})?);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
"--y-nm" => {
|
||||||
|
let value = args.get(i + 1).ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "missing value for inject-drc-error --y-nm".to_string(),
|
||||||
|
})?;
|
||||||
|
y_nm = Some(value.parse::<i64>().map_err(|err| KiCadError::Config {
|
||||||
|
reason: format!("invalid inject-drc-error --y-nm `{value}`: {err}"),
|
||||||
|
})?);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
"--item-id" => {
|
||||||
|
let value = args.get(i + 1).ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "missing value for inject-drc-error --item-id".to_string(),
|
||||||
|
})?;
|
||||||
|
item_ids.push(value.clone());
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_nm.is_some() && y_nm.is_none()) || (x_nm.is_none() && y_nm.is_some()) {
|
||||||
|
return Err(KiCadError::Config {
|
||||||
|
reason:
|
||||||
|
"inject-drc-error requires both --x-nm and --y-nm when providing a position"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::InjectDrcError {
|
||||||
|
severity,
|
||||||
|
message: message.ok_or_else(|| KiCadError::Config {
|
||||||
|
reason: "inject-drc-error requires `--message <text>`".to_string(),
|
||||||
|
})?,
|
||||||
|
x_nm,
|
||||||
|
y_nm,
|
||||||
|
item_ids,
|
||||||
|
}
|
||||||
|
}
|
||||||
"refresh-editor" => {
|
"refresh-editor" => {
|
||||||
let mut frame = EditorFrameType::PcbEditor;
|
let mut frame = EditorFrameType::PcbEditor;
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
|
|
@ -1488,6 +1587,22 @@ fn parse_ratsnest_display_mode(value: &str) -> Result<RatsnestDisplayMode, Strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_drc_severity(value: &str) -> Result<DrcSeverity, String> {
|
||||||
|
match value {
|
||||||
|
"warning" => Ok(DrcSeverity::Warning),
|
||||||
|
"error" => Ok(DrcSeverity::Error),
|
||||||
|
"exclusion" => Ok(DrcSeverity::Exclusion),
|
||||||
|
"ignore" => Ok(DrcSeverity::Ignore),
|
||||||
|
"info" => Ok(DrcSeverity::Info),
|
||||||
|
"action" => Ok(DrcSeverity::Action),
|
||||||
|
"debug" => Ok(DrcSeverity::Debug),
|
||||||
|
"undefined" => Ok(DrcSeverity::Undefined),
|
||||||
|
_ => Err(format!(
|
||||||
|
"unknown drc severity `{value}`; expected warning, error, exclusion, ignore, info, action, debug, or undefined"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn default_config() -> CliConfig {
|
fn default_config() -> CliConfig {
|
||||||
CliConfig {
|
CliConfig {
|
||||||
socket: None,
|
socket: None,
|
||||||
|
|
@ -1499,7 +1614,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 set-appearance --inactive-layer-display <normal|dimmed|hidden>\n --net-color-display <all|ratsnest|off>\n --board-flip <normal|flipped-x>\n --ratsnest-display <all-layers|visible-layers>\n Set 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 set-enabled-layers --copper-layer-count <u32> [--layer-id <i32> ...]\n Set enabled board layer set\n active-layer Show active board layer\n set-active-layer --layer-id <i32>\n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id <i32> ...\n Set visible board layers\n board-origin [--type <t>] Show board origin (`grid` default, or `drill`)\n set-board-origin --type <t> --x-nm <i64> --y-nm <i64>\n Set board origin (`grid` 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"
|
"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 set-appearance --inactive-layer-display <normal|dimmed|hidden>\n --net-color-display <all|ratsnest|off>\n --board-flip <normal|flipped-x>\n --ratsnest-display <all-layers|visible-layers>\n Set editor appearance settings\n inject-drc-error --severity <s> --message <text> [--x-nm <i64> --y-nm <i64>] [--item-id <uuid> ...]\n Inject a DRC marker (severity: warning|error|exclusion|ignore|info|action|debug|undefined)\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 set-enabled-layers --copper-layer-count <u32> [--layer-id <i32> ...]\n Set enabled board layer set\n active-layer Show active board layer\n set-active-layer --layer-id <i32>\n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id <i32> ...\n Set visible board layers\n board-origin [--type <t>] Show board origin (`grid` default, or `drill`)\n set-board-origin --type <t> --x-nm <i64> --y-nm <i64>\n Set board origin (`grid` 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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2053,7 +2168,7 @@ fn hex_char(value: u8) -> char {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{parse_args_from, Command};
|
use super::{parse_args_from, Command};
|
||||||
use kicad_ipc::{
|
use kicad_ipc::{
|
||||||
BoardFlipMode, BoardOriginKind, CommitAction, InactiveLayerDisplayMode,
|
BoardFlipMode, BoardOriginKind, CommitAction, DrcSeverity, InactiveLayerDisplayMode,
|
||||||
NetColorDisplayMode, RatsnestDisplayMode,
|
NetColorDisplayMode, RatsnestDisplayMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2224,4 +2339,37 @@ mod tests {
|
||||||
other => panic!("unexpected command variant: {other:?}"),
|
other => panic!("unexpected command variant: {other:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_args_parses_inject_drc_error() {
|
||||||
|
let (_, command) = parse_args_from(vec![
|
||||||
|
"inject-drc-error".to_string(),
|
||||||
|
"--severity".to_string(),
|
||||||
|
"warning".to_string(),
|
||||||
|
"--message".to_string(),
|
||||||
|
"marker".to_string(),
|
||||||
|
"--x-nm".to_string(),
|
||||||
|
"100".to_string(),
|
||||||
|
"--y-nm".to_string(),
|
||||||
|
"200".to_string(),
|
||||||
|
])
|
||||||
|
.expect("inject-drc-error args should parse");
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Command::InjectDrcError {
|
||||||
|
severity,
|
||||||
|
message,
|
||||||
|
x_nm,
|
||||||
|
y_nm,
|
||||||
|
item_ids,
|
||||||
|
} => {
|
||||||
|
assert_eq!(severity, DrcSeverity::Warning);
|
||||||
|
assert_eq!(message, "marker");
|
||||||
|
assert_eq!(x_nm, Some(100));
|
||||||
|
assert_eq!(y_nm, Some(200));
|
||||||
|
assert!(item_ids.is_empty());
|
||||||
|
}
|
||||||
|
other => panic!("unexpected command variant: {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue